DDS  ver. 1.6
Process.h
Go to the documentation of this file.
1 // Copyright 2014 GSI, Inc. All rights reserved.
2 //
3 // This header contains a subset of helpers for Process, Daemon and PID file operations.
4 //
5 #ifndef PROCESS_H_
6 #define PROCESS_H_
7 
8 // API
9 #include <dirent.h>
10 #include <fcntl.h>
11 #include <signal.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 #include <wordexp.h>
17 
18 #if defined(__APPLE__)
19 #include <sys/sysctl.h>
20 #endif
21 
22 // STD
23 #include <fstream>
24 #include <iterator>
25 #include <memory>
26 #include <stdexcept>
27 // POSIX regexp
28 #include <regex.h>
29 // MiscCommon
30 #include "CustomIterator.h"
31 #include "ErrorCode.h"
32 #include "MiscUtils.h"
33 #include "def.h"
34 #include "stlx.h"
35 
36 namespace MiscCommon
37 {
46  inline bool IsProcessExist(pid_t _PID)
47  {
48  return !(::kill(_PID, 0) == -1 && errno == ESRCH);
49  }
55  class CPIDFile
56  {
57  public:
58  CPIDFile(const std::string& _FileName, pid_t _PID)
59  : m_FileName(_FileName)
60  {
61  if (!_FileName.empty() && _PID > 0)
62  {
63  // Preventing to start a second "instance" if the pidfile references to the running process
64  const pid_t pid = GetPIDFromFile(m_FileName);
65  if (pid > 0 && IsProcessExist(pid))
66  {
67  // We don't want to unlink this file
68  m_FileName.clear();
69  throw std::runtime_error("Error creating pidfile. The process corresponding to pidfile \"" +
70  _FileName + "\" is still running");
71  }
72 
73  // Wrtiting new pidfile
74  std::ofstream f(m_FileName.c_str());
75  if (!f.is_open())
76  throw std::runtime_error("can't create PID file: " + m_FileName);
77 
78  f << _PID;
79  }
80  else
81  m_FileName.clear();
82  }
83 
85  {
86  if (!m_FileName.empty())
87  ::unlink(m_FileName.c_str());
88  }
89 
90  static pid_t GetPIDFromFile(const std::string& _FileName)
91  {
92  std::ifstream f(_FileName.c_str());
93  if (!f.is_open())
94  return 0;
95 
96  pid_t pid(0);
97  f >> pid;
98 
99  return pid;
100  }
101 
102  private:
103  std::string m_FileName;
104  };
105 
106 #if defined(__APPLE__)
107 
114  class CFindProcess
115  {
116  public:
117  typedef std::set<pid_t> ProcContainer_t;
118 
119  public:
120  static void getAllPIDsForProcessName(const std::string& _processName,
121  ProcContainer_t* _pidContainer,
122  bool _filterForRealUserID = false)
123  {
124  // Setting up the mib (Management Information Base)
125  // We pass CTL_KERN, KERN_PROC, KERN_PROC_ALL to sysctl as the MIB
126  // to get back a BSD structure with all BSD process information for
127  // all processes in it (including BSD process names)
128  int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
129 
130  size_t buffSize = 0; // set to zero to start with.
131  int error = 0;
132  struct kinfo_proc* BSDProcInfo = NULL;
133 
134  if (_processName.empty())
135  return;
136  if (!_pidContainer)
137  return;
138 
139  _pidContainer->clear();
140 
141  // Getting list of process information for all processes
142  bool done(false);
143  do
144  {
145  error = sysctl(mib, 3, NULL, &buffSize, NULL, 0);
146  if (error != 0)
147  throw system_error("Error occurred while retrieving a running processes list");
148 
149  BSDProcInfo = (struct kinfo_proc*)malloc(buffSize);
150 
151  if (BSDProcInfo == NULL)
152  throw system_error(
153  "Error occurred while retrieving a running processes list. Unable to allocate the buffer.");
154 
155  error = sysctl(mib, 3, BSDProcInfo, &buffSize, NULL, 0);
156 
157  // Here we successfully got the process information.
158  // Thus set the variable to end this sysctl calling loop
159  if (error == 0)
160  {
161  done = true;
162  }
163  else
164  {
165  // failed getting process information we will try again next time around the loop. Note this is
166  // caused
167  // by the fact the process list changed between getting the size of the buffer and actually filling
168  // the buffer (something which will happen from time to time since the process list is dynamic).
169  // Anyways, the attempted sysctl call failed. We will now begin again by freeing up the allocated
170  // buffer and starting again at the beginning of the loop.
171  free(BSDProcInfo);
172  }
173  } while (!done);
174 
175  // Going through process list looking for processes with matching names
176 
177  uid_t userid = getuid();
178 
179  const size_t processCount = buffSize / sizeof(struct kinfo_proc);
180  for (size_t i = 0; i < processCount; ++i)
181  {
182  // Getting PID of process we are examining
183  const pid_t pid = BSDProcInfo[i].kp_proc.p_pid;
184  // Getting name of process we are examining
185  const std::string name = BSDProcInfo[i].kp_proc.p_comm;
186  // Getting real user if of the process
187  const uid_t uid = BSDProcInfo[i].kp_eproc.e_pcred.p_ruid;
188 
189  if ((pid > 0) && (name == _processName))
190  {
191  if (!_filterForRealUserID)
192  _pidContainer->insert(pid);
193  else if (uid == userid)
194  _pidContainer->insert(pid);
195  }
196  }
197 
198  free(BSDProcInfo);
199  }
200 
201  static bool pidExists(int _pid)
202  {
203  int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
204  size_t length = 0;
205  if (sysctl(name, 4, NULL, &length, NULL, 0))
206  throw system_error("Unable to call sysctl in the pidExists function.");
207  kinfo_proc* result = (kinfo_proc*)malloc(length);
208  if (sysctl(name, 4, result, &length, NULL, 0))
209  throw system_error("Unable to call sysctl in the pidExists function.");
210  int i, procCount = length / sizeof(kinfo_proc);
211  for (i = 0; i < procCount; ++i)
212  {
213  kinfo_proc* test = &result[i];
214 
215  if (test->kp_proc.p_pid == _pid)
216  {
217  free(result);
218  return true;
219  }
220  test = NULL;
221  }
222 
223  free(result);
224  return false;
225  }
226  };
227 #endif
228 
240 #if !defined(__APPLE__)
241  class CProcList
242  {
243  public:
244  typedef std::set<pid_t> ProcContainer_t;
245 
246  public:
247  static void GetProcList(ProcContainer_t* _Procs)
248  {
249  if (!_Procs)
250  throw std::invalid_argument("CProcList::GetProcList: Input container is NULL");
251 
252  _Procs->clear();
253 
254  struct dirent** namelist;
255  // scanning the "/proc" filesystem
256  int n = scandir("/proc", &namelist, CheckDigit, alphasort);
257 
258  if (-1 == n)
259  throw system_error("CProcList::GetProcList exception");
260  if (0 == n)
261  return; // there were no files
262 
263  for (int i = 0; i < n; ++i)
264  {
265  std::stringstream ss(namelist[i]->d_name);
266  pid_t pid;
267  ss >> pid;
268  _Procs->insert(pid);
269  free(namelist[i]);
270  }
271 
272  free(namelist);
273  }
274 
275  private:
276  static int CheckDigit(const struct dirent* _d)
277  {
278  const std::string sName(_d->d_name);
279  // Checking whether file name has all digits
280  return (sName.end() == std::find_if(sName.begin(), sName.end(), std::not1(IsDigit())));
281  }
282  };
283 #endif
284 
285 #if !defined(__APPLE__)
286 
301  // TODO: need a new algorithms for a longer app names retrieval
303  {
304  typedef std::auto_ptr<std::ifstream> ifstream_ptr;
305  typedef std::map<std::string, std::string> keyvalue_t;
306 
307  public:
309  {
310  // Preparing regular expression pattern
311  regcomp(&m_re, "(.*):(.*)", REG_EXTENDED);
312  }
314  {
315  regfree(&m_re);
316  }
317  void Open(pid_t _PId)
318  {
319  m_values.clear();
320  if (m_f.get())
321  m_f->close();
322 
323  std::stringstream ss;
324  ss << "/proc/" << _PId << "/status";
325  m_f = ifstream_ptr(new std::ifstream(ss.str().c_str()));
326  // create reader objects
327  // HACK: the extra set of parenthesis (the last argument of vector's ctor) is required (for gcc 4.1+)
328  // StringVector_t vec( custom_istream_iterator<std::string>(*m_f),
329  // (custom_istream_iterator<std::string>()) );
330  // or
331  // custom_istream_iterator<std::string> in_begin(*m_f);
332  // custom_istream_iterator<std::string> in_end;
333  // StringVector_t vec( in_begin, in_end );
334  // the last method for gcc 3.2+
335  // , because
336  // the compiler is very aggressive in identifying function declarations and will identify the
337  // definition of vec as forward declaration of a function accepting two istream_iterator parameters
338  // and returning a vector of integers
341  StringVector_t vec(in_begin, in_end);
342 
343  for_each(vec.begin(), vec.end(), std::bind1st(MiscCommon::stlx::mem_fun(&CProcStatus::_Parser), this));
344  }
345  std::string GetValue(const std::string& _KeyName) const
346  {
347  // We want to be case insensitive
348  std::string sKey(_KeyName);
349  to_lower(sKey);
350 
351  keyvalue_t::const_iterator iter = m_values.find(sKey);
352  return (m_values.end() == iter ? std::string() : iter->second);
353  }
354 
355  private:
356  bool _Parser(const std::string& _sVal)
357  {
358  regmatch_t PMatch[3];
359  if (0 != regexec(&m_re, _sVal.c_str(), 3, PMatch, 0))
360  return false;
361  std::string sKey(_sVal.c_str() + PMatch[1].rm_so, PMatch[1].rm_eo - PMatch[1].rm_so);
362  std::string sValue(_sVal.c_str() + PMatch[2].rm_so, PMatch[2].rm_eo - PMatch[2].rm_so);
363  // We want to be case insensitive
364  to_lower(sKey);
365 
366  trim<std::string>(&sValue, '\t');
367  trim<std::string>(&sValue, ' ');
368  // insert key-value if found
369  m_values.insert(keyvalue_t::value_type(sKey, sValue));
370  return true;
371  }
372 
373  private:
374  ifstream_ptr m_f;
375  regex_t m_re;
376  keyvalue_t m_values;
377  };
378 #endif
379 
383 #if !defined(__APPLE__)
384  struct SFindName : public std::binary_function<CProcList::ProcContainer_t::value_type, std::string, bool>
385  {
386  bool operator()(CProcList::ProcContainer_t::value_type _pid, const std::string& _Name) const
387  {
388  CProcStatus p;
389  p.Open(_pid);
390  return (p.GetValue("Name") == _Name);
391  }
392  };
393 #endif
394 
398  typedef std::vector<pid_t> vectorPid_t;
399 
400  inline vectorPid_t getprocbyname(const std::string& _Srv, bool _filterForRealUserID = false)
401  {
402  vectorPid_t retVal;
403 #if defined(__APPLE__)
404  CFindProcess::ProcContainer_t container;
405  CFindProcess::getAllPIDsForProcessName(_Srv, &container, _filterForRealUserID);
406  copy(container.begin(), container.end(), std::back_inserter(retVal));
407 #else
409  CProcList::GetProcList(&pids);
410 
411  CProcList::ProcContainer_t::const_iterator iter = pids.begin();
412  while (true)
413  {
414  iter = std::find_if(iter, pids.end(), std::bind2nd(SFindName(), _Srv));
415  if (pids.end() == iter)
416  break;
417 
418  retVal.push_back(*iter);
419  ++iter;
420  };
421 #endif
422  return retVal;
423  }
424 
425  inline bool is_status_ok(int status)
426  {
427  return WIFEXITED(status) && WEXITSTATUS(status) == 0;
428  }
429 
430  inline pid_t do_execv_std2file(const std::string& _Command,
431  std::string _stdoutFileName,
432  std::string _stderrFileName)
433  {
434  //----- Expand the string for the program to run.
435  wordexp_t result;
436  switch (wordexp(_Command.c_str(), &result, 0))
437  {
438  case 0: // Successful.
439  break;
440  case WRDE_NOSPACE:
441  // If the error was WRDE_NOSPACE, then perhaps part of the result was allocated.
442  wordfree(&result);
443  default: // Some other error.
444  return -1;
445  }
447 
448  // make sure exe has it's exe falg
449  struct stat info;
450  stat(result.we_wordv[0], &info);
451  if (!(info.st_mode & S_IXUSR))
452  {
453  int ret = chmod(result.we_wordv[0], info.st_mode | S_IXUSR);
454  if (ret != 0)
455  throw system_error("Can't set executable flag on " + std::string(result.we_wordv[0]));
456  }
457 
458  pid_t child_pid(0);
459  switch (child_pid = fork())
460  {
461  case -1:
462  // Unable to fork
463  throw std::runtime_error("do_execv: Unable to fork process");
464 
465  case 0:
466  // child
467  int fd_out = open(_stdoutFileName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
468  if (fd_out < 0)
469  throw MiscCommon::system_error("Can't open file for user's process stdout: " + _stdoutFileName);
470  int fd_err = open(_stderrFileName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
471  if (fd_err < 0)
472  throw MiscCommon::system_error("Can't open file for user's process stderr: " + _stderrFileName);
473 
474  dup2(fd_out, 1); // make stdout go to file
475  dup2(fd_err, 2); // make stderr go to file - you may choose to not do this
476  // or perhaps send stderr to another file
477 
478  close(fd_out); // fd no longer needed - the dup'ed handles are sufficient
479  close(fd_err); // fd no longer needed - the dup'ed handles are sufficient
480 
481  // child: execute the required command, on success does not return
482  execv(result.we_wordv[0], result.we_wordv);
483  // not usually reached
484  exit(1);
485  }
486 
487  wordfree(&result);
488 
489  return child_pid;
490  }
491 
494  inline void do_execv(const std::string& _Command) throw(std::exception)
495  {
496  //----- Expand the string for the program to run.
497  wordexp_t result;
498  switch (wordexp(_Command.c_str(), &result, 0))
499  {
500  case 0: // Successful.
501  break;
502  case WRDE_NOSPACE:
503  // If the error was WRDE_NOSPACE, then perhaps part of the result was allocated.
504  wordfree(&result);
505  default: // Some other error.
506  return;
507  }
509 
510  // make sure exe has it's exe falg
511  struct stat info;
512  stat(result.we_wordv[0], &info);
513  if (!(info.st_mode & S_IXUSR))
514  {
515  int ret = chmod(result.we_wordv[0], info.st_mode | S_IXUSR);
516  if (ret != 0)
517  throw system_error("Can't set executable flag on " + std::string(result.we_wordv[0]));
518  }
519 
520  pid_t pid1;
521  pid_t pid2;
522  int status;
523 
524  if ((pid1 = fork()))
525  {
526  /* parent process A */
527  ::waitpid(pid1, &status, WUNTRACED);
528  wordfree(&result);
529  }
530  else if (!pid1)
531  {
532  /* child process B */
533  if ((pid2 = fork()))
534  {
535  exit(0);
536  }
537  else if (!pid2)
538  {
539  // child: execute the required command, on success does not return
540  execv(result.we_wordv[0], result.we_wordv);
541  // not usually reached
542  exit(1);
543  }
544  else
545  {
546  // Unable to fork
547  throw std::runtime_error("do_execv: Unable to fork process");
548  }
549  }
550  else
551  {
552  // Unable to fork
553  throw std::runtime_error("do_execv: Unable to fork process");
554  }
555  }
556 
557  // TODO: Document me!
558  // If _Delay is 0, then function returns child pid and doesn't wait for the child process to finish. Otherwise
559  // return value is 0.
560  inline pid_t do_execv(const std::string& _Command,
561  size_t _Delay,
562  std::string* _output,
563  std::string* _errout = nullptr,
564  int* _exitCode = nullptr) throw(std::exception)
565  {
566  pid_t child_pid;
567  int fdpipe_out[2];
568  int fdpipe_err[2];
569  if (_output)
570  {
571  if (pipe(fdpipe_out))
572  throw MiscCommon::system_error("Can't create stdout pipe.");
573  }
574  if (_errout)
575  {
576  if (pipe(fdpipe_err))
577  throw MiscCommon::system_error("Can't create stderr pipe.");
578  }
579 
580  //----- Expand the string for the program to run.
581  wordexp_t result;
582  switch (wordexp(_Command.c_str(), &result, 0))
583  {
584  case 0: // Successful.
585  break;
586  case WRDE_NOSPACE:
587  // If the error was WRDE_NOSPACE, then perhaps part of the result was allocated.
588  wordfree(&result);
589  default: // Some other error.
590  return -1;
591  }
593 
594  // make sure exe has it's exe falg
595  struct stat info;
596  stat(result.we_wordv[0], &info);
597  if (!(info.st_mode & S_IXUSR))
598  {
599  int ret = chmod(result.we_wordv[0], info.st_mode | S_IXUSR);
600  if (ret != 0)
601  throw system_error("Can't set executable flag on " + std::string(result.we_wordv[0]));
602  }
603  switch (child_pid = fork())
604  {
605  case -1:
606  if (_output)
607  {
608  close(fdpipe_out[0]);
609  close(fdpipe_out[1]);
610  }
611  if (_errout)
612  {
613  close(fdpipe_err[0]);
614  close(fdpipe_err[1]);
615  }
616  // Unable to fork
617  throw std::runtime_error("do_execv: Unable to fork process");
618 
619  case 0:
620  if (_output)
621  {
622  close(fdpipe_out[0]);
623  dup2(fdpipe_out[1], STDOUT_FILENO);
624  close(fdpipe_out[1]);
625  }
626  if (_errout)
627  {
628  close(fdpipe_err[0]);
629  dup2(fdpipe_err[1], STDERR_FILENO);
630  close(fdpipe_err[1]);
631  }
632 
633  // child: execute the required command, on success does not return
634  execv(result.we_wordv[0], result.we_wordv);
635  // not usually reached
636  exit(1);
637  }
638 
639  wordfree(&result);
640 
641  if (0 == _Delay)
642  {
643  return child_pid;
644  }
645 
646  // parent
647  if (_output)
648  {
649  close(fdpipe_out[1]);
650  char buf;
651  std::stringstream ss;
652  while (read(fdpipe_out[0], &buf, 1) > 0)
653  ss << buf;
654 
655  *_output = ss.str();
656  }
657  if (_errout)
658  {
659  close(fdpipe_err[1]);
660  char buf;
661  std::stringstream ss;
662  while (read(fdpipe_err[0], &buf, 1) > 0)
663  ss << buf;
664 
665  *_errout = ss.str();
666  }
667 
668  for (size_t i = 0; i < _Delay; ++i)
669  {
670  int stat;
671  if (child_pid == ::waitpid(child_pid, &stat, WNOHANG))
672  {
673  // check if the caller wants the exit code of the process
674  if (_exitCode != nullptr)
675  {
676  *_exitCode = WEXITSTATUS(stat);
677  }
678  if (!is_status_ok(stat))
679  {
680  std::stringstream ss;
681  ss << "do_execv: ";
682  if (WIFSIGNALED(stat))
683  ss << "Child ended because of an uncaught signal.";
684  else if (WIFSTOPPED(stat))
685  ss << "Child process has stopped.";
686 
687  ss << " (exit code: " << WEXITSTATUS(stat) << "). Process: \"" << _Command << "\"";
688  throw std::runtime_error(ss.str());
689  }
690  return 0;
691  }
692  // TODO: Needs to be fixed! Implement time-function based timeout measurements instead
693  sleep(1);
694  }
695  throw std::runtime_error("do_execv: Timeout has been reached, command execution will be terminated.");
696  // kills the child
697  kill(child_pid, SIGKILL);
698  return 0;
699  }
700 };
701 
702 #endif /*PROCESS_H_*/
std::vector< std::string > StringVector_t
An STL vector of strings.
Definition: def.h:115
This class is used to quarry a list of currently running processes.
Definition: Process.h:241
~CPIDFile()
Definition: Process.h:84
std::vector< pid_t > vectorPid_t
Definition: Process.h:398
The system_error exception class retrieves a string, which represent the last error.
Definition: ErrorCode.h:77
_T & to_lower(_T &_str)
convert string to lower case.
Definition: MiscUtils.h:245
void Open(pid_t _PId)
Definition: Process.h:317
A PID-file helper.
Definition: Process.h:55
mem_fun1_t< _Result, _Class, _Argument > mem_fun(_Result(_Class::*member)(_Argument))
The mem_fun() template is a custom mem_fun adapter, which extends std::mem_fun.
Definition: stlx.h:55
CProcStatus()
Definition: Process.h:308
CPIDFile(const std::string &_FileName, pid_t _PID)
Definition: Process.h:58
bool operator()(CProcList::ProcContainer_t::value_type _pid, const std::string &_Name) const
Definition: Process.h:386
pid_t do_execv_std2file(const std::string &_Command, std::string _stdoutFileName, std::string _stderrFileName)
Definition: Process.h:430
static void GetProcList(ProcContainer_t *_Procs)
Definition: Process.h:247
static pid_t GetPIDFromFile(const std::string &_FileName)
Definition: Process.h:90
~CProcStatus()
Definition: Process.h:313
This custom istream iterator helps to read input line by line without breaking lines after whitespace...
Definition: CustomIterator.h:24
vectorPid_t getprocbyname(const std::string &_Srv, bool _filterForRealUserID=false)
Definition: Process.h:400
std::set< pid_t > ProcContainer_t
Definition: Process.h:244
Definition: Process.h:384
Definition: def.h:152
Definition: MiscUtils.h:215
std::string GetValue(const std::string &_KeyName) const
Definition: Process.h:345
bool IsProcessExist(pid_t _PID)
The function checks, whether the process which corresponds to the given _PID can be found...
Definition: Process.h:46
void do_execv(const std::string &_Command)
We do not want the parent process to wait for its child process and we do not want to create a zombie...
Definition: Process.h:494
bool is_status_ok(int status)
Definition: Process.h:425
Miscellaneous functions and helpers are located here.
Definition: BOOST_FILESYSTEM.h:21
This class helps to retrieve process&#39;s information from /proc/<pid>/status.
Definition: Process.h:302