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