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