1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/misc/CheckAccessDeleted.cc
13 #include "zypp/base/LogTools.h"
14 #include "zypp/base/String.h"
15 #include "zypp/base/Exception.h"
17 #include "zypp/PathInfo.h"
18 #include "zypp/ExternalProgram.h"
20 #include "zypp/misc/CheckAccessDeleted.h"
24 #undef ZYPP_BASE_LOGGER_LOGGROUP
25 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
27 ///////////////////////////////////////////////////////////////////
29 { /////////////////////////////////////////////////////////////////
31 ///////////////////////////////////////////////////////////////////
33 { /////////////////////////////////////////////////////////////////
35 // lsof output lines are a sequence of NUL terminated fields,
36 // where the 1st char determines the fiels type.
38 // (pcuL) pid command userid loginname
39 // (ftkn).filedescriptor type linkcount filename
41 /////////////////////////////////////////////////////////////////
42 /** Add \c cache to \c data if the process is accessing deleted files.
43 * \c pid string in \c cache is the proc line \c (pcuLR), \c iles
44 * are lready in place. Always clear the \c cache.files!
46 inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, CheckAccessDeleted::ProcInfo & cache_r )
48 if ( cache_r.files.empty() )
51 // at least one file access so keep it:
52 data_r.push_back( CheckAccessDeleted::ProcInfo() );
53 CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
56 cache_r.pid.swap( pline );
57 cache_r.files.swap( pinfo.files ); // clears cache.files
59 for_( ch, pline.begin(), pline.end() )
67 pinfo.ppid = &*(ch+1);
70 pinfo.puid = &*(ch+1);
73 pinfo.login = &*(ch+1);
76 pinfo.command = &*(ch+1);
79 if ( *ch == '\n' ) break; // end of data
80 do { ++ch; } while ( *ch != '\0' ); // skip to next field
83 if ( pinfo.command.size() == 15 )
85 // the command name might be truncated, so we check against /proc/<pid>/exe
86 Pathname command( filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ) );
87 if ( ! command.empty() )
88 pinfo.command = command.basename();
91 MIL << " Take " << pinfo << endl;
94 /** Add line to cache if it refers to a deleted executable or library file:
95 * - Either the link count \c(k) os \c 0, or no link cout is present.
96 * - The type \c (t) is set to \c REG or \c DEL
97 * - The filedescriptor \c (f) is set to \c txt, \c mem or \c DEL
99 inline void addCacheIf( CheckAccessDeleted::ProcInfo & cache_r, const std::string & line_r )
101 const char * k = ".";
102 const char * f = ".";
103 const char * t = ".";
104 const char * n = ".";
106 for_( ch, line_r.c_str(), ch+line_r.size() )
111 if ( *(ch+1) != '0' ) // skip non-zero link counts
125 if ( *ch == '\n' ) break; // end of data
126 do { ++ch; } while ( *ch != '\0' ); // skip to next field
129 if ( !t || !f || !n )
130 return; // wrong filedescriptor/type/name
132 if ( !( ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
133 || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
134 return; // wrong type
136 if ( !( ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
137 || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
138 || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
139 || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
140 return; // wrong filedescriptor type
142 std::string name( n );
144 if ( *f == 'm' || *f == 'D' ) // skip some wellknown nonlibrary memorymapped files
146 static const char * black[] = {
150 for_( it, arrayBegin( black ), arrayEnd( black ) )
152 if ( str::hasPrefix( n, *it ) )
157 if ( std::find( cache_r.files.begin(), cache_r.files.end(), name ) == cache_r.files.end() )
159 // Add if no duplicate
160 cache_r.files.push_back( n );
163 /////////////////////////////////////////////////////////////////
165 ///////////////////////////////////////////////////////////////////
167 CheckAccessDeleted::size_type CheckAccessDeleted::check()
170 std::vector<ProcInfo> data;
172 static const char* argv[] =
174 "lsof", "-n", "-FpcuLRftkn0", NULL
176 ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
178 CheckAccessDeleted::ProcInfo cache;
179 for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
181 if ( line[0] == 'p' )
183 addDataIf( data, cache );
188 addCacheIf( cache, line );
191 addDataIf( data, cache );
193 int ret = prog.close();
196 Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
197 err.remember( prog.execError() );
205 std::string CheckAccessDeleted::findService( const Pathname & command_r )
208 p.command = command_r.basename();
211 std::string CheckAccessDeleted::findService( const char * command_r )
212 { return findService( Pathname( command_r ) ); }
214 std::string CheckAccessDeleted::findService( const std::string & command_r )
215 { return findService( Pathname( command_r ) ); }
217 std::string CheckAccessDeleted::findService( pid_t pid_r )
218 { return findService( filesystem::readlink( Pathname("/proc")/str::numstring(pid_r)/"exe" ) ); }
220 ///////////////////////////////////////////////////////////////////
222 { /////////////////////////////////////////////////////////////////
223 /////////////////////////////////////////////////////////////////
225 ///////////////////////////////////////////////////////////////////
227 std::string CheckAccessDeleted::ProcInfo::service() const
229 if ( command.empty() )
230 return std::string();
231 // TODO: This needs to be implemented smarter... be carefull
232 // as we don't know whether the target is up.
234 static const Pathname initD( "/etc/init.d" );
235 { // init.d script with same name
236 PathInfo pi( initD/command );
237 if ( pi.isFile() && pi.isX() )
240 { // init.d script with name + 'd'
241 std::string alt( command+"d" );
242 PathInfo pi( initD/alt );
243 if ( pi.isFile() && pi.isX() )
246 if ( *command.rbegin() == 'd' )
247 { // init.d script with name - trailing'd'
248 std::string alt( command );
249 alt.erase( alt.size()-1 );
250 PathInfo pi( initD/alt );
252 if ( pi.isFile() && pi.isX() )
255 return std::string();
258 /******************************************************************
260 ** FUNCTION NAME : operator<<
261 ** FUNCTION TYPE : std::ostream &
263 std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
265 return dumpRange( str << "CheckAccessDeleted ",
270 /******************************************************************
272 ** FUNCTION NAME : operator<<
273 ** FUNCTION TYPE : std::ostream &
275 std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
277 if ( obj.pid.empty() )
278 return str << "<NoProc>";
280 return dumpRangeLine( str << obj.command
290 /////////////////////////////////////////////////////////////////
292 ///////////////////////////////////////////////////////////////////