CheckAccessDeleted.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 #include "zypp/base/LogTools.h"
00014 #include "zypp/base/String.h"
00015 #include "zypp/base/Exception.h"
00016 
00017 #include "zypp/PathInfo.h"
00018 #include "zypp/ExternalProgram.h"
00019 
00020 #include "zypp/misc/CheckAccessDeleted.h"
00021 
00022 using std::endl;
00023 
00024 #undef ZYPP_BASE_LOGGER_LOGGROUP
00025 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
00026 
00028 namespace zypp
00029 { 
00030 
00032   namespace
00033   { 
00034     //
00035     // lsof output lines are a sequence of NUL terminated fields,
00036     // where the 1st char determines the fiels type.
00037     //
00038     // (pcuL) pid command userid loginname
00039     // (ftkn).filedescriptor type linkcount filename
00040     //
00042 
00046     inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, CheckAccessDeleted::ProcInfo & cache_r )
00047     {
00048       if ( cache_r.files.empty() )
00049         return;
00050 
00051       // at least one file access so keep it:
00052       data_r.push_back( CheckAccessDeleted::ProcInfo() );
00053       CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
00054 
00055       std::string pline;
00056       cache_r.pid.swap( pline );
00057       cache_r.files.swap( pinfo.files ); // clears cache.files
00058 
00059       for_( ch, pline.begin(), pline.end() )
00060       {
00061         switch ( *ch )
00062         {
00063           case 'p':
00064             pinfo.pid = &*(ch+1);
00065             break;
00066           case 'R':
00067             pinfo.ppid = &*(ch+1);
00068             break;
00069           case 'u':
00070             pinfo.puid = &*(ch+1);
00071             break;
00072           case 'L':
00073             pinfo.login = &*(ch+1);
00074             break;
00075           case 'c':
00076             pinfo.command = &*(ch+1);
00077             break;
00078         }
00079         if ( *ch == '\n' ) break;               // end of data
00080         do { ++ch; } while ( *ch != '\0' );     // skip to next field
00081       }
00082 
00083       if ( pinfo.command.size() == 15 )
00084       {
00085         // the command name might be truncated, so we check against /proc/<pid>/exe
00086         Pathname command( filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ) );
00087         if ( ! command.empty() )
00088           pinfo.command = command.basename();
00089       }
00090 
00091       //MIL << " Take " << pinfo << endl;
00092     }
00093 
00099     inline void addCacheIf( CheckAccessDeleted::ProcInfo & cache_r, const std::string & line_r, bool verbose_r  )
00100     {
00101       const char * f = 0;
00102       const char * t = 0;
00103       const char * n = 0;
00104 
00105       for_( ch, line_r.c_str(), ch+line_r.size() )
00106       {
00107         switch ( *ch )
00108         {
00109           case 'k':
00110             if ( *(ch+1) != '0' )       // skip non-zero link counts
00111               return;
00112             break;
00113           case 'f':
00114             f = ch+1;
00115             break;
00116           case 't':
00117             t = ch+1;
00118             break;
00119           case 'n':
00120             n = ch+1;
00121             break;
00122         }
00123         if ( *ch == '\n' ) break;               // end of data
00124         do { ++ch; } while ( *ch != '\0' );     // skip to next field
00125       }
00126 
00127       if ( !t || !f || !n )
00128         return; // wrong filedescriptor/type/name
00129 
00130       if ( !(    ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
00131               || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
00132         return; // wrong type
00133 
00134       if ( !(    ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
00135               || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
00136               || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
00137               || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
00138         return; // wrong filedescriptor type
00139 
00140       if ( str::contains( n, "(stat: Permission denied)" ) )
00141         return; // Avoid reporting false positive due to insufficient permission.
00142 
00143       if ( ! verbose_r )
00144       {
00145         if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
00146           return; // Try to avoid reporting false positive unless verbose.
00147       }
00148 
00149       if ( *f == 'm' || *f == 'D' )     // skip some wellknown nonlibrary memorymapped files
00150       {
00151         static const char * black[] = {
00152             "/SYSV"
00153           , "/var/run/"
00154           , "/dev/"
00155         };
00156         for_( it, arrayBegin( black ), arrayEnd( black ) )
00157         {
00158           if ( str::hasPrefix( n, *it ) )
00159             return;
00160         }
00161       }
00162 
00163       if ( std::find( cache_r.files.begin(), cache_r.files.end(), n ) == cache_r.files.end() )
00164       {
00165         // Add if no duplicate
00166         cache_r.files.push_back( n );
00167       }
00168     }
00170   } // namespace
00172 
00173   CheckAccessDeleted::size_type CheckAccessDeleted::check( bool verbose_r )
00174   {
00175     _data.clear();
00176     std::vector<ProcInfo> data;
00177 
00178     static const char* argv[] =
00179     {
00180       "lsof", "-n", "-FpcuLRftkn0", NULL
00181     };
00182     ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
00183 
00184     CheckAccessDeleted::ProcInfo cache;
00185     for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
00186     {
00187       if ( line[0] == 'p' )
00188       {
00189         addDataIf( data, cache );
00190         cache.pid = line; //
00191       }
00192       else
00193       {
00194         addCacheIf( cache, line, verbose_r );
00195       }
00196     }
00197     addDataIf( data, cache );
00198 
00199     int ret = prog.close();
00200     if ( ret != 0 )
00201     {
00202       Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
00203       err.remember( prog.execError() );
00204       ZYPP_THROW( err );
00205     }
00206 
00207     _data.swap( data );
00208     return _data.size();
00209   }
00210 
00211   std::string CheckAccessDeleted::findService( const Pathname & command_r )
00212   {
00213     ProcInfo p;
00214     p.command = command_r.basename();
00215     return p.service();
00216   }
00217   std::string CheckAccessDeleted::findService( const char * command_r )
00218   { return findService( Pathname( command_r ) ); }
00219 
00220   std::string CheckAccessDeleted::findService( const std::string & command_r )
00221   { return findService( Pathname( command_r ) ); }
00222 
00223   std::string CheckAccessDeleted::findService( pid_t pid_r )
00224   { return findService( filesystem::readlink( Pathname("/proc")/str::numstring(pid_r)/"exe" ) ); }
00225 
00227   namespace
00228   { 
00229 
00230   } // namespace
00232 
00233   std::string CheckAccessDeleted::ProcInfo::service() const
00234   {
00235     if ( command.empty() )
00236       return std::string();
00237     // TODO: This needs to be implemented smarter... be carefull
00238     // as we don't know whether the target is up.
00239 
00240     static const Pathname initD( "/etc/init.d" );
00241     { // init.d script with same name
00242       PathInfo pi( initD/command );
00243       if ( pi.isFile() && pi.isX() )
00244         return command;
00245     }
00246     { // init.d script with name + 'd'
00247       std::string alt( command+"d" );
00248       PathInfo pi( initD/alt );
00249       if ( pi.isFile() && pi.isX() )
00250         return alt;
00251     }
00252     if ( *command.rbegin() == 'd' )
00253     { // init.d script with name - trailing'd'
00254       std::string alt( command );
00255       alt.erase( alt.size()-1 );
00256       PathInfo pi( initD/alt );
00257       WAR <<pi << endl;
00258       if ( pi.isFile() && pi.isX() )
00259         return alt;
00260     }
00261     return std::string();
00262   }
00263 
00264   /******************************************************************
00265   **
00266   **    FUNCTION NAME : operator<<
00267   **    FUNCTION TYPE : std::ostream &
00268   */
00269   std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
00270   {
00271     return dumpRange( str << "CheckAccessDeleted ",
00272                       obj.begin(),
00273                       obj.end() );
00274   }
00275 
00276    /******************************************************************
00277   **
00278   **    FUNCTION NAME : operator<<
00279   **    FUNCTION TYPE : std::ostream &
00280   */
00281   std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
00282   {
00283     if ( obj.pid.empty() )
00284       return str << "<NoProc>";
00285 
00286     return dumpRangeLine( str << obj.command
00287                               << '<' << obj.pid
00288                               << '|' << obj.ppid
00289                               << '|' << obj.puid
00290                               << '|' << obj.login
00291                               << '>',
00292                           obj.files.begin(),
00293                           obj.files.end() );
00294   }
00295 
00297 } // namespace zypp

doxygen