libzypp 8.13.6

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