libzypp  15.21.1
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 #include "zypp/base/Regex.h"
22 #include "zypp/base/IOStream.h"
23 #include "zypp/base/InputStream.h"
24 
26 
27 using std::endl;
28 
29 #undef ZYPP_BASE_LOGGER_LOGGROUP
30 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
31 
33 namespace zypp
34 {
35 
37  namespace
38  {
39  //
40  // lsof output lines are a sequence of NUL terminated fields,
41  // where the 1st char determines the fields type.
42  //
43  // (pcuL) pid command userid loginname
44  // (ftkn).filedescriptor type linkcount filename
45  //
47 
49  typedef std::pair<std::string,std::unordered_set<std::string>> CacheEntry;
50 
55  inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, const CacheEntry & cache_r )
56  {
57  const auto & filelist( cache_r.second );
58 
59  if ( filelist.empty() )
60  return;
61 
62  // at least one file access so keep it:
63  data_r.push_back( CheckAccessDeleted::ProcInfo() );
64  CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
65  pinfo.files.insert( pinfo.files.begin(), filelist.begin(), filelist.end() );
66 
67  const std::string & pline( cache_r.first );
68  for_( ch, pline.begin(), pline.end() )
69  {
70  switch ( *ch )
71  {
72  case 'p':
73  pinfo.pid = &*(ch+1);
74  break;
75  case 'R':
76  pinfo.ppid = &*(ch+1);
77  break;
78  case 'u':
79  pinfo.puid = &*(ch+1);
80  break;
81  case 'L':
82  pinfo.login = &*(ch+1);
83  break;
84  case 'c':
85  pinfo.command = &*(ch+1);
86  break;
87  }
88  if ( *ch == '\n' ) break; // end of data
89  do { ++ch; } while ( *ch != '\0' ); // skip to next field
90  }
91 
92  if ( pinfo.command.size() == 15 )
93  {
94  // the command name might be truncated, so we check against /proc/<pid>/exe
95  Pathname command( filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ) );
96  if ( ! command.empty() )
97  pinfo.command = command.basename();
98  }
99  //MIL << " Take " << pinfo << endl;
100  }
101 
102 
108  inline void addCacheIf( CacheEntry & cache_r, const std::string & line_r, bool verbose_r )
109  {
110  const char * f = 0;
111  const char * t = 0;
112  const char * n = 0;
113 
114  for_( ch, line_r.c_str(), ch+line_r.size() )
115  {
116  switch ( *ch )
117  {
118  case 'k':
119  if ( *(ch+1) != '0' ) // skip non-zero link counts
120  return;
121  break;
122  case 'f':
123  f = ch+1;
124  break;
125  case 't':
126  t = ch+1;
127  break;
128  case 'n':
129  n = ch+1;
130  break;
131  }
132  if ( *ch == '\n' ) break; // end of data
133  do { ++ch; } while ( *ch != '\0' ); // skip to next field
134  }
135 
136  if ( !t || !f || !n )
137  return; // wrong filedescriptor/type/name
138 
139  if ( !( ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
140  || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
141  return; // wrong type
142 
143  if ( !( ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
144  || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
145  || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
146  || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
147  return; // wrong filedescriptor type
148 
149  if ( str::contains( n, "(stat: Permission denied)" ) )
150  return; // Avoid reporting false positive due to insufficient permission.
151 
152  if ( ! verbose_r )
153  {
154  if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
155  return; // Try to avoid reporting false positive unless verbose.
156  }
157 
158  if ( *f == 'm' || *f == 'D' ) // skip some wellknown nonlibrary memorymapped files
159  {
160  static const char * black[] = {
161  "/SYSV"
162  , "/var/run/"
163  , "/dev/"
164  };
165  for_( it, arrayBegin( black ), arrayEnd( black ) )
166  {
167  if ( str::hasPrefix( n, *it ) )
168  return;
169  }
170  }
171  // Add if no duplicate
172  cache_r.second.insert( n );
173  }
174 
181  struct FilterRunsInLXC
182  {
183  bool operator()( pid_t pid_r ) const
184  { return( nsIno( pid_r, "pid" ) != pidNS || nsIno( pid_r, "mnt" ) != mntNS ); }
185 
186  FilterRunsInLXC()
187  : pidNS( nsIno( "self", "pid" ) )
188  , mntNS( nsIno( "self", "mnt" ) )
189  {}
190 
191  static inline ino_t nsIno( const std::string & pid_r, const std::string & ns_r )
192  { return PathInfo("/proc/"+pid_r+"/ns/"+ns_r).ino(); }
193 
194  static inline ino_t nsIno( pid_t pid_r, const std::string & ns_r )
195  { return nsIno( asString(pid_r), ns_r ); }
196 
197  ino_t pidNS;
198  ino_t mntNS;
199  };
200 
202  } // namespace
204 
206  {
207  _data.clear();
208 
209  static const char* argv[] =
210  {
211  "lsof", "-n", "-FpcuLRftkn0", NULL
212  };
214 
215  // cachemap: PID => (deleted files)
216  // NOTE: omit PIDs running in a (lxc/docker) container
217  std::map<pid_t,CacheEntry> cachemap;
218  pid_t cachepid = 0;
219  FilterRunsInLXC runsInLXC;
220  for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
221  {
222  // NOTE: line contains '\0' separeated fields!
223  if ( line[0] == 'p' )
224  {
225  str::strtonum( line.c_str()+1, cachepid ); // line is "p<PID>\0...."
226  if ( !runsInLXC( cachepid ) )
227  cachemap[cachepid].first.swap( line );
228  else
229  cachepid = 0; // ignore this pid
230  }
231  else if ( cachepid )
232  {
233  addCacheIf( cachemap[cachepid], line, verbose_r );
234  }
235  }
236 
237  int ret = prog.close();
238  if ( ret != 0 )
239  {
240  if ( ret == 129 )
241  {
242  ZYPP_THROW( Exception(_("Please install package 'lsof' first.") ) );
243  }
244  Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
245  err.remember( prog.execError() );
246  ZYPP_THROW( err );
247  }
248 
249  std::vector<ProcInfo> data;
250  for ( const auto & cached : cachemap )
251  {
252  addDataIf( data, cached.second );
253  }
254  _data.swap( data );
255  return _data.size();
256  }
257 
258  std::string CheckAccessDeleted::findService( pid_t pid_r )
259  {
260  ProcInfo p;
261  p.pid = str::numstring( pid_r );
262  return p.service();
263  }
264 
266  namespace
267  {
268  } // namespace
271 
273  {
274  static const str::regex rx( "[0-9]+:name=systemd:/system.slice/(.*/)?(.*).service$" );
275  str::smatch what;
276  std::string ret;
277  iostr::simpleParseFile( InputStream( Pathname("/proc")/pid/"cgroup" ),
278  [&]( int num_r, std::string line_r )->bool
279  {
280  if ( str::regex_match( line_r, what, rx ) )
281  {
282  ret = what[2];
283  return false; // stop after match
284  }
285  return true;
286  } );
287  return ret;
288  }
289 
290  /******************************************************************
291  **
292  ** FUNCTION NAME : operator<<
293  ** FUNCTION TYPE : std::ostream &
294  */
295  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
296  {
297  return dumpRange( str << "CheckAccessDeleted ",
298  obj.begin(),
299  obj.end() );
300  }
301 
302  /******************************************************************
303  **
304  ** FUNCTION NAME : operator<<
305  ** FUNCTION TYPE : std::ostream &
306  */
307  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
308  {
309  if ( obj.pid.empty() )
310  return str << "<NoProc>";
311 
312  return dumpRangeLine( str << obj.command
313  << '<' << obj.pid
314  << '|' << obj.ppid
315  << '|' << obj.puid
316  << '|' << obj.login
317  << '>',
318  obj.files.begin(),
319  obj.files.end() );
320  }
321 
323 } // namespace zypp
std::string asString(const Patch::Category &obj)
Definition: Patch.cc:117
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:964
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:321
Regular expression.
Definition: Regex.h:86
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like 'readlink'.
Definition: PathInfo.cc:847
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 a systemd service script.
std::string command
process command name
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::ostream & operator<<(std::ostream &str, const CheckAccessDeleted &obj)
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:89
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
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
TInt strtonum(const C_Str &str)
Parsing numbers from string.
Definition: String.h:403
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:304
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
Regular expression match result.
Definition: Regex.h:145
Base class for Exception.
Definition: Exception.h:143
ino_t mntNS
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
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1027
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.