YARP
Yet Another Robot Platform
RunProcManager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * Copyright (C) 2006-2010 RobotCub Consortium
4  * All rights reserved.
5  *
6  * This software may be modified and distributed under the terms of the
7  * BSD-3-Clause license. See the accompanying LICENSE file for details.
8  */
9 
12 
13 #include <yarp/os/Network.h>
14 #include <yarp/os/RpcClient.h>
15 #include <yarp/os/Semaphore.h>
16 #include <yarp/os/Time.h>
17 
19 
20 #include <cstring>
21 
22 #define WAIT() { RUNLOG("<<<mutex.lock()") mutex.lock(); RUNLOG(">>>mutex.lock()") }
23 #define POST() { RUNLOG("<<<mutex.unlock()") mutex.unlock(); RUNLOG(">>>mutex.unlock()") }
24 
25 #if defined(_WIN32)
26  #include <process.h>
27 
28  #define SIGKILL 9
29  static bool KILL(HANDLE handle)
30  {
31  BOOL bRet=TerminateProcess(handle, 0);
32 
33  CloseHandle(handle);
34  fprintf(stderr, "brutally terminated by TerminateProcess\n");
35 
36  return bRet?true:false;
37  }
38  static bool TERMINATE(PID pid);
39  #define CLOSE(h) CloseHandle(h)
40  #ifndef __GNUC__
41  static DWORD WINAPI ZombieHunter(__in LPVOID lpParameter)
42  #else
43  static DWORD WINAPI ZombieHunter(LPVOID lpParameter)
44  #endif
45  {
46  YarpRunInfoVector* pProcessVector=(YarpRunInfoVector*)lpParameter;
47 
48  while (true)
49  {
50  DWORD nCount=0;
51  HANDLE* aHandlesVector = nullptr;
52  pProcessVector->GetHandles(aHandlesVector, nCount);
53 
54  if (nCount)
55  {
56  WaitForMultipleObjects(nCount, aHandlesVector, FALSE, INFINITE);
57  delete [] aHandlesVector;
58  }
59  else
60  {
61  //hZombieHunter = nullptr;
62 
63  return 0;
64  }
65  }
66 
67  return 0;
68  }
69 #else // LINUX
70  #include <unistd.h>
71  #include <fcntl.h>
72 
73  int CLOSE(int h)
74  {
75  int ret=(close(h)==0);
76  return ret;
77  }
78 
79  int SIGNAL(int pid, int signum)
80  {
81  int ret=!yarp::os::impl::kill(pid, signum);
82  return ret;
83  }
84 #endif // LINUX
85 
86 YarpRunProcInfo::YarpRunProcInfo(std::string& alias, std::string& on, PID pidCmd, HANDLE handleCmd, bool hold)
87 {
88  mAlias=alias;
89  mOn=on;
90  mPidCmd=pidCmd;
91  mCleanCmd=false;
92  mHandleCmd=handleCmd;
93  mHold=hold;
94 }
95 
96 bool YarpRunProcInfo::Signal(int signum)
97 {
98 #if defined(_WIN32)
99  if (signum==SIGKILL)
100  {
101  if (mHandleCmd)
102  {
103  bool ret=KILL(mHandleCmd);
104  return ret;
105  }
106  }
107  else
108  {
109  if (mPidCmd)
110  {
111  bool ret=TERMINATE(mPidCmd);
112  return ret;
113  }
114  }
115 #else
116  if (mPidCmd && !mHold)
117  {
118  bool ret=SIGNAL(mPidCmd, signum);
119  return ret;
120  }
121 #endif
122 
123  return true;
124 }
125 
127 {
128  if (!mPidCmd)
129  {
130  return false;
131  }
132 #if defined(_WIN32)
133  DWORD status;
134  RUNLOG("<<<GetExitCodeProcess(mHandleCmd, &status)")
135  bool ret=(::GetExitCodeProcess(mHandleCmd, &status) && status==STILL_ACTIVE);
136  RUNLOG(">>>GetExitCodeProcess(mHandleCmd, &status)")
137  return ret;
138 #else
139  bool ret=!yarp::os::impl::kill(mPidCmd, 0);
140  return ret;
141 #endif
142 }
143 
145 {
146 #if !defined(_WIN32)
147  if (!mCleanCmd && yarp::os::impl::waitpid(mPidCmd, nullptr, WNOHANG) == mPidCmd)
148  {
149  fprintf(stderr, "CLEANUP cmd %d\n", mPidCmd);
150  mCleanCmd=true;
151  }
152 
153  return mCleanCmd;
154 #else
155  return true;
156 #endif
157 }
158 
160 {
161  m_nProcesses=0;
162  m_pStdioMate = nullptr;
163  for (auto & i : m_apList)
164  {
165  i = nullptr;
166  }
167 }
168 
170 {
171  WAIT()
172 
173  for (auto & i : m_apList)
174  {
175  if (i)
176  {
177  delete i;
178  i = nullptr;
179  }
180  }
181 
182 #if defined(_WIN32)
183  if (hZombieHunter)
184  {
185  HANDLE hkill=hZombieHunter;
186  hZombieHunter = nullptr;
187  TerminateThread(hkill, 0);
188  }
189 #endif
190 
191  POST()
192 }
193 
195 {
196  WAIT()
197 
199  {
200  fprintf(stderr, "ERROR: maximum process limit reached\n");
201  POST()
202  return false;
203  }
204 
205 #if defined(_WIN32)
206  if (hZombieHunter)
207  {
208  HANDLE hkill=hZombieHunter;
209  hZombieHunter = nullptr;
210  TerminateThread(hkill, 0);
211  }
212 #endif
213 
214  m_apList[m_nProcesses++]=process;
215 
216 #if defined(_WIN32)
217  hZombieHunter=CreateThread(0, 0, ZombieHunter, this, 0, 0);
218 #endif
219 
220  POST()
221  return true;
222 }
223 
224 int YarpRunInfoVector::Signal(std::string& alias, int signum)
225 {
226  WAIT()
227 
228  auto* *aKill=new YarpRunProcInfo*[m_nProcesses];
229  int nKill=0;
230 
231  for (int i=0; i<m_nProcesses; ++i)
232  {
233  if (m_apList[i] && m_apList[i]->Match(alias)) // && m_apList[i]->IsActive())
234  {
235  aKill[nKill++]=m_apList[i];
236  }
237  }
238 
239  POST()
240 
241  for (int k=0; k<nKill; ++k)
242  {
243  fprintf(stderr, "SIGNAL %s (%d)\n", aKill[k]->mAlias.c_str(), aKill[k]->mPidCmd);
244  aKill[k]->Signal(signum);
245  }
246 
247  delete [] aKill;
248 
249  return nKill;
250 }
251 
253 {
254  WAIT()
255 
256  auto* *aKill=new YarpRunProcInfo*[m_nProcesses];
257  int nKill=0;
258 
259  for (int i=0; i<m_nProcesses; ++i)
260  {
261  if (m_apList[i] && m_apList[i]->IsActive())
262  {
263  aKill[nKill++]=m_apList[i];
264  }
265  }
266 
267  POST()
268 
269  for (int k=0; k<nKill; ++k)
270  {
271  fprintf(stderr, "SIGNAL %s (%d)\n", aKill[k]->mAlias.c_str(), aKill[k]->mPidCmd);
272  aKill[k]->Signal(signum);
273  }
274 
275  delete [] aKill;
276 
277  return nKill;
278 }
279 
280 #if defined(_WIN32)
281 void YarpRunInfoVector::GetHandles(HANDLE* &lpHandles, DWORD &nCount)
282 {
283  WAIT()
284 
285  if (lpHandles) delete [] lpHandles;
286 
287  if (m_nProcesses>0) lpHandles=new HANDLE[m_nProcesses];
288 
289  for (int i=0; i<m_nProcesses; ++i) if (m_apList[i])
290  {
291  if (!m_apList[i]->IsActive())
292  {
293  fprintf(stderr, "CLEANUP %s (%d)\n", m_apList[i]->mAlias.c_str(), m_apList[i]->mPidCmd);
294  fflush(stderr);
295 
296  m_apList[i]->Clean();
297  delete m_apList[i];
298  m_apList[i]=0;
299  }
300  }
301 
302  Pack();
303 
304  for (int i=0; i<m_nProcesses; ++i)
305  {
306  lpHandles[nCount+i]=m_apList[i]->mHandleCmd;
307  }
308 
309  nCount+=m_nProcesses;
310 
311  POST()
312 }
313 
314 #else
315 
317 {
318  bool bFound=false;
319 
320  YarpRunProcInfo *pZombie = nullptr;
321 
322  WAIT()
323 
324  for (int i=0; i<m_nProcesses; ++i)
325  {
326  if (m_apList[i] && m_apList[i]->Clean(zombie, pZombie))
327  {
328  bFound=true;
329  if (pZombie) m_apList[i] = nullptr;
330  break;
331  }
332  }
333 
334  Pack();
335 
336  POST()
337 
338  if (pZombie)
339  {
340  pZombie->finalize();
341  delete pZombie;
342  }
343 
344  return bFound;
345 }
346 #endif
347 
349 {
350  WAIT()
351 
352  yarp::os::Bottle ps, line, grp;
353 
354  for (int i=0; i<m_nProcesses; ++i) if (m_apList[i])
355  {
356  line.clear();
357 
358  grp.clear();
359  grp.addString("pid");
360  grp.addInt32(m_apList[i]->mPidCmd);
361  line.addList()=grp;
362 
363  grp.clear();
364  grp.addString("tag");
365  grp.addString(m_apList[i]->mAlias.c_str());
366  line.addList()=grp;
367 
368  grp.clear();
369  grp.addString("status");
370  grp.addString(m_apList[i]->IsActive()?"running":"zombie");
371  line.addList()=grp;
372 
373  grp.clear();
374  grp.addString("cmd");
375  grp.addString(m_apList[i]->mCmd.c_str());
376  line.addList()=grp;
377 
378  grp.clear();
379  grp.addString("env");
380  grp.addString(m_apList[i]->mEnv.c_str());
381  line.addList()=grp;
382 
383  ps.addList()=line;
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 
566 
571  }
572 
573  if (!mCleanCmd && !mKillingCmd)
574  {
575  yarp::os::impl::kill(mPidCmd, SIGTERM);
576  mKillingCmd=true;
577  }
578 
579  if (!mCleanStdin && !mKillingStdin)
580  {
581  yarp::os::impl::kill(mPidStdin, SIGTERM);
582  mKillingStdin=true;
583  }
584 
585  if (!mCleanStdout && !mKillingStdout)
586  {
587  yarp::os::impl::kill(mPidStdout, SIGTERM);
588  mKillingStdout=true;
589  }
590 
592  {
593  return true;
594  }
595 
596  return false;
597 #endif
598 }
599 
601 {
602  if (!mStdioVector) return;
603 
604  if (mOn==mStdio)
605  {
606  mStdioVector->Signal(mAlias, SIGTERM);
607  }
608  else
609  {
610  yarp::os::Bottle msg;
611  msg.fromString(std::string("(killstdio ")+mAlias+")");
612  yarp::run::Run::sendMsg(msg, mStdio);
613  }
614 }
615 
619 
620 #if defined(_WIN32)
621 
622 #define TA_FAILED 0
623 #define TA_SUCCESS_CLEAN 1
624 #define TA_SUCCESS_KILL 2
625 
626 class TerminateParams
627 {
628 public:
629  TerminateParams(DWORD id)
630  {
631  nWin=0;
632  dwID=id;
633  }
634 
635  ~TerminateParams(){}
636 
637  int nWin;
638  DWORD dwID;
639 };
640 
641 BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM lParam)
642 {
643  TerminateParams* params=(TerminateParams*)lParam;
644 
645  DWORD dwID;
646  GetWindowThreadProcessId(hwnd, &dwID) ;
647 
648  if (dwID==params->dwID)
649  {
650  params->nWin++;
651  PostMessage(hwnd, WM_CLOSE, 0, 0);
652  }
653 
654  return TRUE;
655 }
656 
657 /*----------------------------------------------------------------
658 Purpose:
659 Shut down a 32-Bit Process
660 
661 Parameters:
662 dwPID
663 Process ID of the process to shut down.
664 ----------------------------------------------------------------*/
665 bool TERMINATE(PID dwPID)
666 {
667  HANDLE hProc;
668 
669  // If we can't open the process with PROCESS_TERMINATE rights,
670  // then we give up immediately.
671  hProc=OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, dwPID);
672 
673  if (hProc == nullptr)
674  {
675  return false;
676  }
677 
678  // TerminateAppEnum() posts WM_CLOSE to all windows whose PID
679  // matches your process's.
680 
681  TerminateParams params(dwPID);
682 
683  EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)&params);
684 
685  if (params.nWin)
686  {
687  fprintf(stderr, "%d terminated by WM_CLOSE (sending anyway CTRL_C_EVENT/CTRL_BREAK_EVENT)\n", dwPID);
688  }
689  else
690  {
691  //GenerateConsoleCtrlEvent(CTRL_C_EVENT, dwPID);
692  //GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, dwPID);
693  fprintf(stderr, "%d terminated by CTRL_C_EVENT/CTRL_BREAK_EVENT\n", dwPID);
694  }
695 
696  GenerateConsoleCtrlEvent(CTRL_C_EVENT, dwPID);
697  GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, dwPID);
698 
699  CloseHandle(hProc);
700 
701  return true;
702 }
703 
704 #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:76
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:207
Bottle & addList()
Places an empty nested list in the bottle, at the end of the list.
Definition: Bottle.cpp:185
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:124
void addInt32(std::int32_t x)
Places a 32-bit integer in the bottle, at the end of the list.
Definition: Bottle.cpp:143
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:173