libzypp  13.10.6
CheckAccessDeleted.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <unordered_set>
14 #include "zypp/base/LogTools.h"
15 #include "zypp/base/String.h"
16 #include "zypp/base/Gettext.h"
17 #include "zypp/base/Exception.h"
18 
19 #include "zypp/PathInfo.h"
20 #include "zypp/ExternalProgram.h"
21 
23 
24 using std::endl;
25 
26 #undef ZYPP_BASE_LOGGER_LOGGROUP
27 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
28 
30 namespace zypp
31 {
32 
34  namespace
35  {
36  //
37  // lsof output lines are a sequence of NUL terminated fields,
38  // where the 1st char determines the fiels type.
39  //
40  // (pcuL) pid command userid loginname
41  // (ftkn).filedescriptor type linkcount filename
42  //
44 
46  typedef std::pair<std::string,std::unordered_set<std::string>> CacheEntry;
47 
52  inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, const CacheEntry & cache_r )
53  {
54  const auto & filelist( cache_r.second );
55 
56  if ( filelist.empty() )
57  return;
58 
59  // at least one file access so keep it:
60  data_r.push_back( CheckAccessDeleted::ProcInfo() );
61  CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
62  pinfo.files.insert( pinfo.files.begin(), filelist.begin(), filelist.end() );
63 
64  const std::string & pline( cache_r.first );
65  for_( ch, pline.begin(), pline.end() )
66  {
67  switch ( *ch )
68  {
69  case 'p':
70  pinfo.pid = &*(ch+1);
71  break;
72  case 'R':
73  pinfo.ppid = &*(ch+1);
74  break;
75  case 'u':
76  pinfo.puid = &*(ch+1);
77  break;
78  case 'L':
79  pinfo.login = &*(ch+1);
80  break;
81  case 'c':
82  pinfo.command = &*(ch+1);
83  break;
84  }
85  if ( *ch == '\n' ) break; // end of data
86  do { ++ch; } while ( *ch != '\0' ); // skip to next field
87  }
88 
89  if ( pinfo.command.size() == 15 )
90  {
91  // the command name might be truncated, so we check against /proc/<pid>/exe
92  Pathname command( filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ) );
93  if ( ! command.empty() )
94  pinfo.command = command.basename();
95  }
96  //MIL << " Take " << pinfo << endl;
97  }
98 
99 
105  inline void addCacheIf( CacheEntry & cache_r, const std::string & line_r, bool verbose_r )
106  {
107  const char * f = 0;
108  const char * t = 0;
109  const char * n = 0;
110 
111  for_( ch, line_r.c_str(), ch+line_r.size() )
112  {
113  switch ( *ch )
114  {
115  case 'k':
116  if ( *(ch+1) != '0' ) // skip non-zero link counts
117  return;
118  break;
119  case 'f':
120  f = ch+1;
121  break;
122  case 't':
123  t = ch+1;
124  break;
125  case 'n':
126  n = ch+1;
127  break;
128  }
129  if ( *ch == '\n' ) break; // end of data
130  do { ++ch; } while ( *ch != '\0' ); // skip to next field
131  }
132 
133  if ( !t || !f || !n )
134  return; // wrong filedescriptor/type/name
135 
136  if ( !( ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
137  || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
138  return; // wrong type
139 
140  if ( !( ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
141  || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
142  || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
143  || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
144  return; // wrong filedescriptor type
145 
146  if ( str::contains( n, "(stat: Permission denied)" ) )
147  return; // Avoid reporting false positive due to insufficient permission.
148 
149  if ( ! verbose_r )
150  {
151  if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
152  return; // Try to avoid reporting false positive unless verbose.
153  }
154 
155  if ( *f == 'm' || *f == 'D' ) // skip some wellknown nonlibrary memorymapped files
156  {
157  static const char * black[] = {
158  "/SYSV"
159  , "/var/run/"
160  , "/dev/"
161  };
162  for_( it, arrayBegin( black ), arrayEnd( black ) )
163  {
164  if ( str::hasPrefix( n, *it ) )
165  return;
166  }
167  }
168  // Add if no duplicate
169  cache_r.second.insert( n );
170  }
172  } // namespace
174 
176  {
177  _data.clear();
178 
179  static const char* argv[] =
180  {
181  "lsof", "-n", "-FpcuLRftkn0", NULL
182  };
184 
185  // cachemap: PID => (deleted files)
186  std::map<pid_t,CacheEntry> cachemap;
187  pid_t cachepid;
188  for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
189  {
190  // NOTE: line contains '\0' separeated fields!
191  if ( line[0] == 'p' )
192  {
193  str::strtonum( line.c_str()+1, cachepid ); // line is "p<PID>\0...."
194  cachemap[cachepid].first.swap( line );
195  }
196  else
197  {
198  addCacheIf( cachemap[cachepid], line, verbose_r );
199  }
200  }
201 
202  int ret = prog.close();
203  if ( ret != 0 )
204  {
205  if ( ret == 129 )
206  {
207  ZYPP_THROW( Exception(_("Please install package 'lsof' first.") ) );
208  }
209  Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
210  err.remember( prog.execError() );
211  ZYPP_THROW( err );
212  }
213 
214  std::vector<ProcInfo> data;
215  for ( const auto & cached : cachemap )
216  {
217  addDataIf( data, cached.second );
218  }
219  _data.swap( data );
220  return _data.size();
221  }
222 
223  std::string CheckAccessDeleted::findService( const Pathname & command_r )
224  {
225  ProcInfo p;
226  p.command = command_r.basename();
227  return p.service();
228  }
229  std::string CheckAccessDeleted::findService( const char * command_r )
230  { return findService( Pathname( command_r ) ); }
231 
232  std::string CheckAccessDeleted::findService( const std::string & command_r )
233  { return findService( Pathname( command_r ) ); }
234 
235  std::string CheckAccessDeleted::findService( pid_t pid_r )
236  { return findService( filesystem::readlink( Pathname("/proc")/str::numstring(pid_r)/"exe" ) ); }
237 
239  namespace
240  {
241  } // namespace
244 
246  {
247  if ( command.empty() )
248  return std::string();
249  // TODO: This needs to be implemented smarter... be carefull
250  // as we don't know whether the target is up.
251 
252  static const Pathname initD( "/etc/init.d" );
253  { // init.d script with same name
254  PathInfo pi( initD/command );
255  if ( pi.isFile() && pi.isX() )
256  return command;
257  }
258  { // init.d script with name + 'd'
259  std::string alt( command+"d" );
260  PathInfo pi( initD/alt );
261  if ( pi.isFile() && pi.isX() )
262  return alt;
263  }
264  if ( *command.rbegin() == 'd' )
265  { // init.d script with name - trailing'd'
266  std::string alt( command );
267  alt.erase( alt.size()-1 );
268  PathInfo pi( initD/alt );
269  WAR <<pi << endl;
270  if ( pi.isFile() && pi.isX() )
271  return alt;
272  }
273  return std::string();
274  }
275 
276  /******************************************************************
277  **
278  ** FUNCTION NAME : operator<<
279  ** FUNCTION TYPE : std::ostream &
280  */
281  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
282  {
283  return dumpRange( str << "CheckAccessDeleted ",
284  obj.begin(),
285  obj.end() );
286  }
287 
288  /******************************************************************
289  **
290  ** FUNCTION NAME : operator<<
291  ** FUNCTION TYPE : std::ostream &
292  */
293  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
294  {
295  if ( obj.pid.empty() )
296  return str << "<NoProc>";
297 
298  return dumpRangeLine( str << obj.command
299  << '<' << obj.pid
300  << '|' << obj.ppid
301  << '|' << obj.puid
302  << '|' << obj.login
303  << '>',
304  obj.files.begin(),
305  obj.files.end() );
306  }
307 
309 } // namespace zypp
Interface to gettext.
Data about one running process accessing deleted files.
bool contains(const C_Str &str_r, const C_Str &val_r)
Locate substring case sensitive.
Definition: String.h:770
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:320
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like &#39;readlink&#39;.
Definition: PathInfo.cc:853
std::vector< ProcInfo > _data
const std::string & execError() const
Some detail telling why the execution failed, if it failed.
std::string service() const
Guess if command was started by an /etc/init.d/ script.
std::string command
process command name
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
std::ostream & operator<<(std::ostream &str, const CheckAccessDeleted &obj)
void remember(const Exception &old_r)
Store an other Exception as history.
Definition: Exception.cc:89
size_type check(bool verbose_r=false)
Check for running processes which access deleted executables or libraries.
std::ostream & dumpRange(std::ostream &str, _Iterator begin, _Iterator end, const std::string &intro="{", const std::string &pfx="\n ", const std::string &sep="\n ", const std::string &sfx="\n", const std::string &extro="}")
Print range defined by iterators (multiline style).
Definition: LogTools.h:91
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
#define WAR
Definition: Logger.h:48
std::string puid
process user ID
#define _(MSG)
Return translated text.
Definition: Gettext.h:21
std::string receiveLine()
Read one line from the input stream.
_It strtonum(const C_Str &str)
Parsing numbers from string.
Definition: String.h:304
std::ostream & dumpRangeLine(std::ostream &str, _Iterator begin, _Iterator end)
Print range defined by iterators (single line style).
Definition: LogTools.h:114
std::string numstring(char n, int w=0)
Definition: String.h:219
int close()
Wait for the progamm to complete.
#define arrayEnd(A)
Definition: Easy.h:42
#define arrayBegin(A)
Simple C-array iterator.
Definition: Easy.h:40
std::string form(const char *format,...)
Printf style construction of std::string.
Definition: String.cc:34
Base class for Exception.
Definition: Exception.h:143
Check for running processes which access deleted executables or libraries.
const_iterator end() const
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:828
const_iterator begin() const
std::string login
process login name
static std::string findService(const char *command_r)
Guess if command was started by an /etc/init.d/ script.
std::vector< std::string > files
list of deleted executables or libraries accessed
std::string ppid
parent process ID