libzypp 8.13.6
|
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