libzypp  16.17.12
CheckAccessDeleted.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <fstream>
14 #include <unordered_set>
15 #include "zypp/base/LogTools.h"
16 #include "zypp/base/String.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/Exception.h"
19 
20 #include "zypp/PathInfo.h"
21 #include "zypp/ExternalProgram.h"
22 #include "zypp/base/Regex.h"
23 #include "zypp/base/IOStream.h"
24 #include "zypp/base/InputStream.h"
25 
27 
28 using std::endl;
29 
30 #undef ZYPP_BASE_LOGGER_LOGGROUP
31 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
32 
34 namespace zypp
35 {
36 
38  namespace
39  {
40  //
41  // lsof output lines are a sequence of NUL terminated fields,
42  // where the 1st char determines the fields type.
43  //
44  // (pcuL) pid command userid loginname
45  // (ftkn).filedescriptor type linkcount filename
46  //
48 
50  typedef std::pair<std::string,std::unordered_set<std::string>> CacheEntry;
51 
56  inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, const CacheEntry & cache_r )
57  {
58  const auto & filelist( cache_r.second );
59 
60  if ( filelist.empty() )
61  return;
62 
63  // at least one file access so keep it:
64  data_r.push_back( CheckAccessDeleted::ProcInfo() );
65  CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
66  pinfo.files.insert( pinfo.files.begin(), filelist.begin(), filelist.end() );
67 
68  const std::string & pline( cache_r.first );
69  std::string commandname; // pinfo.command if still needed...
70  for_( ch, pline.begin(), pline.end() )
71  {
72  switch ( *ch )
73  {
74  case 'p':
75  pinfo.pid = &*(ch+1);
76  break;
77  case 'R':
78  pinfo.ppid = &*(ch+1);
79  break;
80  case 'u':
81  pinfo.puid = &*(ch+1);
82  break;
83  case 'L':
84  pinfo.login = &*(ch+1);
85  break;
86  case 'c':
87  if ( pinfo.command.empty() )
88  commandname = &*(ch+1);
89  break;
90  }
91  if ( *ch == '\n' ) break; // end of data
92  do { ++ch; } while ( *ch != '\0' ); // skip to next field
93  }
94 
95  if ( pinfo.command.empty() )
96  {
97  // the lsof command name might be truncated, so we prefer /proc/<pid>/exe
98  pinfo.command = filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ).basename();
99  if ( pinfo.command.empty() )
100  pinfo.command = std::move(commandname);
101  }
102  }
103 
104 
110  inline void addCacheIf( CacheEntry & cache_r, const std::string & line_r, bool verbose_r )
111  {
112  const char * f = 0;
113  const char * t = 0;
114  const char * n = 0;
115 
116  for_( ch, line_r.c_str(), ch+line_r.size() )
117  {
118  switch ( *ch )
119  {
120  case 'k':
121  if ( *(ch+1) != '0' ) // skip non-zero link counts
122  return;
123  break;
124  case 'f':
125  f = ch+1;
126  break;
127  case 't':
128  t = ch+1;
129  break;
130  case 'n':
131  n = ch+1;
132  break;
133  }
134  if ( *ch == '\n' ) break; // end of data
135  do { ++ch; } while ( *ch != '\0' ); // skip to next field
136  }
137 
138  if ( !t || !f || !n )
139  return; // wrong filedescriptor/type/name
140 
141  if ( !( ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
142  || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
143  return; // wrong type
144 
145  if ( !( ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
146  || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
147  || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
148  || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
149  return; // wrong filedescriptor type
150 
151  if ( str::contains( n, "(stat: Permission denied)" ) )
152  return; // Avoid reporting false positive due to insufficient permission.
153 
154  if ( ! verbose_r )
155  {
156  if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
157  return; // Try to avoid reporting false positive unless verbose.
158  }
159 
160  if ( *f == 'm' || *f == 'D' ) // skip some wellknown nonlibrary memorymapped files
161  {
162  static const char * black[] = {
163  "/SYSV"
164  , "/var/run/"
165  , "/var/lib/sss/"
166  , "/dev/"
167  , "/var/lib/gdm"
168  };
169  for_( it, arrayBegin( black ), arrayEnd( black ) )
170  {
171  if ( str::hasPrefix( n, *it ) )
172  return;
173  }
174  }
175  // Add if no duplicate
176  cache_r.second.insert( n );
177  }
178 
185  struct FilterRunsInLXC
186  {
187  bool operator()( pid_t pid_r ) const
188  { return( nsIno( pid_r, "pid" ) != pidNS ); }
189 
190  FilterRunsInLXC()
191  : pidNS( nsIno( "self", "pid" ) )
192  {}
193 
194  static inline ino_t nsIno( const std::string & pid_r, const std::string & ns_r )
195  { return PathInfo("/proc/"+pid_r+"/ns/"+ns_r).ino(); }
196 
197  static inline ino_t nsIno( pid_t pid_r, const std::string & ns_r )
198  { return nsIno( asString(pid_r), ns_r ); }
199 
200  ino_t pidNS;
201  };
202 
203 #if 0
204  void lsofdebug( const Pathname & file_r )
205  {
206  std::ifstream infile( file_r.c_str() );
207  USR << infile << endl;
208  std::vector<std::string> fields;
209  CacheEntry cache;
210  for( iostr::EachLine in( infile ); in; in.next() )
211  {
212  std::string field( *in );
213  if ( field[0] == 'f' || field[0] == 'p' )
214  {
215  if ( !fields.empty() )
216  {
217  // consume
218  std::string line( str::join( fields, "\n" ) );
219  for ( char & c : line )
220  { if ( c == '\n' ) c = '\0'; }
221  line.push_back( '\n' );
222 
223  size_t sze = cache.second.size();
224  addCacheIf( cache, line, false );
225  if ( sze != cache.second.size() )
226  USR << fields << endl;
227 
228  fields.clear();
229  }
230  if ( field[0] == 'p' )
231  continue;
232  fields.push_back( field );
233  }
234  else if ( !fields.empty() )
235  {
236  fields.push_back( field );
237  }
238  }
239  }
240 #endif
241  } // namespace
244 
246  {
247  _data.clear();
248 
249  static const char* argv[] =
250  {
251  "lsof", "-n", "-FpcuLRftkn0", NULL
252  };
254 
255  // cachemap: PID => (deleted files)
256  // NOTE: omit PIDs running in a (lxc/docker) container
257  std::map<pid_t,CacheEntry> cachemap;
258  pid_t cachepid = 0;
259  FilterRunsInLXC runsInLXC;
260  for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
261  {
262  // NOTE: line contains '\0' separeated fields!
263  if ( line[0] == 'p' )
264  {
265  str::strtonum( line.c_str()+1, cachepid ); // line is "p<PID>\0...."
266  if ( !runsInLXC( cachepid ) )
267  cachemap[cachepid].first.swap( line );
268  else
269  cachepid = 0; // ignore this pid
270  }
271  else if ( cachepid )
272  {
273  addCacheIf( cachemap[cachepid], line, verbose_r );
274  }
275  }
276 
277  int ret = prog.close();
278  if ( ret != 0 )
279  {
280  if ( ret == 129 )
281  {
282  ZYPP_THROW( Exception(_("Please install package 'lsof' first.") ) );
283  }
284  Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
285  err.remember( prog.execError() );
286  ZYPP_THROW( err );
287  }
288 
289  std::vector<ProcInfo> data;
290  for ( const auto & cached : cachemap )
291  {
292  addDataIf( data, cached.second );
293  }
294  _data.swap( data );
295  return _data.size();
296  }
297 
298  std::string CheckAccessDeleted::findService( pid_t pid_r )
299  {
300  ProcInfo p;
301  p.pid = str::numstring( pid_r );
302  return p.service();
303  }
304 
306  namespace
307  {
308  } // namespace
311 
313  {
314  static const str::regex rx( "[0-9]+:name=systemd:/system.slice/(.*/)?(.*).service$" );
315  str::smatch what;
316  std::string ret;
317  iostr::simpleParseFile( InputStream( Pathname("/proc")/pid/"cgroup" ),
318  [&]( int num_r, std::string line_r )->bool
319  {
320  if ( str::regex_match( line_r, what, rx ) )
321  {
322  ret = what[2];
323  return false; // stop after match
324  }
325  return true;
326  } );
327  return ret;
328  }
329 
330  /******************************************************************
331  **
332  ** FUNCTION NAME : operator<<
333  ** FUNCTION TYPE : std::ostream &
334  */
335  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
336  {
337  return dumpRange( str << "CheckAccessDeleted ",
338  obj.begin(),
339  obj.end() );
340  }
341 
342  /******************************************************************
343  **
344  ** FUNCTION NAME : operator<<
345  ** FUNCTION TYPE : std::ostream &
346  */
347  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
348  {
349  if ( obj.pid.empty() )
350  return str << "<NoProc>";
351 
352  return dumpRangeLine( str << obj.command
353  << '<' << obj.pid
354  << '|' << obj.ppid
355  << '|' << obj.puid
356  << '|' << obj.login
357  << '>',
358  obj.files.begin(),
359  obj.files.end() );
360  }
361 
363 } // 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:965
std::string asString(const DefaultIntegral< Tp, TInitial > &obj)
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:350
Regular expression.
Definition: Regex.h:86
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like &#39;readlink&#39;.
Definition: PathInfo.cc:877
std::string join(TIterator begin, TIterator end, const C_Str &sep_r=" ")
Join strings using separator sep_r (defaults to BLANK).
Definition: String.h:759
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 a systemd service script.
std::string command
process command name
String related utilities and Regular expression matching.
Helper to create and pass std::istream.
Definition: InputStream.h:56
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
void remember(const Exception &old_r)
Store an other Exception as history.
Definition: Exception.cc:105
size_type check(bool verbose_r=false)
Check for running processes which access deleted executables or libraries.
int simpleParseFile(std::istream &str_r, ParseFlags flags_r, function< bool(int, std::string)> consume_r)
Simple lineparser optionally trimming and skipping comments.
Definition: IOStream.cc:124
std::ostream & dumpRange(std::ostream &str, TIterator begin, TIterator 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
std::ostream & operator<<(std::ostream &str, const Exception &obj)
Definition: Exception.cc:147
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
std::list< PublicKeyData > _data
Definition: KeyRing.cc:122
#define arrayEnd(A)
Definition: Easy.h:42
#define USR
Definition: Logger.h:69
TInt strtonum(const C_Str &str)
Parsing numbers from string.
Definition: String.h:404
std::string puid
process user ID
#define _(MSG)
Definition: Gettext.h:29
std::string receiveLine()
Read one line from the input stream.
std::string numstring(char n, int w=0)
Definition: String.h:305
int close()
Wait for the progamm to complete.
#define arrayBegin(A)
Simple C-array iterator.
Definition: Easy.h:40
Regular expression match result.
Definition: Regex.h:145
Base class for Exception.
Definition: Exception.h:143
Check for running processes which access deleted executables or libraries.
const_iterator end() const
std::ostream & dumpRangeLine(std::ostream &str, TIterator begin, TIterator end)
Print range defined by iterators (single line style).
Definition: LogTools.h:114
ino_t pidNS
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
regex ZYPP_STR_REGEX regex ZYPP_STR_REGEX
Definition: Regex.h:70
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1028
const_iterator begin() const
std::string login
process login name
std::vector< std::string > files
list of deleted executables or libraries accessed
std::string ppid
parent process ID
static std::string findService(pid_t pid_r)
Guess if pid was started by a systemd service script.