YARP
Yet Another Robot Platform
RunProcManager.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
9 
10 #include <yarp/os/Network.h>
11 #include <yarp/os/RpcClient.h>
12 #include <yarp/os/Semaphore.h>
13 #include <yarp/os/Time.h>
14 
16 
17 #include <cstring>
18 
19 #define WAIT() { RUNLOG("<<<mutex.lock()") mutex.lock(); RUNLOG(">>>mutex.lock()") }
20 #define POST() { RUNLOG("<<<mutex.unlock()") mutex.unlock(); RUNLOG(">>>mutex.unlock()") }
21 
22 #if defined(_WIN32)
23  #include <process.h>
24 
25  #define SIGKILL 9
26  static bool KILL(HANDLE handle)
27  {
28  BOOL bRet=TerminateProcess(handle, 0);
29 
30  CloseHandle(handle);
31  fprintf(stderr, "brutally terminated by TerminateProcess\n");
32 
33  return bRet?true:false;
34  }
35  static bool TERMINATE(PID pid);
36  #define CLOSE(h) CloseHandle(h)
37  #ifndef __GNUC__
38  static DWORD WINAPI ZombieHunter(__in LPVOID lpParameter)
39  #else
40  static DWORD WINAPI ZombieHunter(LPVOID lpParameter)
41  #endif
42  {
43  YarpRunInfoVector* pProcessVector=(YarpRunInfoVector*)lpParameter;
44 
45  while (true)
46  {
47  DWORD nCount=0;
48  HANDLE* aHandlesVector = nullptr;
49  pProcessVector->GetHandles(aHandlesVector, nCount);
50 
51  if (nCount)
52  {
53  WaitForMultipleObjects(nCount, aHandlesVector, FALSE, INFINITE);
54  delete [] aHandlesVector;
55  }
56  else
57  {
58  //hZombieHunter = nullptr;
59 
60  return 0;
61  }
62  }
63 
64  return 0;
65  }
66 #else // LINUX
67  #include <unistd.h>
68  #include <fcntl.h>
69 
70  int CLOSE(int h)
71  {
72  int ret=(close(h)==0);
73  return ret;
74  }
75 
76  int SIGNAL(int pid, int signum)
77  {
78  int ret=!yarp::os::impl::kill(pid, signum);
79  return ret;
80  }
81 #endif // LINUX
82 
83 YarpRunProcInfo::YarpRunProcInfo(std::string& alias, std::string& on, PID pidCmd, HANDLE handleCmd, bool hold)
84 {
85  mAlias=alias;
86  mOn=on;
87  mPidCmd=pidCmd;
88  mCleanCmd=false;
89  mHandleCmd=handleCmd;
90  mHold=hold;
91 }
92 
93 bool YarpRunProcInfo::Signal(int signum)
94 {
95 #if defined(_WIN32)
96  if (signum==SIGKILL)
97  {
98  if (mHandleCmd)
99  {
100  bool ret=KILL(mHandleCmd);
101  return ret;
102  }
103  }
104  else
105  {
106  if (mPidCmd)
107  {
108  bool ret=TERMINATE(mPidCmd);
109  return ret;
110  }
111  }
112 #else
113  if (mPidCmd && !mHold)
114  {
115  bool ret=SIGNAL(mPidCmd, signum);
116  return ret;
117  }
118 #endif
119 
120  return true;
121 }
122 
124 {
125  if (!mPidCmd)
126  {
127  return false;
128  }
129 #if defined(_WIN32)
130  DWORD status;
131  RUNLOG("<<<GetExitCodeProcess(mHandleCmd, &status)")
132  bool ret=(::GetExitCodeProcess(mHandleCmd, &status) && status==STILL_ACTIVE);
133  RUNLOG(">>>GetExitCodeProcess(mHandleCmd, &status)")
134  return ret;
135 #else
136  bool ret=!yarp::os::impl::kill(mPidCmd, 0);
137  return ret;
138 #endif
139 }
140 
142 {
143 #if !defined(_WIN32)
144  if (!mCleanCmd && yarp::os::impl::waitpid(mPidCmd, nullptr, WNOHANG) == mPidCmd)
145  {
146  fprintf(stderr, "CLEANUP cmd %d\n", mPidCmd);
147  mCleanCmd=true;
148  }
149 
150  return mCleanCmd;
151 #else
152  return true;
153 #endif
154 }
155 
157 {
158  m_nProcesses=0;
159  m_pStdioMate = nullptr;
160  for (auto & i : m_apList)
161  {
162  i = nullptr;
163  }
164 }
165 
167 {
168  WAIT()
169 
170  for (auto & i : m_apList)
171  {
172  if (i)
173  {
174  delete i;
175  i = nullptr;
176  }
177  }
178 
179 #if defined(_WIN32)
180  if (hZombieHunter)
181  {
182  HANDLE hkill=hZombieHunter;
183  hZombieHunter = nullptr;
184  TerminateThread(hkill, 0);
185  }
186 #endif
187 
188  POST()
189 }
190 
192 {
193  WAIT()
194 
196  {
197  fprintf(stderr, "ERROR: maximum process limit reached\n");
198  POST()
199  return false;
200  }
201 
202 #if defined(_WIN32)
203  if (hZombieHunter)
204  {
205  HANDLE hkill=hZombieHunter;
206  hZombieHunter = nullptr;
207  TerminateThread(hkill, 0);
208  }
209 #endif
210 
211  m_apList[m_nProcesses++]=process;
212 
213 #if defined(_WIN32)
214  hZombieHunter=CreateThread(0, 0, ZombieHunter, this, 0, 0);
215 #endif
216 
217  POST()
218  return true;
219 }
220 
221 int YarpRunInfoVector::Signal(std::string& alias, int signum)
222 {
223  WAIT()
224 
225  auto* *aKill=new YarpRunProcInfo*[m_nProcesses];
226  int nKill=0;
227 
228  for (int i=0; i<m_nProcesses; ++i)
229  {
230  if (m_apList[i] && m_apList[i]->Match(alias)) // && m_apList[i]->IsActive())
231  {
232  aKill[nKill++]=m_apList[i];
233  }
234  }
235 
236  POST()
237 
238  for (int k=0; k<nKill; ++k)
239  {
240  fprintf(stderr, "SIGNAL %s (%d)\n", aKill[k]->mAlias.c_str(), aKill[k]->mPidCmd);
241  aKill[k]->Signal(signum);
242  }
243 
244  delete [] aKill;
245 
246  return nKill;
247 }
248 
250 {
251  WAIT()
252 
253  auto* *aKill=new YarpRunProcInfo*[m_nProcesses];
254  int nKill=0;
255 
256  for (int i=0; i<m_nProcesses; ++i)
257  {
258  if (m_apList[i] && m_apList[i]->IsActive())
259  {
260  aKill[nKill++]=m_apList[i];
261  }
262  }
263 
264  POST()
265 
266  for (int k=0; k<nKill; ++k)
267  {
268  fprintf(stderr, "SIGNAL %s (%d)\n", aKill[k]->mAlias.c_str(), aKill[k]->mPidCmd);
269  aKill[k]->Signal(signum);
270  }
271 
272  delete [] aKill;
273 
274  return nKill;
275 }
276 
277 #if defined(_WIN32)
278 void YarpRunInfoVector::GetHandles(HANDLE* &lpHandles, DWORD &nCount)
279 {
280  WAIT()
281 
282  if (lpHandles) delete [] lpHandles;
283 
284  if (m_nProcesses>0) lpHandles=new HANDLE[m_nProcesses];
285 
286  for (int i=0; i<m_nProcesses; ++i) if (m_apList[i])
287  {
288  if (!m_apList[i]->IsActive())
289  {
290  fprintf(stderr, "CLEANUP %s (%d)\n", m_apList[i]->mAlias.c_str(), m_apList[i]->mPidCmd);
291  fflush(stderr);
292 
293  m_apList[i]->Clean();
294  delete m_apList[i];
295  m_apList[i]=0;
296  }
297  }
298 
299  Pack();
300 
301  for (int i=0; i<m_nProcesses; ++i)
302  {
303  lpHandles[nCount+i]=m_apList[i]->mHandleCmd;
304  }
305 
306  nCount+=m_nProcesses;
307 
308  POST()
309 }
310 
311 #else
312 
314 {
315  bool bFound=false;
316 
317  YarpRunProcInfo *pZombie = nullptr;
318 
319  WAIT()
320 
321  for (int i=0; i<m_nProcesses; ++i)
322  {
323  if (m_apList[i] && m_apList[i]->Clean(zombie, pZombie))
324  {
325  bFound=true;
326  if (pZombie) {
327  m_apList[i] = nullptr;
328  }
329  break;
330  }
331  }
332 
333  Pack();
334 
335  POST()
336 
337  if (pZombie)
338  {
339  pZombie->finalize();
340  delete pZombie;
341  }
342 
343  return bFound;
344 }
345 #endif
346 
348 {
349  WAIT()
350 
351  yarp::os::Bottle ps, line, grp;
352 
353  for (int i = 0; i < m_nProcesses; ++i) {
354  if (m_apList[i]) {
355  line.clear();
356 
357  grp.clear();
358  grp.addString("pid");
359  grp.addInt32(m_apList[i]->mPidCmd);
360  line.addList() = grp;
361 
362  grp.clear();
363  grp.addString("tag");
364  grp.addString(m_apList[i]->mAlias.c_str());
365  line.addList() = grp;
366 
367  grp.clear();
368  grp.addString("status");
369  grp.addString(m_apList[i]->IsActive() ? "running" : "zombie");
370  line.addList() = grp;
371 
372  grp.clear();
373  grp.addString("cmd");
374  grp.addString(m_apList[i]->mCmd.c_str());
375  line.addList() = grp;
376 
377  grp.clear();
378  grp.addString("env");
379  grp.addString(m_apList[i]->mEnv.c_str());
380  line.addList() = grp;
381 
382  ps.addList() = line;
383  }
384  }
385 
386  POST()
387 
388  return ps;
389 }
390 
391 bool YarpRunInfoVector::IsRunning(std::string &alias)
392 {
393  WAIT()
394 
395  for (int i=0; i<m_nProcesses; ++i)
396  {
397  if (m_apList[i] && m_apList[i]->Match(alias))
398  {
399  if (m_apList[i]->IsActive())
400  {
401  POST()
402  return true;
403  }
404  else
405  {
406  POST()
407  return false;
408  }
409  }
410  }
411 
412  POST()
413 
414  return false;
415 }
416 
418 {
419  int tot=0;
420 
421  for (int i=0; i<m_nProcesses; ++i)
422  {
423  if (m_apList[i])
424  {
425  m_apList[tot++]=m_apList[i];
426  }
427  }
428 
429  for (int i=tot; i<m_nProcesses; ++i)
430  {
431  m_apList[i]=nullptr;
432  }
433 
434  m_nProcesses=tot;
435 }
436 
438  std::string& on,
439  std::string& stdio,
440  PID pidCmd,
441  PID pidStdout,
442  FDESC readFromPipeCmdToStdout,
443  FDESC writeToPipeCmdToStdout,
444  HANDLE handleCmd,
445  bool hold)
446  :
447 YarpRunProcInfo(alias, on, pidCmd, handleCmd, hold)
448 {
449  mPidStdin=0;
450  mPidStdout=pidStdout;
451  mStdio=stdio;
452  mStdioUUID="";
453  mStdioVector = nullptr;
454 
457  mReadFromPipeCmdToStdout=readFromPipeCmdToStdout;
458  mWriteToPipeCmdToStdout=writeToPipeCmdToStdout;
459 
460  mKillingCmd=false;
461  mKillingStdio=false;
462  mKillingStdin=false;
463  mKillingStdout=false;
464 
465  mCleanStdin=true;
466  mCleanStdout=false;
467 }
468 
470  std::string& on,
471  std::string& stdio,
472  PID pidCmd,
473  std::string& stdioUUID,
474  YarpRunInfoVector* stdioVector,
475  PID pidStdin,
476  PID pidStdout,
477  FDESC readFromPipeStdinToCmd,
478  FDESC writeToPipeStdinToCmd,
479  FDESC readFromPipeCmdToStdout,
480  FDESC writeToPipeCmdToStdout,
481  HANDLE handleCmd,
482  bool hold)
483  :
484 YarpRunProcInfo(alias, on, pidCmd, handleCmd, hold)
485 {
486  mPidStdin=pidStdin;
487  mPidStdout=pidStdout;
488  mStdio=stdio;
489  mStdioUUID=stdioUUID;
490 
491  mStdioVector=stdioVector;
492 
493  mReadFromPipeStdinToCmd=readFromPipeStdinToCmd;
494  mWriteToPipeStdinToCmd=writeToPipeStdinToCmd;
495  mReadFromPipeCmdToStdout=readFromPipeCmdToStdout;
496  mWriteToPipeCmdToStdout=writeToPipeCmdToStdout;
497 
498  mKillingCmd=false;
499  mKillingStdio=false;
500  mKillingStdin=false;
501  mKillingStdout=false;
502 
503  mCleanStdin=false;
504  mCleanStdout=false;
505 }
506 
508 {
509 #if defined(_WIN32)
510  if (mPidCmd)
511  {
512  mPidCmd=0;
513 
518 
523 
524  if (mPidStdin) TERMINATE(mPidStdin);
525 
526  if (mPidStdout) TERMINATE(mPidStdout);
527 
528  TerminateStdio();
529  }
530 
531  return false;
532 
533 #else
534 
535  if (!mCleanCmd && yarp::os::impl::waitpid(mPidCmd, nullptr, WNOHANG) == mPidCmd)
536  {
537  fprintf(stderr, "CLEANUP cmd %d\n", mPidCmd);
538  mCleanCmd=true;
539  }
540 
541  if (!mCleanStdin && yarp::os::impl::waitpid(mPidStdin, nullptr, WNOHANG) == mPidStdin)
542  {
543  fprintf(stderr, "CLEANUP stdin %d\n", mPidStdin);
544  mCleanStdin=true;
545  }
546 
547  if (!mCleanStdout && yarp::os::impl::waitpid(mPidStdout, nullptr, WNOHANG) == mPidStdout)
548  {
549  fprintf(stderr, "CLEANUP stdout %d\n", mPidStdout);
550  mCleanStdout=true;
551  }
552 
553  if (!(mCleanCmd || mCleanStdin || mCleanStdout))
554  {
555  return false;
556  }
557 
558  if (!mKillingStdio)
559  {
560  mKillingStdio=true;
561 
564  }
567  }
570  }
573  }
574 
579  }
580 
581  if (!mCleanCmd && !mKillingCmd)
582  {
583  yarp::os::impl::kill(mPidCmd, SIGTERM);
584  mKillingCmd=true;
585  }
586 
587  if (!mCleanStdin && !mKillingStdin)
588  {
589  yarp::os::impl::kill(mPidStdin, SIGTERM);
590  mKillingStdin=true;
591  }
592 
593  if (!mCleanStdout && !mKillingStdout)
594  {
595  yarp::os::impl::kill(mPidStdout, SIGTERM);
596  mKillingStdout=true;
597  }
598 
600  {
601  return true;
602  }
603 
604  return false;
605 #endif
606 }
607 
609 {
610  if (!mStdioVector) {
611  return;
612  }
613 
614  if (mOn==mStdio)
615  {
616  mStdioVector->Signal(mAlias, SIGTERM);
617  }
618  else
619  {
620  yarp::os::Bottle msg;
621  msg.fromString(std::string("(killstdio ")+mAlias+")");
622  yarp::run::Run::sendMsg(msg, mStdio);
623  }
624 }
625 
629 
630 #if defined(_WIN32)
631 
632 #define TA_FAILED 0
633 #define TA_SUCCESS_CLEAN 1
634 #define TA_SUCCESS_KILL 2
635 
636 class TerminateParams
637 {
638 public:
639  TerminateParams(DWORD id)
640  {
641  nWin=0;
642  dwID=id;
643  }
644 
645  ~TerminateParams(){}
646 
647  int nWin;
648  DWORD dwID;
649 };
650 
651 BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM lParam)
652 {
653  TerminateParams* params=(TerminateParams*)lParam;
654 
655  DWORD dwID;
656  GetWindowThreadProcessId(hwnd, &dwID) ;
657 
658  if (dwID==params->dwID)
659  {
660  params->nWin++;
661  PostMessage(hwnd, WM_CLOSE, 0, 0);
662  }
663 
664  return TRUE;
665 }
666 
667 /*----------------------------------------------------------------
668 Purpose:
669 Shut down a 32-Bit Process
670 
671 Parameters:
672 dwPID
673 Process ID of the process to shut down.
674 ----------------------------------------------------------------*/
675 bool TERMINATE(PID dwPID)
676 {
677  HANDLE hProc;
678 
679  // If we can't open the process with PROCESS_TERMINATE rights,
680  // then we give up immediately.
681  hProc=OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, dwPID);
682 
683  if (hProc == nullptr)
684  {
685  return false;
686  }
687 
688  // TerminateAppEnum() posts WM_CLOSE to all windows whose PID
689  // matches your process's.
690 
691  TerminateParams params(dwPID);
692 
693  EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)&params);
694 
695  if (params.nWin)
696  {
697  fprintf(stderr, "%d terminated by WM_CLOSE (sending anyway CTRL_C_EVENT/CTRL_BREAK_EVENT)\n", dwPID);
698  }
699  else
700  {
701  //GenerateConsoleCtrlEvent(CTRL_C_EVENT, dwPID);
702  //GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, dwPID);
703  fprintf(stderr, "%d terminated by CTRL_C_EVENT/CTRL_BREAK_EVENT\n", dwPID);
704  }
705 
706  GenerateConsoleCtrlEvent(CTRL_C_EVENT, dwPID);
707  GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, dwPID);
708 
709  CloseHandle(hProc);
710 
711  return true;
712 }
713 
714 #endif
bool ret
#define RUNLOG(msg)
int SIGNAL(int pid, int signum)
int CLOSE(int h)
#define POST()
#define WAIT()
int FDESC
void * HANDLE
pid_t PID
YarpRunInfoVector * mStdioVector
YarpRunCmdWithStdioInfo(std::string &alias, std::string &on, std::string &stdio, PID pidCmd, PID pidStdout, FDESC readFromPipeCmdToStdout, FDESC writeToPipeCmdToStdout, HANDLE handleCmd, bool hold)
int Signal(std::string &alias, int signum)
bool CleanZombie(int zombie)
static const int MAX_PROCESSES
int Killall(int signum)
yarp::os::Bottle PS()
YarpRunInfoVector * m_pStdioMate
YarpRunProcInfo * m_apList[MAX_PROCESSES]
bool IsRunning(std::string &alias)
bool Add(YarpRunProcInfo *process)
virtual void finalize()
std::string mAlias
std::string mOn
YarpRunProcInfo(std::string &alias, std::string &on, PID pidCmd, HANDLE handleCmd, bool hold)
virtual bool Signal(int signum)
virtual bool Clean()
virtual bool Clean(PID pid, YarpRunProcInfo *&pRef)
virtual bool IsActive()
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
Bottle & addList()
Places an empty nested list in the bottle, at the end of the list.
Definition: Bottle.cpp:182
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
void addInt32(std::int32_t x)
Places a 32-bit integer in the bottle, at the end of the list.
Definition: Bottle.cpp:140
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170