YARP
Yet Another Robot Platform
Run.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 
10 #include <yarp/run/Run.h>
17 
18 #include <yarp/conf/environment.h>
19 #include <yarp/conf/filesystem.h>
20 
21 #include <yarp/os/Network.h>
22 #include <yarp/os/Os.h>
23 #include <yarp/os/LogStream.h>
24 #include <yarp/os/RpcClient.h>
25 #include <yarp/os/RpcServer.h>
26 #include <yarp/os/Semaphore.h>
27 #include <yarp/os/SystemInfo.h>
29 #include <yarp/os/Time.h>
30 
34 
35 #include <cstdio>
36 #include <string>
37 #include <cstring>
38 #include <random>
39 
40 #if defined(_WIN32)
41 # if !defined(WIN32_LEAN_AND_MEAN)
42 # define WIN32_LEAN_AND_MEAN
43 # endif
44 # include <windows.h>
45 #else
46 # define C_MAXARGS 128 // the max number of command parameters. rational?
47 #endif
48 
49 #if defined(_WIN32)
50 YarpRunInfoVector yarp::run::Run::mProcessVector;
51 YarpRunInfoVector yarp::run::Run::mStdioVector;
52 inline std::string lastError2String()
53 {
54  int error=GetLastError();
55  char buff[1024];
56  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, 0, buff, 1024, nullptr);
57 
58  return std::string(buff);
59 }
60 #else
61 //#define SIGSTDIO SIGHUP
62 #define READ_FROM_PIPE 0
63 #define WRITE_TO_PIPE 1
64 #define REDIRECT_TO(from, to) yarp::run::impl::dup2(to, from)
65 YarpRunInfoVector* yarp::run::Run::mProcessVector = nullptr;
66 YarpRunInfoVector* yarp::run::Run::mStdioVector = nullptr;
67 ZombieHunterThread* yarp::run::Run::mBraveZombieHunter = nullptr;
68 #endif
69 
71 // OS INDEPENDENT FUNCTIONS
73 
74 std::string yarp::run::Run::mPortName;
75 yarp::os::RpcServer* yarp::run::Run::pServerPort=nullptr;
76 int yarp::run::Run::mProcCNT=0;
77 bool yarp::run::Run::mStresstest=false;
78 bool yarp::run::Run::mLogged=false;
79 std::string yarp::run::Run::mLoggerPort("/yarplogger");
80 
81 namespace fs = yarp::conf::filesystem;
82 
84 
85 static RunTerminator *pTerminator = nullptr;
86 
87 void sigstdio_handler(int sig)
88 {
89  char msg[16];
91  sprintf(msg, "SIGNAL %d", sig);
92  RUNLOG(msg);
93 
95 }
96 
98 
102 static yarp::os::Bottle parsePaths(const std::string& txt)
103 {
104  yarp::os::Bottle result;
105  const char *at = txt.c_str();
106  int slash_tweak = 0;
107  int len = 0;
108  for (char ch : txt) {
109  if (ch==sep) {
110  result.addString(std::string(at, len-slash_tweak));
111  at += len+1;
112  len = 0;
113  slash_tweak = 0;
114  continue;
115  }
116  slash_tweak = (ch==slash && len>0)?1:0;
117  len++;
118  }
119  if (len>0) {
120  result.addString(std::string(at, len-slash_tweak));
121  }
122  return result;
123 }
124 
125 static bool fileExists(const char *fname)
126 {
127  FILE *fp = nullptr;
128  fp = fopen(fname, "r");
129  if (!fp) {
130  return false;
131  } else {
132  fclose(fp);
133  return true;
134  }
135 }
136 
137 
139 int yarp::run::Run::main(int argc, char *argv[])
140 {
141  yarp::os::Property config;
142  config.fromCommand(argc, argv, false);
143 
144  // SERVER
145  if (config.check("server"))
146  {
147  mLogged=config.check("log");
148 
149  if (mLogged)
150  {
151  yarp::os::Bottle botPortLogger=config.findGroup("log");
152 
153  if (botPortLogger.size()>1)
154  {
155  mLoggerPort=botPortLogger.get(1).asString();
156  }
157  }
158 
159  mPortName=std::string(config.find("server").asString());
160  return server();
161  }
162 
163  mPortName="";
164 
166  {
168  {
169  fprintf(stderr, "ERROR: no yarp network found.\n");
170 
171  return YARPRUN_ERROR;
172  }
173  }
174 
175  // READWRITE
176  if (config.check("readwrite"))
177  {
178  std::string uuid=config.findGroup("readwrite").get(1).asString();
179  std::string fPortName;
180  std::string lPortName;
181 
182  if (config.check("forward"))
183  {
184  fPortName=config.findGroup("forward").get(1).asString();
185  lPortName=config.findGroup("forward").get(2).asString();
186  }
187 
188 #if defined(_WIN32)
189  yarp::os::impl::signal(SIGINT, sigstdio_handler);
190  yarp::os::impl::signal(SIGTERM, sigstdio_handler);
191  yarp::os::impl::signal(SIGBREAK, sigstdio_handler);
192 #elif defined(__APPLE__)
193  //prctl(PR_SET_PDEATHSIG, SIGTERM);
194 
195  struct sigaction new_action;
196  new_action.sa_handler=sigstdio_handler;
197  sigfillset(&new_action.sa_mask);
198  new_action.sa_flags=0;
199 
200  sigaction(SIGTERM, &new_action, nullptr);
201  sigaction(SIGHUP, &new_action, nullptr);
202  //yarp::os::impl::signal(SIGHUP, SIG_IGN);
203  //yarp::os::impl::signal(SIGINT, SIG_IGN);
204  yarp::os::impl::signal(SIGPIPE, SIG_IGN);
205 
206  if (getppid()==1) return 0;
207 #else
208  yarp::os::impl::prctl(PR_SET_PDEATHSIG, SIGTERM);
209 
210  struct sigaction new_action;
211  new_action.sa_handler=sigstdio_handler;
212  yarp::os::impl::sigfillset(&new_action.sa_mask);
213  new_action.sa_flags=0;
214 
215  yarp::os::impl::sigaction(SIGTERM, &new_action, nullptr);
216  yarp::os::impl::signal(SIGHUP, SIG_IGN);
217  //yarp::os::impl::signal(SIGINT, SIG_IGN);
218  yarp::os::impl::signal(SIGPIPE, SIG_IGN);
219 
220  if (yarp::os::getpid() == 1) {
221  return 0;
222  }
223 #endif
224 
225  RunReadWrite rw(uuid, fPortName, lPortName);
226  RunTerminator rt(&rw);
227  pTerminator=&rt;
228  rt.start();
229 
230  return rw.loop();
231  }
232 
233  // WRITE
234  if (config.check("write"))
235  {
236  std::string portName=config.findGroup("write").get(1).asString();
237 
238 #if defined(_WIN32)
239  yarp::os::impl::signal(SIGINT, sigstdio_handler);
240  yarp::os::impl::signal(SIGTERM, sigstdio_handler);
241  yarp::os::impl::signal(SIGBREAK, sigstdio_handler);
242 #else
243  struct sigaction new_action;
244  new_action.sa_handler=sigstdio_handler;
245  yarp::os::impl::sigfillset(&new_action.sa_mask);
246  new_action.sa_flags=0;
247  yarp::os::impl::sigaction(SIGTERM, &new_action, nullptr);
248  //yarp::os::impl::signal(SIGINT, SIG_IGN);
249  yarp::os::impl::signal(SIGPIPE, SIG_IGN);
250  yarp::os::impl::signal(SIGHUP, SIG_IGN);
251 #endif
252 
253  if (config.check("log"))
254  {
255  std::string loggerName=config.find("log").asString();
256  RunWrite w(portName, loggerName);
257  RunTerminator rt(&w);
258  pTerminator=&rt;
259  rt.start();
260  return w.loop();
261  }
262  else
263  {
264  RunWrite w(portName);
265  RunTerminator rt(&w);
266  pTerminator=&rt;
267  rt.start();
268  return w.loop();
269  }
270 
271  return 0;
272  }
273 
274  // READ
275  if (config.check("read"))
276  {
277  std::string uuid=config.findGroup("read").get(1).asString();
278 
279  #if defined(_WIN32)
280  yarp::os::impl::signal(SIGINT, sigstdio_handler);
281  yarp::os::impl::signal(SIGTERM, sigstdio_handler);
282  yarp::os::impl::signal(SIGBREAK, sigstdio_handler);
283  #else
284  //yarp::os::impl::signal(SIGINT, SIG_IGN);
285  yarp::os::impl::signal(SIGTERM, sigstdio_handler);
286  yarp::os::impl::signal(SIGHUP, SIG_IGN);
287  #endif
288 
289  RunRead r(uuid);
290  RunTerminator rt(&r);
291  pTerminator=&rt;
292  rt.start();
293 
294  return r.loop();
295  }
296 
297  // STRESSTEST
298  if (config.check("stresstest"))
299  {
300  fprintf(stderr, "Yarprun stress test started.\n");
301  fflush(stderr);
302 
303  int max_interval_ms=config.find("stresstest").asInt32();
304  std::string tag_zero=config.find("as").asString();
305  yarp::os::Bottle srv=config.findGroup("on");
306 
307  config.unput("as");
308  config.unput("stresstest");
309 
310  std::string cmd;
311 
312  bool isCommand=false;
313 
314  if (config.check("cmd"))
315  {
316  isCommand=true;
317  cmd=config.find("cmd").asString();
318  config.unput("cmd");
319  }
320 
321  unsigned int t=0, u=0;
322  int term_cycle=0;
323 
324  char tag[256];
325  char cmd_and_name[512];
326 
327  mStresstest=true;
328 
329  std::random_device rd;
330  std::mt19937 mt(rd());
331  std::uniform_int_distribution<int> dist0maxint(0, max_interval_ms -1);
332 
333  while (mStresstest)
334  {
335  yarp::os::SystemClock::delaySystem(0.001*(dist0maxint(mt)));
336 
337  yarp::os::Property stresser=config;
338 
339  sprintf(tag, "%s_%u", tag_zero.c_str(), t++);
340  stresser.put("as", tag);
341 
342  if (isCommand)
343  {
344  sprintf(cmd_and_name, "%s --name /%s", cmd.c_str(), tag);
345  stresser.put("cmd", cmd_and_name);
346  }
347 
348  client(stresser);
349 
350  std::uniform_int_distribution<int> dist07(0, 7);
351  if (isCommand && ++term_cycle>=4)
352  {
353  term_cycle=0;
354 
355  int r = t - (dist07(mt));
356 
357  for (int i=u; i<r; ++i)
358  {
359  sprintf(tag, "%s_%u", tag_zero.c_str(), i);
360 
361  yarp::os::Bottle as;
362  as.addString("sigterm");
363  as.addString(tag);
364 
365  yarp::os::Bottle term;
366  term.addList()=srv;
367  term.addList()=as;
368 
369  sendMsg(term, srv.get(1).asString());
370 
371  ++u;
372  }
373  }
374  }
375 
376  return 0;
377  }
378 
379  // HELP
380  if (config.check("help"))
381  {
382  Help();
383 
384  return 0;
385  }
386 
387  // CLIENT (config is from keyboard)
388  if (config.check("stdio")
389  || config.check("cmd")
390  || config.check("kill")
391  || config.check("sigterm")
392  || config.check("sigtermall")
393  || config.check("exit")
394  || config.check("isrunning")
395  || config.check("ps")
396  || config.check("env")
397  || config.check("sysinfo")
398  || config.check("which"))
399  {
400  int ret=client(config);
401 
402  return ret;
403  }
404 
405  Help();
406 
407  return 0;
408 }
409 
410 yarp::os::Bottle yarp::run::Run::sendMsg(yarp::os::Bottle& msg, std::string target, int RETRY, double DELAY)
411 {
412  yarp::os::Bottle response;
413 
414  for (int r=0; r<RETRY; ++r)
415  {
416  yarp::os::RpcClient port;
417 
418  if (!port.open("..."))
419  {
421  continue;
422  }
423 
424  if (!yarp::os::Network::connect(port.getName(), target))
425  {
426  port.close();
428  continue;
429  }
430 
431  RUNLOG("<<<port.write(msg, response)")
432  if (!port.write(msg, response))
433  {
434  port.close();
436  continue;
437  }
438  RUNLOG(">>>port.write(msg, response)")
439 
440  yarp::os::Network::disconnect(port.getName(), target);
441  port.close();
442 
443  fprintf(stderr, "RESPONSE:\n=========\n");
444  for (size_t s=0; s<response.size(); ++s)
445  {
446  fprintf(stderr, "%s\n", response.get(s).toString().c_str());
447  }
448 
449  return response;
450  }
451 
452  response.addString("RESPONSE:\n");
453  response.addString("=========\n");
454  response.addString("Cannot connect to remote server, aborting...\n");
455  for (size_t s=0; s<response.size(); ++s)
456  {
457  fprintf(stderr, "%s\n", response.get(s).toString().c_str());
458  }
459  return response;
460 }
461 
462 void sigint_handler(int sig)
463 {
464  YARP_UNUSED(sig);
465  yarp::run::Run::mStresstest=false;
466 
467  if (yarp::run::Run::pServerPort)
468  {
469  yarp::os::RpcServer *pClose=yarp::run::Run::pServerPort;
470  yarp::run::Run::pServerPort = nullptr;
471  pClose->close();
472  }
473  //else
474  //{
475  //}
476 }
477 
479 // WINDOWS SERVER
480 #if defined(_WIN32)
481 int yarp::run::Run::server()
482 {
483  yarp::os::Semaphore serializer(1);
484 
485  yarp::os::RpcServer port;
486 
487  if (!port.open(mPortName.c_str()))
488  {
489  yError() << "Yarprun failed to open port: " << mPortName.c_str();
490  return YARPRUN_ERROR;
491  }
492 
493  yarp::os::Bottle cmd, reply;
494  cmd.addString("set");
495  cmd.addString(port.getName());
496  cmd.addString("yarprun");
497  cmd.addString("true");
499 
500  yInfo() << "Yarprun successfully started on port: " << mPortName.c_str();
501 
502  pServerPort=&port;
503 
504  yarp::os::impl::signal(SIGINT, sigint_handler);
505  yarp::os::impl::signal(SIGTERM, sigint_handler);
506 
507  // Enabling cpu load collector on windows
508  //yarp::os::impl::SystemInfo::enableCpuLoadCollector();
509 
510  while (pServerPort)
511  {
512  yarp::os::Bottle msg;
513 
514  RUNLOG("<<<port.read(msg, true)")
515  if (!port.read(msg, true)) break;
516  RUNLOG(">>>port.read(msg, true)")
517 
518  if (!pServerPort) break;
519 
520  //printf("<<< %s >>>\n", msg.toString().c_str());
521  //fflush(stdout);
522 
524 
525  // command with stdio management
526  if (msg.check("stdio"))
527  {
528  std::string strOnPort=msg.find("on").asString();
529  std::string strStdioPort=msg.find("stdio").asString();
530 
531  if (strOnPort==mPortName)
532  {
533  std::string strUUID=mPortName+"/"+int2String(getpid())+"/"+msg.find("as").asString()+"-"+int2String(mProcCNT++);
534  yarp::os::Bottle botUUID;
535  botUUID.addString("stdiouuid");
536  botUUID.addString(strUUID.c_str());
537  msg.addList()=botUUID;
538 
539  if (mLogged || msg.check("log"))
540  {
541  std::string strAlias=msg.find("as").asString();
542  std::string portName="/log";
543  portName+=mPortName+"/";
544  std::string command = msg.findGroup("cmd").get(1).asString();
545  command = command.substr(0, command.find(' '));
546  command = command.substr(command.find_last_of("\\/") + 1);
547  portName+=command;
548 
549  yarp::os::Bottle botFwd;
550  botFwd.addString("forward");
551  botFwd.addString(portName.c_str());
552  if (msg.check("log"))
553  {
554  yarp::os::Bottle botLogger=msg.findGroup("log");
555 
556  if (botLogger.size()>1)
557  {
558  botFwd.addString(botLogger.get(1).asString());
559  }
560  else
561  {
562  botFwd.addString(mLoggerPort);
563  }
564  }
565  else
566  {
567  botFwd.addString(mLoggerPort);
568  }
569  msg.addList()=botFwd;
570  }
571 
572  yarp::os::Bottle cmdResult;
573  if (executeCmdAndStdio(msg, cmdResult)>0)
574  {
575  if (strStdioPort==mPortName)
576  {
577  yarp::os::Bottle stdioResult;
578  userStdio(msg, stdioResult);
579  cmdResult.append(stdioResult);
580  }
581  else
582  {
583  cmdResult.append(sendMsg(msg, strStdioPort));
584  }
585  }
586 
587  port.reply(cmdResult);
588  }
589  else
590  {
591  yarp::os::Bottle stdioResult;
592  userStdio(msg, stdioResult);
593  port.reply(stdioResult);
594  }
595 
596  continue;
597  }
598 
599  // without stdio
600  if (msg.check("cmd"))
601  {
602  yarp::os::Bottle cmdResult;
603 
604  if (msg.check("log"))
605  {
606  yarp::os::Bottle botLogger=msg.findGroup("log");
607 
608  if (botLogger.size()>1)
609  {
610  std::string loggerName=botLogger.get(1).asString();
611  executeCmdStdout(msg, cmdResult, loggerName);
612  }
613  else
614  {
615  executeCmdStdout(msg, cmdResult, mLoggerPort);
616  }
617  }
618  else if (mLogged)
619  {
620  executeCmdStdout(msg, cmdResult, mLoggerPort);
621  }
622  else
623  {
624  executeCmd(msg, cmdResult);
625  }
626  port.reply(cmdResult);
627  continue;
628  }
629 
630  if (msg.check("kill"))
631  {
632  std::string alias(msg.findGroup("kill").get(1).asString());
633  int sig=msg.findGroup("kill").get(2).asInt32();
634  yarp::os::Bottle result;
635  result.addString(mProcessVector.Signal(alias, sig)?"kill OK":"kill FAILED");
636  port.reply(result);
637  continue;
638  }
639 
640  if (msg.check("sigterm"))
641  {
642  std::string alias(msg.find("sigterm").asString());
643  yarp::os::Bottle result;
644  result.addString(mProcessVector.Signal(alias, SIGTERM)?"sigterm OK":"sigterm FAILED");
645  port.reply(result);
646  continue;
647  }
648 
649  if (msg.check("sigtermall"))
650  {
651  mProcessVector.Killall(SIGTERM);
652  yarp::os::Bottle result;
653  result.addString("sigtermall OK");
654  port.reply(result);
655  continue;
656  }
657 
658  if (msg.check("ps"))
659  {
660  yarp::os::Bottle result;
661  result.append(mProcessVector.PS());
662  port.reply(result);
663  continue;
664  }
665 
666  if (msg.check("isrunning"))
667  {
668  std::string alias(msg.find("isrunning").asString());
669  yarp::os::Bottle result;
670  result.addString(mProcessVector.IsRunning(alias)?"running":"not running");
671  port.reply(result);
672  continue;
673  }
674 
675  if (msg.check("killstdio"))
676  {
677  std::string alias(msg.find("killstdio").asString());
678  mStdioVector.Signal(alias, SIGTERM);
679  yarp::os::Bottle result;
680  result.addString("killstdio OK");
681  port.reply(result);
682  continue;
683  }
684 
686 
687  if (msg.check("sysinfo"))
688  {
690  port.reply(sysinfo);
691  continue;
692  }
693 
694  if (msg.check("which"))
695  {
696  std::string fileName=msg.find("which").asString();
697  if (fileName!="")
698  {
700  for (int i=0; i<possiblePaths.size(); ++i)
701  {
702  std::string guessString=possiblePaths.get(i).asString() +
703  std::string{slash} + fileName;
704  const char* guess=guessString.c_str();
705  if (fileExists (guess))
706  {
707  fileName= "\"" + std::string(guess) + "\"";
708  break;
709  }
710  }
711  }
712  yarp::os::Value fileNameWriter(fileName);
713  port.reply(fileNameWriter);
714  continue;
715  }
716 
717  if (msg.check("exit"))
718  {
719  pServerPort=0;
720  yarp::os::Bottle result;
721  result.addString("exit OK");
722  port.reply(result);
723  port.close();
724  }
725  }
726 
727 
728  Run::mStdioVector.Killall(SIGTERM);
729 
730  Run::mProcessVector.Killall(SIGTERM);
731 
732  return 0;
733 }
734 
736 #else // LINUX SERVER
738 
739 void yarp::run::Run::cleanBeforeExec()
740 {
741  // zombie hunter stop
742 
743  //yarp::os::impl::signal(SIGPIPE, SIG_IGN);
744  //yarp::os::impl::signal(SIGCHLD, SIG_DFL);
745  //yarp::os::impl::signal(SIGINT, SIG_DFL);
746  //yarp::os::impl::signal(SIGTERM, SIG_DFL);
747 
748  if (mProcessVector)
749  {
750  YarpRunInfoVector *p=mProcessVector;
751  mProcessVector = nullptr;
752  delete p;
753  }
754  if (mStdioVector)
755  {
756  YarpRunInfoVector *p=mStdioVector;
757  mStdioVector = nullptr;
758  delete p;
759  }
760  if (mBraveZombieHunter)
761  {
762  ZombieHunterThread *p=mBraveZombieHunter;
763  mBraveZombieHunter = nullptr;
764  p->stop();
765  delete p;
766  }
767 
768  //yarp::os::Network::fini();
769 }
770 
771 void yarp::run::Run::writeToPipe(int fd, std::string str)
772 {
773  int len = str.length() + 1;
774  int ret;
775  ret = write(fd, &len, sizeof(len));
776  if (ret != sizeof(len)) {
777  fprintf(stderr, "Warning: could not write string length to pipe.\n");
778  }
779  ret = write(fd, str.c_str(), len);
780  if (ret != len) {
781  fprintf(stderr, "Warning: could not write string to pipe.\n");
782  }
783 }
784 
785 int yarp::run::Run::readFromPipe(int fd, char* &data, int& buffsize)
786 {
787  int len=0;
788  char* buff=(char*)&len;
789 
790  for (int c=4, r=0; c>0; c-=r)
791  {
792  r=read(fd, buff, c);
793 
794  if (r<1) return -1;
795 
796  buff+=r;
797  }
798 
799  if (len<=0) return 0;
800 
801  if (len>buffsize)
802  {
803  delete [] data;
804  data=new char[buffsize=1024+(len/1024)*1024];
805  }
806 
807  buff=data;
808 
809  for (int c=len, r=0; c>0; c-=r)
810  {
811  r=read(fd, buff, c);
812 
813  if (r<1) return -1;
814 
815  buff+=r;
816  }
817 
818  return len;
819 }
820 
821 static void sigchld_handler(int sig)
822 {
823  YARP_UNUSED(sig);
824  if (yarp::run::Run::mBraveZombieHunter)
825  {
826  yarp::run::Run::mBraveZombieHunter->sigchldHandler();
827  }
828 }
829 
830 int yarp::run::Run::server()
831 {
832  int pipe_server2manager[2];
833  int pipe_manager2server[2];
834 
835  if (yarp::run::impl::pipe(pipe_server2manager))
836  {
837  fprintf(stderr, "Can't open pipe because %s\n", strerror(errno));
838  fflush(stderr);
839 
840  return YARPRUN_ERROR;
841  }
842 
843  if (yarp::run::impl::pipe(pipe_manager2server))
844  {
845  fprintf(stderr, "Can't open pipe because %s\n", strerror(errno));
846  fflush(stderr);
847 
848  return YARPRUN_ERROR;
849  }
850 
851  int pid_process_manager=yarp::run::impl::fork();
852 
853  if (IS_INVALID(pid_process_manager))
854  {
855  int error=errno;
856 
857  CLOSE(pipe_server2manager[WRITE_TO_PIPE]);
858  CLOSE(pipe_server2manager[READ_FROM_PIPE]);
859  CLOSE(pipe_manager2server[WRITE_TO_PIPE]);
860  CLOSE(pipe_manager2server[READ_FROM_PIPE]);
861 
862  fprintf(stderr, "Can't fork process manager because %s\n", strerror(error));
863  fflush(stderr);
864 
865  return YARPRUN_ERROR;
866  }
867 
868  if (IS_PARENT_OF(pid_process_manager))
869  {
870  yarp::os::impl::signal(SIGPIPE, SIG_IGN);
871 
872  CLOSE(pipe_server2manager[READ_FROM_PIPE]);
873  CLOSE(pipe_manager2server[WRITE_TO_PIPE]);
874 
875  yarp::os::RpcServer port;
876 
877  if (!port.open(mPortName))
878  {
879  yError() << "Yarprun failed to open port: " << mPortName.c_str();
880 
881  if (mPortName[0]!='/') yError("Invalid port name '%s', it should start with '/'\n", mPortName.c_str());
882  return YARPRUN_ERROR;
883  }
884  yarp::os::Bottle cmd, reply;
885  cmd.addString("set");
886  cmd.addString(port.getName());
887  cmd.addString("yarprun");
888  cmd.addString("true");
889 
891 
892  yInfo() << "Yarprun successfully started on port: " << mPortName.c_str();
893 
894  pServerPort=&port;
895 
896  yarp::os::impl::signal(SIGINT, sigint_handler);
897  yarp::os::impl::signal(SIGTERM, sigint_handler);
898 
899  int rsp_size=1024;
900  char *rsp_str=new char[rsp_size];
901 
902  yarp::os::Bottle msg, response;
903 
904  while (pServerPort)
905  {
906  RUNLOG("<<<port.read(msg, true)")
907  if (!port.read(msg, true)) break;
908  RUNLOG(">>>port.read(msg, true)")
909 
910  if (!pServerPort) break;
911 
912  if (msg.check("sysinfo"))
913  {
915  port.reply(sysinfo);
916  continue;
917  }
918 
919  if (msg.check("which"))
920  {
921  std::string fileName=msg.find("which").asString();
922  if (fileName!="")
923  {
925  for (size_t i=0; i<possiblePaths.size(); ++i)
926  {
927  std::string guessString=possiblePaths.get(i).asString() + slash + fileName;
928  const char* guess=guessString.c_str();
929  if (fileExists (guess))
930  {
931  fileName = guess;
932  break;
933  }
934  }
935  }
936  yarp::os::Value fileNameWriter(fileName);
937  port.reply(fileNameWriter);
938  continue;
939  }
940 
941  if (msg.check("exit"))
942  {
943  pServerPort = nullptr;
944  yarp::os::Bottle result;
945  result.addString("exit OK");
946  port.reply(result);
947  port.close();
948  break;
949  }
950 
951  RUNLOG("<<<writeToPipe")
952  writeToPipe(pipe_server2manager[WRITE_TO_PIPE], msg.toString());
953  RUNLOG(">>>writeToPipe")
954 
955  RUNLOG("<<<readFromPipe")
956  int nread=readFromPipe(pipe_manager2server[READ_FROM_PIPE], rsp_str, rsp_size);
957  RUNLOG(">>>readFromPipe")
958 
959  if (nread<0)
960  {
961  fprintf(stderr, "ERROR: broken pipe between server and manager\n");
962  fflush(stderr);
963  break;
964  }
965 
966  if (nread)
967  {
968  response.fromString(rsp_str);
969  port.reply(response);
970  }
971  }
972 
973  //yarp::os::Network::fini();
974 
975  CLOSE(pipe_server2manager[WRITE_TO_PIPE]);
976  CLOSE(pipe_manager2server[READ_FROM_PIPE]);
977 
978  delete [] rsp_str;
979 
980  return 0;
981  }
982 
983  if (IS_NEW_PROCESS(pid_process_manager))
984  {
985  yarp::os::impl::signal(SIGPIPE, SIG_IGN);
986 
987  CLOSE(pipe_server2manager[WRITE_TO_PIPE]);
988  CLOSE(pipe_manager2server[READ_FROM_PIPE]);
989 
990  //yarp::os::Network::init();
991 
992  mProcessVector=new YarpRunInfoVector;
993  mStdioVector=new YarpRunInfoVector;
994 
995  mBraveZombieHunter=new ZombieHunterThread;
996  mBraveZombieHunter->start();
997 
998  yarp::os::impl::signal(SIGCHLD, sigchld_handler);
999  //yarp::os::impl::signal(SIGINT, SIG_IGN);
1000  //yarp::os::impl::signal(SIGTERM, SIG_IGN);
1001 
1002  int msg_size=1024;
1003  char *msg_str=new char[msg_size];
1004 
1005  yarp::os::Bottle msg;
1006 
1007  //while(readFromPipe(pipe_server2manager[READ_FROM_PIPE], msg_str, msg_size)>0)
1008  while (true)
1009  {
1010  RUNLOG("<<<readFromPipe")
1011  if (readFromPipe(pipe_server2manager[READ_FROM_PIPE], msg_str, msg_size)<=0) break;
1012  RUNLOG(">>>readFromPipe")
1013 
1014  //printf("<<< %s >>>\n", msg_str);
1015  //fflush(stdout);
1016 
1017  msg.fromString(msg_str);
1018 
1019  // command with stdio management
1020  if (msg.check("stdio"))
1021  {
1022  std::string strOnPort=msg.find("on").asString();
1023  std::string strStdioPort=msg.find("stdio").asString();
1024 
1025  if (strOnPort==mPortName)
1026  {
1027  std::string strUUID=mPortName+"/"+int2String(getpid())+"/"+msg.find("as").asString()+"-"+int2String(mProcCNT++);
1028  yarp::os::Bottle botUUID;
1029  botUUID.addString("stdiouuid");
1030  botUUID.addString(strUUID.c_str());
1031  msg.addList()=botUUID;
1032 
1033  if (mLogged || msg.check("log"))
1034  {
1035  std::string strAlias=msg.find("as").asString();
1036  std::string portName="/log";
1037  portName+=mPortName+"/";
1038  std::string command = msg.findGroup("cmd").get(1).asString();
1039  command = command.substr(0, command.find(' '));
1040  command = command.substr(command.find_last_of("\\/") + 1);
1041  portName+=command;
1042 
1043  yarp::os::Bottle botFwd;
1044  botFwd.addString("forward");
1045  botFwd.addString(portName.c_str());
1046  if (msg.check("log"))
1047  {
1048  yarp::os::Bottle botLogger=msg.findGroup("log");
1049 
1050  if (botLogger.size()>1)
1051  {
1052  botFwd.addString(botLogger.get(1).asString());
1053  }
1054  else
1055  {
1056  botFwd.addString(mLoggerPort);
1057  }
1058  }
1059  else
1060  {
1061  botFwd.addString(mLoggerPort);
1062  }
1063  msg.addList()=botFwd;
1064 
1065  yarp::os::ContactStyle style;
1066  style.persistent=true;
1067  yarp::os::Network::connect(portName, mLoggerPort, style);
1068  }
1069 
1070  yarp::os::Bottle cmdResult;
1071  if (executeCmdAndStdio(msg, cmdResult)>0)
1072  {
1073  if (strStdioPort==mPortName)
1074  {
1075  yarp::os::Bottle stdioResult;
1076  userStdio(msg, stdioResult);
1077  cmdResult.append(stdioResult);
1078  }
1079  else
1080  {
1081  cmdResult.append(sendMsg(msg, strStdioPort));
1082  }
1083  }
1084 
1085  RUNLOG("<<<writeToPipe")
1086  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], cmdResult.toString());
1087  RUNLOG(">>>writeToPipe")
1088  }
1089  else
1090  {
1091  yarp::os::Bottle stdioResult;
1092  userStdio(msg, stdioResult);
1093  RUNLOG("<<<writeToPipe")
1094  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], stdioResult.toString());
1095  RUNLOG(">>>writeToPipe")
1096  }
1097 
1098  continue;
1099  }
1100 
1101  // without stdio
1102  if (msg.check("cmd"))
1103  {
1104  yarp::os::Bottle cmdResult;
1105 
1106  if (msg.check("log"))
1107  {
1108  yarp::os::Bottle botLogger=msg.findGroup("log");
1109 
1110  if (botLogger.size()>1)
1111  {
1112  std::string loggerName=botLogger.get(1).asString();
1113  executeCmdStdout(msg, cmdResult, loggerName);
1114  }
1115  else
1116  {
1117  executeCmdStdout(msg, cmdResult, mLoggerPort);
1118  }
1119  }
1120  else if (mLogged)
1121  {
1122  executeCmdStdout(msg, cmdResult, mLoggerPort);
1123  }
1124  else
1125  {
1126  executeCmd(msg, cmdResult);
1127  }
1128 
1129  RUNLOG("<<<writeToPipe")
1130  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], cmdResult.toString());
1131  RUNLOG(">>>writeToPipe")
1132  continue;
1133  }
1134 
1135  if (msg.check("kill"))
1136  {
1137  std::string alias(msg.findGroup("kill").get(1).asString());
1138  int sig=msg.findGroup("kill").get(2).asInt32();
1139  yarp::os::Bottle result;
1140  result.addString(mProcessVector->Signal(alias, sig)?"kill OK":"kill FAILED");
1141  RUNLOG("<<<writeToPipe")
1142  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], result.toString());
1143  RUNLOG(">>>writeToPipe")
1144  continue;
1145  }
1146 
1147  if (msg.check("sigterm"))
1148  {
1149  std::string alias(msg.find("sigterm").asString());
1150  yarp::os::Bottle result;
1151  result.addString(mProcessVector->Signal(alias, SIGTERM)?"sigterm OK":"sigterm FAILED");
1152  RUNLOG("<<<writeToPipe")
1153  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], result.toString());
1154  RUNLOG(">>>writeToPipe")
1155  continue;
1156  }
1157 
1158  if (msg.check("sigtermall"))
1159  {
1160  mProcessVector->Killall(SIGTERM);
1161  yarp::os::Bottle result;
1162  result.addString("sigtermall OK");
1163 
1164  RUNLOG("<<<writeToPipe")
1165  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], result.toString());
1166  RUNLOG(">>>writeToPipe")
1167  continue;
1168  }
1169 
1170  if (msg.check("ps"))
1171  {
1172  yarp::os::Bottle result;
1173  result.append(mProcessVector->PS());
1174  RUNLOG("<<<writeToPipe")
1175  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], result.toString());
1176  RUNLOG(">>>writeToPipe")
1177  continue;
1178  }
1179 
1180  if (msg.check("isrunning"))
1181  {
1182  std::string alias(msg.find("isrunning").asString());
1183  yarp::os::Bottle result;
1184  result.addString(mProcessVector->IsRunning(alias)?"running":"not running");
1185  RUNLOG("<<<writeToPipe")
1186  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], result.toString());
1187  RUNLOG(">>>writeToPipe")
1188  continue;
1189  }
1190 
1191  if (msg.check("killstdio"))
1192  {
1193  std::string alias(msg.find("killstdio").asString());
1194  mStdioVector->Signal(alias, SIGTERM);
1195  yarp::os::Bottle result;
1196  result.addString("killstdio OK");
1197  RUNLOG("<<<writeToPipe")
1198  writeToPipe(pipe_manager2server[WRITE_TO_PIPE], result.toString());
1199  RUNLOG(">>>writeToPipe")
1200  continue;
1201  }
1202  }
1203 
1204  mStdioVector->Killall(SIGTERM);
1205 
1206  mProcessVector->Killall(SIGTERM);
1207 
1208  if (mBraveZombieHunter)
1209  {
1210  mBraveZombieHunter->stop();
1211  delete mBraveZombieHunter;
1212  mBraveZombieHunter = nullptr;
1213  }
1214 
1215  delete mProcessVector;
1216 
1217  delete mStdioVector;
1218 
1219  //yarp::os::Network::fini();
1220 
1221  CLOSE(pipe_server2manager[READ_FROM_PIPE]);
1222  CLOSE(pipe_manager2server[WRITE_TO_PIPE]);
1223 
1224  delete [] msg_str;
1225  }
1226 
1227  return 0;
1228 } // LINUX SERVER
1229 #endif
1230 
1231 
1232 
1233 
1234 // CLIENT
1236 {
1237  // WITH STDIO
1238  //
1239  if (config.check("cmd") && config.check("stdio"))
1240  {
1242  // syntax check
1243  if (config.find("stdio").asString()=="")
1244  {
1245  Help("SYNTAX ERROR: missing remote stdio server\n");
1246  return YARPRUN_ERROR;
1247  }
1248  if (config.find("cmd").asString()=="")
1249  {
1250  Help("SYNTAX ERROR: missing command\n");
1251  return YARPRUN_ERROR;
1252  }
1253  if (!config.check("as") || config.find("as").asString()=="")
1254  {
1255  Help("SYNTAX ERROR: missing tag\n");
1256  return YARPRUN_ERROR;
1257  }
1258  if (!config.check("on") || config.find("on").asString()=="")
1259  {
1260  Help("SYNTAX ERROR: missing remote server\n");
1261  return YARPRUN_ERROR;
1262  }
1263  //
1265 
1266  printf("*********** %s ************\n", config.toString().c_str());
1267 
1268  yarp::os::Bottle msg;
1269  msg.addList()=config.findGroup("stdio");
1270  msg.addList()=config.findGroup("cmd");
1271  msg.addList()=config.findGroup("as");
1272  msg.addList()=config.findGroup("on");
1273 
1274  if (config.check("workdir")) msg.addList()=config.findGroup("workdir");
1275  if (config.check("geometry")) msg.addList()=config.findGroup("geometry");
1276  if (config.check("hold")) msg.addList()=config.findGroup("hold");
1277  if (config.check("env")) msg.addList()=config.findGroup("env");
1278  if (config.check("log")) msg.addList()=config.findGroup("log");
1279  /*
1280  {
1281  yarp::os::Bottle log;
1282  log.addString("log");
1283  log.addString("log");
1284  msg.addList()=log;
1285  }
1286  */
1287 
1288  std::string on=config.find("on").asString();
1289 
1290  yarp::os::Bottle response=sendMsg(msg, on);
1291 
1292  if (!response.size()) return YARPRUN_ERROR;
1293 
1294  if (response.get(0).asInt32()<=0) return 2;
1295 
1296  return 0;
1297  }
1298 
1299  // NO STDIO
1300  //
1301  if (config.check("cmd"))
1302  {
1304  // syntax check
1305  if (config.find("cmd").asString()=="")
1306  {
1307  Help("SYNTAX ERROR: missing command\n");
1308  return YARPRUN_ERROR;
1309  }
1310  if (!config.check("as") || config.find("as").asString()=="")
1311  {
1312  Help("SYNTAX ERROR: missing tag\n");
1313  return YARPRUN_ERROR;
1314  }
1315  if (!config.check("on") || config.find("on").asString()=="")
1316  {
1317  Help("SYNTAX ERROR: missing remote server\n");
1318  return YARPRUN_ERROR;
1319  }
1320  //
1322 
1323  yarp::os::Bottle msg;
1324  msg.addList()=config.findGroup("cmd");
1325  msg.addList()=config.findGroup("as");
1326 
1327  if (config.check("workdir")) msg.addList()=config.findGroup("workdir");
1328  if (config.check("log")) msg.addList()=config.findGroup("log");
1329  /*
1330  {
1331  yarp::os::Bottle log;
1332  log.addString("log");
1333  log.addString("log");
1334  msg.addList()=log;
1335  }
1336  */
1337  if (config.check("env")) msg.addList()=config.findGroup("env");
1338 
1339  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1340 
1341  if (!response.size()) return YARPRUN_ERROR;
1342 
1343  if (response.get(0).asInt32()<=0) return 2;
1344 
1345  return 0;
1346  }
1347 
1348 
1349 
1350 
1351 
1352  // client -> cmd server
1353  if (config.check("kill"))
1354  {
1355  if (!config.check("on") || config.find("on").asString()=="")
1356  {
1357  Help("SYNTAX ERROR: missing remote server\n");
1358  return YARPRUN_ERROR;
1359  }
1360  if (config.findGroup("kill").get(1).asString()=="")
1361  {
1362  Help("SYNTAX ERROR: missing tag\n");
1363  return YARPRUN_ERROR;
1364  }
1365  if (config.findGroup("kill").get(2).asInt32()==0)
1366  {
1367  Help("SYNTAX ERROR: missing signum\n");
1368  return YARPRUN_ERROR;
1369  }
1370 
1371  yarp::os::Bottle msg;
1372  msg.addList()=config.findGroup("kill");
1373 
1374  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1375 
1376  if (!response.size())
1377  {
1378  return YARPRUN_ERROR;
1379  }
1380 
1381  return response.get(0).asString()=="kill OK"?0:2;
1382  }
1383 
1384  // client -> cmd server
1385  if (config.check("sigterm"))
1386  {
1387  if (config.find("sigterm").asString()=="")
1388  {
1389  Help("SYNTAX ERROR: missing tag");
1390  return YARPRUN_ERROR;
1391  }
1392  if (!config.check("on") || config.find("on").asString()=="")
1393  {
1394  Help("SYNTAX ERROR: missing remote server\n");
1395  return YARPRUN_ERROR;
1396  }
1397 
1398  yarp::os::Bottle msg;
1399  msg.addList()=config.findGroup("sigterm");
1400 
1401  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1402 
1403  if (!response.size())
1404  {
1405  return YARPRUN_ERROR;
1406  }
1407 
1408  return response.get(0).asString()=="sigterm OK"?0:2;
1409  }
1410 
1411  // client -> cmd server
1412  if (config.check("sigtermall"))
1413  {
1414  if (!config.check("on") || config.find("on").asString()=="")
1415  {
1416  Help("SYNTAX ERROR: missing remote server\n");
1417  return YARPRUN_ERROR;
1418  }
1419 
1420  yarp::os::Bottle msg;
1421  msg.addList()=config.findGroup("sigtermall");
1422 
1423  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1424 
1425  if (!response.size())
1426  {
1427  return YARPRUN_ERROR;
1428  }
1429 
1430  return 0;
1431  }
1432 
1433  if (config.check("ps"))
1434  {
1435  if (!config.check("on") || config.find("on").asString()=="")
1436  {
1437  Help("SYNTAX ERROR: missing remote server\n");
1438  return YARPRUN_ERROR;
1439  }
1440 
1441  yarp::os::Bottle msg;
1442  msg.addList()=config.findGroup("ps");
1443 
1444  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1445 
1446  if (!response.size())
1447  {
1448  return YARPRUN_ERROR;
1449  }
1450 
1451  return 0;
1452  }
1453 
1454  if (config.check("isrunning"))
1455  {
1456  if (!config.check("on") || config.find("on").asString()=="")
1457  {
1458  Help("SYNTAX ERROR: missing remote server\n");
1459  return YARPRUN_ERROR;
1460  }
1461 
1462  if (config.find("isrunning").asString()=="")
1463  {
1464  Help("SYNTAX ERROR: missing tag\n");
1465  return YARPRUN_ERROR;
1466  }
1467 
1468  yarp::os::Bottle msg;
1469  msg.addList()=config.findGroup("isrunning");
1470 
1471  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1472 
1473  if (!response.size())
1474  {
1475  return YARPRUN_ERROR;
1476  }
1477 
1478  return response.get(0).asString()=="running"?0:2;
1479  }
1480 
1481  if (config.check("sysinfo"))
1482  {
1483  if (!config.check("on") || config.find("on").asString()=="")
1484  {
1485  Help("SYNTAX ERROR: missing remote server\n");
1486  return YARPRUN_ERROR;
1487  }
1488 
1489  yarp::os::Bottle msg;
1490  msg.addList()=config.findGroup("sysinfo");
1491 
1492  yarp::os::RpcClient port;
1493  //port.setTimeout(5.0);
1494  if (!port.open("..."))
1495  {
1496  fprintf(stderr, "RESPONSE:\n=========\n");
1497  fprintf(stderr, "Cannot open port, aborting...\n");
1498 
1499  return YARPRUN_ERROR;
1500  }
1501 
1502  bool connected = yarp::os::Network::connect(port.getName(), config.find("on").asString());
1503 
1504  if (!connected)
1505  {
1506  fprintf(stderr, "RESPONSE:\n=========\n");
1507  fprintf(stderr, "Cannot connect to remote server, aborting...\n");
1508  port.close();
1509  //yarp::os::Network::unregisterName(port.getName());
1510  return YARPRUN_ERROR;
1511  }
1512 
1514 
1515  RUNLOG("<<<port.write(msg, info)")
1516  int ret = port.write(msg, info);
1517  RUNLOG(">>>port.write(msg, info)")
1518  yarp::os::Network::disconnect(port.getName(), config.find("on").asString());
1519  port.close();
1520  //yarp::os::Network::unregisterName(port.getName());
1521  fprintf(stdout, "RESPONSE:\n=========\n\n");
1522 
1523  if (!ret)
1524  {
1525  fprintf(stdout, "No response. (timeout)\n");
1526 
1527  return YARPRUN_ERROR;
1528  }
1529 
1530  fprintf(stdout, "Platform name : %s\n", info.platform.name.c_str());
1531  fprintf(stdout, "Platform dist : %s\n", info.platform.distribution.c_str());
1532  fprintf(stdout, "Platform release : %s\n", info.platform.release.c_str());
1533  fprintf(stdout, "Platform code : %s\n", info.platform.codename.c_str());
1534  fprintf(stdout, "Platform kernel : %s\n\n", info.platform.kernel.c_str());
1535 
1536  fprintf(stdout, "User Id : %d\n", info.user.userID);
1537  fprintf(stdout, "User name : %s\n", info.user.userName.c_str());
1538  fprintf(stdout, "User real name : %s\n", info.user.realName.c_str());
1539  fprintf(stdout, "User home dir : %s\n\n", info.user.homeDir.c_str());
1540 
1541  fprintf(stdout, "Cpu load Ins.: %d\n", info.load.cpuLoadInstant);
1542  fprintf(stdout, "Cpu load 1 : %.2lf\n", info.load.cpuLoad1);
1543  fprintf(stdout, "Cpu load 5 : %.2lf\n", info.load.cpuLoad5);
1544  fprintf(stdout, "Cpu load 15 : %.2lf\n\n", info.load.cpuLoad15);
1545 
1546  fprintf(stdout, "Memory total : %dM\n", info.memory.totalSpace);
1547  fprintf(stdout, "Memory free : %dM\n\n", info.memory.freeSpace);
1548 
1549  fprintf(stdout, "Storage total : %dM\n", info.storage.totalSpace);
1550  fprintf(stdout, "Storage free : %dM\n\n", info.storage.freeSpace);
1551 
1552  fprintf(stdout, "Processor model : %s\n", info.processor.model.c_str());
1553  fprintf(stdout, "Processor model num : %d\n", info.processor.modelNumber);
1554  fprintf(stdout, "Processor family : %d\n", info.processor.family);
1555  fprintf(stdout, "Processor vendor : %s\n", info.processor.vendor.c_str());
1556  fprintf(stdout, "Processor arch : %s\n", info.processor.architecture.c_str());
1557  fprintf(stdout, "Processor cores : %d\n", info.processor.cores);
1558  fprintf(stdout, "Processor siblings : %d\n", info.processor.siblings);
1559  fprintf(stdout, "Processor Mhz : %.2lf\n\n", info.processor.frequency);
1560 
1561  fprintf(stdout, "Environment variables :\n%s\n", info.platform.environmentVars.toString().c_str());
1562  //fprintf(stdout, "Network IP4 : %s\n", info.network.ip4.c_str());
1563  //fprintf(stdout, "Network IP6 : %s\n", info.network.ip6.c_str());
1564  //fprintf(stdout, "Network mac : %s\n\n", info.network.mac.c_str());
1565 
1566  return 0;
1567  }
1568 
1569  if (config.check("which"))
1570  {
1571  if (!config.check("on") || config.find("on").asString()=="")
1572  {
1573  Help("SYNTAX ERROR: missing remote server\n");
1574 
1575  return YARPRUN_ERROR;
1576  }
1577 
1578  yarp::os::Bottle msg;
1579  msg.addList()=config.findGroup("which");
1580 
1581  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1582 
1583  if (!response.size())
1584  {
1585  return YARPRUN_ERROR;
1586  }
1587  return 0;
1588  }
1589 
1590  if (config.check("exit"))
1591  {
1592  if (!config.check("on") || config.find("on").asString()=="")
1593  {
1594  Help("SYNTAX ERROR: missing remote server\n");
1595 
1596  return YARPRUN_ERROR;
1597  }
1598 
1599  yarp::os::Bottle msg;
1600  msg.addList()=config.findGroup("exit");
1601 
1602  yarp::os::Bottle response=sendMsg(msg, config.find("on").asString());
1603 
1604  if (!response.size())
1605  {
1606  return YARPRUN_ERROR;
1607  }
1608 
1609  return 0;
1610  }
1611 
1612  return 0;
1613 }
1614 
1615 void yarp::run::Run::Help(const char *msg)
1616 {
1617  fprintf(stderr, "%s", msg);
1618  fprintf(stderr, "\nUSAGE:\n\n");
1619  fprintf(stderr, "yarp run --server SERVERPORT\nrun a server on the local machine\n\n");
1620  fprintf(stderr, "yarp run --on SERVERPORT --as TAG --cmd COMMAND [ARGLIST] [--workdir WORKDIR] [--env ENVIRONMENT]\nrun a command on SERVERPORT server\n\n");
1621  fprintf(stderr, "yarp run --on SERVERPORT --as TAG --stdio STDIOSERVERPORT [--hold] [--geometry WxH+X+Y] --cmd COMMAND [ARGLIST] [--workdir WORKDIR] [--env ENVIRONMENT]\n");
1622  fprintf(stderr, "run a command on SERVERPORT server sending I/O to STDIOSERVERPORT server\n\n");
1623  fprintf(stderr, "yarp run --on SERVERPORT --kill TAG SIGNUM\nsend SIGNUM signal to TAG command\n\n");
1624  fprintf(stderr, "yarp run --on SERVERPORT --sigterm TAG\nterminate TAG command\n\n");
1625  fprintf(stderr, "yarp run --on SERVERPORT --sigtermall\nterminate all commands\n\n");
1626  fprintf(stderr, "yarp run --on SERVERPORT --ps\nreport commands running on SERVERPORT\n\n");
1627  fprintf(stderr, "yarp run --on SERVERPORT --isrunning TAG\nTAG command is running?\n\n");
1628  fprintf(stderr, "yarp run --on SERVERPORT --sysinfo\nreport system information of SERVERPORT\n\n");
1629  fprintf(stderr, "yarp run --on SERVERPORT --exit\nstop SERVERPORT server\n\n");
1630 }
1631 
1633 // OS DEPENDENT FUNCTIONS
1635 
1636 // WINDOWS
1637 
1638 #if defined(_WIN32)
1639 
1640 // CMD SERVER
1641 int yarp::run::Run::executeCmdAndStdio(yarp::os::Bottle& msg, yarp::os::Bottle& result)
1642 {
1643  std::string strAlias=msg.find("as").asString();
1644  std::string strStdio=msg.find("stdio").asString();
1645  std::string strStdioUUID=msg.find("stdiouuid").asString();
1646  //std::string strCmdUUID=mPortName+"/"+int2String(GetCurrentProcessId())+"/"+strAlias+"-"+int2String(mProcCNT++);
1647 
1648  // PIPES
1649  SECURITY_ATTRIBUTES pipe_sec_attr;
1650  pipe_sec_attr.nLength=sizeof(SECURITY_ATTRIBUTES);
1651  pipe_sec_attr.bInheritHandle=TRUE;
1652  pipe_sec_attr.lpSecurityDescriptor = nullptr;
1653  HANDLE read_from_pipe_stdin_to_cmd, write_to_pipe_stdin_to_cmd;
1654  CreatePipe(&read_from_pipe_stdin_to_cmd, &write_to_pipe_stdin_to_cmd, &pipe_sec_attr, 0);
1655  HANDLE read_from_pipe_cmd_to_stdout, write_to_pipe_cmd_to_stdout;
1656  CreatePipe(&read_from_pipe_cmd_to_stdout, &write_to_pipe_cmd_to_stdout, &pipe_sec_attr, 0);
1657 
1658  // RUN STDOUT
1659  PROCESS_INFORMATION stdout_process_info;
1660  ZeroMemory(&stdout_process_info, sizeof(PROCESS_INFORMATION));
1661  STARTUPINFO stdout_startup_info;
1662  ZeroMemory(&stdout_startup_info, sizeof(STARTUPINFO));
1663 
1664  stdout_startup_info.cb=sizeof(STARTUPINFO);
1665  stdout_startup_info.hStdError=GetStdHandle(STD_ERROR_HANDLE);
1666  stdout_startup_info.hStdOutput=GetStdHandle(STD_OUTPUT_HANDLE);
1667  stdout_startup_info.hStdInput=read_from_pipe_cmd_to_stdout;
1668  stdout_startup_info.dwFlags|=STARTF_USESTDHANDLES;
1669 
1670  BOOL bSuccess=CreateProcess(nullptr, // command name
1671  (char*)(std::string("yarprun --write ")+strStdioUUID).c_str(), // command line
1672  nullptr, // process security attributes
1673  nullptr, // primary thread security attributes
1674  TRUE, // handles are inherited
1675  CREATE_NEW_PROCESS_GROUP, // creation flags
1676  nullptr, // use parent's environment
1677  nullptr, // use parent's current directory
1678  &stdout_startup_info, // STARTUPINFO pointer
1679  &stdout_process_info); // receives PROCESS_INFORMATION
1680 
1681  if (!bSuccess)
1682  {
1683  std::string strError=std::string("ABORTED: server=")+mPortName
1684  +std::string(" alias=")+strAlias
1685  +std::string(" cmd=stdout\n")
1686  +std::string("Can't execute stdout because ")+lastError2String()
1687  +std::string("\n");
1688 
1689  result.addInt32(YARPRUN_ERROR);
1690  result.addString(strError.c_str());
1691  fprintf(stderr, "%s", strError.c_str());
1692  fflush(stderr);
1693 
1694  CloseHandle(write_to_pipe_stdin_to_cmd);
1695  CloseHandle(read_from_pipe_stdin_to_cmd);
1696  CloseHandle(write_to_pipe_cmd_to_stdout);
1697  CloseHandle(read_from_pipe_cmd_to_stdout);
1698 
1699  return YARPRUN_ERROR;
1700  }
1701 
1702  // RUN STDIN
1703 
1704  PROCESS_INFORMATION stdin_process_info;
1705  ZeroMemory(&stdin_process_info, sizeof(PROCESS_INFORMATION));
1706  STARTUPINFO stdin_startup_info;
1707  ZeroMemory(&stdin_startup_info, sizeof(STARTUPINFO));
1708 
1709  stdin_startup_info.cb=sizeof(STARTUPINFO);
1710  stdin_startup_info.hStdError=write_to_pipe_stdin_to_cmd;
1711  stdin_startup_info.hStdOutput=write_to_pipe_stdin_to_cmd;
1712  stdin_startup_info.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
1713  stdin_startup_info.dwFlags|=STARTF_USESTDHANDLES;
1714 
1715  bSuccess=CreateProcess(nullptr, // command name
1716  (char*)(std::string("yarprun --read ")+strStdioUUID).c_str(), // command line
1717  nullptr, // process security attributes
1718  nullptr, // primary thread security attributes
1719  TRUE, // handles are inherited
1720  CREATE_NEW_PROCESS_GROUP, // creation flags
1721  nullptr, // use parent's environment
1722  nullptr, // use parent's current directory
1723  &stdin_startup_info, // STARTUPINFO pointer
1724  &stdin_process_info); // receives PROCESS_INFORMATION
1725 
1726  if (!bSuccess)
1727  {
1728  std::string strError=std::string("ABORTED: server=")+mPortName
1729  +std::string(" alias=")+strAlias
1730  +std::string(" cmd=stdin\n")
1731  +std::string("Can't execute stdin because ")+lastError2String()
1732  +std::string("\n");
1733 
1734  result.addInt32(YARPRUN_ERROR);
1735  result.addString(strError.c_str());
1736  fprintf(stderr, "%s", strError.c_str());
1737 
1738  TerminateProcess(stdout_process_info.hProcess, YARPRUN_ERROR);
1739 
1740  CloseHandle(stdout_process_info.hProcess);
1741 
1742  CloseHandle(write_to_pipe_stdin_to_cmd);
1743  CloseHandle(read_from_pipe_stdin_to_cmd);
1744  CloseHandle(write_to_pipe_cmd_to_stdout);
1745  CloseHandle(read_from_pipe_cmd_to_stdout);
1746 
1747  return YARPRUN_ERROR;
1748  }
1749 
1750  // RUN COMMAND
1751 
1752  PROCESS_INFORMATION cmd_process_info;
1753  ZeroMemory(&cmd_process_info, sizeof(PROCESS_INFORMATION));
1754  STARTUPINFO cmd_startup_info;
1755  ZeroMemory(&cmd_startup_info, sizeof(STARTUPINFO));
1756 
1757  cmd_startup_info.cb=sizeof(STARTUPINFO);
1758  cmd_startup_info.hStdError=write_to_pipe_cmd_to_stdout;
1759  cmd_startup_info.hStdOutput=write_to_pipe_cmd_to_stdout;
1760  cmd_startup_info.hStdInput=read_from_pipe_stdin_to_cmd;
1761  cmd_startup_info.dwFlags|=STARTF_USESTDHANDLES;
1762 
1763  yarp::os::Bottle botCmd=msg.findGroup("cmd").tail();
1764 
1765  std::string strCmd;
1766  for (int s=0; s<botCmd.size(); ++s)
1767  {
1768  strCmd+=botCmd.get(s).toString()+std::string(" ");
1769  }
1770 
1771  /*
1772  * setting environment variable for child process
1773  */
1774  TCHAR chNewEnv[32767];
1775 
1776  // Get a pointer to the env block.
1777  LPTCH chOldEnv = GetEnvironmentStrings();
1778 
1779  // copying parent env variables
1780  LPTSTR lpOld = (LPTSTR) chOldEnv;
1781  LPTSTR lpNew = (LPTSTR) chNewEnv;
1782  while (*lpOld)
1783  {
1784  lstrcpy(lpNew, lpOld);
1785  lpOld += lstrlen(lpOld) + 1;
1786  lpNew += lstrlen(lpNew) + 1;
1787  }
1788 
1789  // Set the YARP_IS_YARPRUN environment variable to 1, so that the child
1790  // process will now that is running inside yarprun.
1791  lstrcpy(lpNew, (LPTCH) "YARP_IS_YARPRUN=1");
1792  lpNew += lstrlen(lpNew) + 1;
1793 
1794  // Set the YARPRUN_IS_FORWARDING_LOG environment variable to 1, so that
1795  // the child process will now that yarprun is not logging the output.
1796  lstrcpy(lpNew, (LPTCH) "YARPRUN_IS_FORWARDING_LOG=1");
1797  lpNew += lstrlen(lpNew) + 1;
1798 
1799  // adding new env variables
1800  std::string cstrEnvName;
1801  if (msg.check("env"))
1802  {
1803  auto ss = yarp::conf::string::split(msg.find("env").asString(), ';');
1804  for (const auto& s : ss) {
1805  lstrcpy(lpNew, (LPTCH) s.c_str());
1806  lpNew += lstrlen(lpNew) + 1;
1807  }
1808  }
1809 
1810  // closing env block
1811  *lpNew = (TCHAR)0;
1812 
1813  bool bWorkdir=msg.check("workdir");
1814  std::string strWorkdir=bWorkdir?msg.find("workdir").asString()+"\\":"";
1815 
1816  bSuccess=CreateProcess(nullptr, // command name
1817  (char*)(strWorkdir+strCmd).c_str(), // command line
1818  nullptr, // process security attributes
1819  nullptr, // primary thread security attributes
1820  TRUE, // handles are inherited
1821  CREATE_NEW_PROCESS_GROUP, // creation flags
1822  (LPVOID) chNewEnv, // use new environment list
1823  bWorkdir ? strWorkdir.c_str() : nullptr, // working directory
1824  &cmd_startup_info, // STARTUPINFO pointer
1825  &cmd_process_info); // receives PROCESS_INFORMATION
1826 
1827  if (!bSuccess && bWorkdir)
1828  {
1829  bSuccess=CreateProcess(nullptr, // command name
1830  (char*)(strCmd.c_str()), // command line
1831  nullptr, // process security attributes
1832  nullptr, // primary thread security attributes
1833  TRUE, // handles are inherited
1834  CREATE_NEW_PROCESS_GROUP, // creation flags
1835  (LPVOID) chNewEnv, // use new environment list
1836  strWorkdir.c_str(), // working directory
1837  &cmd_startup_info, // STARTUPINFO pointer
1838  &cmd_process_info); // receives PROCESS_INFORMATION
1839  }
1840 
1841  // deleting old environment variable
1842  FreeEnvironmentStrings(chOldEnv);
1843 
1844  if (!bSuccess)
1845  {
1846  result.addInt32(YARPRUN_ERROR);
1847 
1848  DWORD nBytes;
1849  std::string line1=std::string("ABORTED: server=")+mPortName
1850  +std::string(" alias=")+strAlias
1851  +std::string(" cmd=")+strCmd
1852  +std::string("pid=")+int2String(cmd_process_info.dwProcessId)
1853  +std::string("\n");
1854 
1855  WriteFile(write_to_pipe_cmd_to_stdout, line1.c_str(), line1.length(), &nBytes, 0);
1856 
1857  std::string line2=std::string("Can't execute command because ")+lastError2String()+std::string("\n");
1858  WriteFile(write_to_pipe_cmd_to_stdout, line1.c_str(), line2.length(), &nBytes, 0);
1859  FlushFileBuffers(write_to_pipe_cmd_to_stdout);
1860 
1861  std::string out=line1+line2;
1862  result.addString(out.c_str());
1863  fprintf(stderr, "%s", out.c_str());
1864  fflush(stderr);
1865 
1866  CloseHandle(write_to_pipe_stdin_to_cmd);
1867  CloseHandle(read_from_pipe_stdin_to_cmd);
1868  CloseHandle(write_to_pipe_cmd_to_stdout);
1869  CloseHandle(read_from_pipe_cmd_to_stdout);
1870 
1871  TerminateProcess(stdout_process_info.hProcess, YARPRUN_ERROR);
1872 
1873  CloseHandle(stdout_process_info.hProcess);
1874 
1875  TerminateProcess(stdin_process_info.hProcess, YARPRUN_ERROR);
1876 
1877  CloseHandle(stdin_process_info.hProcess);
1878 
1879  return YARPRUN_ERROR;
1880  }
1881 
1882  FlushFileBuffers(write_to_pipe_cmd_to_stdout);
1883 
1884  // EVERYTHING IS ALL RIGHT
1885  YarpRunCmdWithStdioInfo* pInf = new YarpRunCmdWithStdioInfo(strAlias,
1886  mPortName,
1887  strStdio,
1888  cmd_process_info.dwProcessId,
1889  strStdioUUID,
1890  &mStdioVector,
1891  stdin_process_info.dwProcessId,
1892  stdout_process_info.dwProcessId,
1893  read_from_pipe_stdin_to_cmd,
1894  write_to_pipe_stdin_to_cmd,
1895  read_from_pipe_cmd_to_stdout,
1896  write_to_pipe_cmd_to_stdout,
1897  cmd_process_info.hProcess,
1898  false);
1899 
1900  pInf->setCmd(strCmd);
1901  if (msg.check("env"))
1902  {
1903  pInf->setEnv(msg.find("env").asString());
1904  }
1905  mProcessVector.Add(pInf);
1906 
1907  result.addInt32(cmd_process_info.dwProcessId);
1908  std::string out=std::string("STARTED: server=")+mPortName
1909  +std::string(" alias=")+strAlias
1910  +std::string(" cmd=")+strCmd
1911  +std::string(" pid=")+int2String(cmd_process_info.dwProcessId)
1912  +std::string("\n");
1913 
1914  result.addString(out.c_str());
1915  result.addString(strStdioUUID.c_str());
1916  fprintf(stderr, "%s", out.c_str());
1917 
1918  return cmd_process_info.dwProcessId;
1919 }
1920 
1921 int yarp::run::Run::executeCmdStdout(yarp::os::Bottle& msg, yarp::os::Bottle& result, std::string& loggerName)
1922 {
1923  std::string strAlias=msg.find("as").asString();
1924  std::string portName="/log";
1925  portName+=mPortName+"/";
1926  std::string command = msg.findGroup("cmd").get(1).asString();
1927  command = command.substr(0, command.find(' '));
1928  command = command.substr(command.find_last_of("\\/") + 1);
1929  portName+=command;
1930 
1931  // PIPES
1932  SECURITY_ATTRIBUTES pipe_sec_attr;
1933  pipe_sec_attr.nLength=sizeof(SECURITY_ATTRIBUTES);
1934  pipe_sec_attr.bInheritHandle=TRUE;
1935  pipe_sec_attr.lpSecurityDescriptor = nullptr;
1936  HANDLE read_from_pipe_cmd_to_stdout, write_to_pipe_cmd_to_stdout;
1937  CreatePipe(&read_from_pipe_cmd_to_stdout, &write_to_pipe_cmd_to_stdout, &pipe_sec_attr, 0);
1938 
1939  // RUN STDOUT
1940  PROCESS_INFORMATION stdout_process_info;
1941  ZeroMemory(&stdout_process_info, sizeof(PROCESS_INFORMATION));
1942  STARTUPINFO stdout_startup_info;
1943  ZeroMemory(&stdout_startup_info, sizeof(STARTUPINFO));
1944 
1945  stdout_startup_info.cb=sizeof(STARTUPINFO);
1946  stdout_startup_info.hStdError=GetStdHandle(STD_ERROR_HANDLE);
1947  stdout_startup_info.hStdOutput=GetStdHandle(STD_OUTPUT_HANDLE);
1948  stdout_startup_info.hStdInput=read_from_pipe_cmd_to_stdout;
1949  stdout_startup_info.dwFlags|=STARTF_USESTDHANDLES;
1950 
1951  BOOL bSuccess=CreateProcess(nullptr, // command name
1952  (char*)(std::string("yarprun --log ")+loggerName+std::string(" --write ")+portName).c_str(), // command line
1953  nullptr, // process security attributes
1954  nullptr, // primary thread security attributes
1955  TRUE, // handles are inherited
1956  CREATE_NEW_PROCESS_GROUP, // creation flags
1957  nullptr, // use parent's environment
1958  nullptr, // use parent's current directory
1959  &stdout_startup_info, // STARTUPINFO pointer
1960  &stdout_process_info); // receives PROCESS_INFORMATION
1961 
1962  if (!bSuccess)
1963  {
1964  std::string strError=std::string("ABORTED: server=")+mPortName
1965  +std::string(" alias=")+strAlias
1966  +std::string(" cmd=stdout\n")
1967  +std::string("Can't execute stdout because ")+lastError2String()
1968  +std::string("\n");
1969 
1970  result.addInt32(YARPRUN_ERROR);
1971  result.addString(strError.c_str());
1972  fprintf(stderr, "%s", strError.c_str());
1973  fflush(stderr);
1974 
1975  CloseHandle(write_to_pipe_cmd_to_stdout);
1976  CloseHandle(read_from_pipe_cmd_to_stdout);
1977 
1978  return YARPRUN_ERROR;
1979  }
1980 
1981  // RUN COMMAND
1982 
1983  PROCESS_INFORMATION cmd_process_info;
1984  ZeroMemory(&cmd_process_info, sizeof(PROCESS_INFORMATION));
1985  STARTUPINFO cmd_startup_info;
1986  ZeroMemory(&cmd_startup_info, sizeof(STARTUPINFO));
1987 
1988  cmd_startup_info.cb=sizeof(STARTUPINFO);
1989  cmd_startup_info.hStdError=write_to_pipe_cmd_to_stdout;
1990  cmd_startup_info.hStdOutput=write_to_pipe_cmd_to_stdout;
1991  cmd_startup_info.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
1992  cmd_startup_info.dwFlags|=STARTF_USESTDHANDLES;
1993 
1994  yarp::os::Bottle botCmd=msg.findGroup("cmd").tail();
1995 
1996  std::string strCmd;
1997  for (int s=0; s<botCmd.size(); ++s)
1998  {
1999  strCmd+=botCmd.get(s).toString()+std::string(" ");
2000  }
2001 
2002  /*
2003  * setting environment variable for child process
2004  */
2005  TCHAR chNewEnv[32767];
2006 
2007  // Get a pointer to the env block.
2008  LPTCH chOldEnv = GetEnvironmentStrings();
2009 
2010  // copying parent env variables
2011  LPTSTR lpOld = (LPTSTR) chOldEnv;
2012  LPTSTR lpNew = (LPTSTR) chNewEnv;
2013  while (*lpOld)
2014  {
2015  lstrcpy(lpNew, lpOld);
2016  lpOld += lstrlen(lpOld) + 1;
2017  lpNew += lstrlen(lpNew) + 1;
2018  }
2019 
2020  // Set the YARP_IS_YARPRUN environment variable to 1, so that the child
2021  // process will now that is running inside yarprun.
2022  lstrcpy(lpNew, (LPTCH) "YARP_IS_YARPRUN=1");
2023  lpNew += lstrlen(lpNew) + 1;
2024 
2025  // Set the YARPRUN_IS_FORWARDING_LOG environment variable to 1, so that
2026  // the child process will now that yarprun is not logging the output.
2027  lstrcpy(lpNew, (LPTCH) "YARPRUN_IS_FORWARDING_LOG=1");
2028  lpNew += lstrlen(lpNew) + 1;
2029 
2030  // adding new env variables
2031  std::string cstrEnvName;
2032  if (msg.check("env"))
2033  {
2034  lstrcpy(lpNew, (LPTCH) msg.find("env").asString().c_str());
2035  lpNew += lstrlen(lpNew) + 1;
2036  }
2037 
2038  // closing env block
2039  *lpNew = (TCHAR)0;
2040 
2041  bool bWorkdir=msg.check("workdir");
2042  std::string strWorkdir=bWorkdir?msg.find("workdir").asString()+"\\":"";
2043 
2044  bSuccess=CreateProcess(nullptr, // command name
2045  (char*)(strWorkdir+strCmd).c_str(), // command line
2046  nullptr, // process security attributes
2047  nullptr, // primary thread security attributes
2048  TRUE, // handles are inherited
2049  CREATE_NEW_PROCESS_GROUP, // creation flags
2050  (LPVOID) chNewEnv, // use new environment list
2051  bWorkdir?strWorkdir.c_str():nullptr, // working directory
2052  &cmd_startup_info, // STARTUPINFO pointer
2053  &cmd_process_info); // receives PROCESS_INFORMATION
2054 
2055  if (!bSuccess && bWorkdir)
2056  {
2057  bSuccess=CreateProcess(nullptr, // command name
2058  (char*)(strCmd.c_str()), // command line
2059  nullptr, // process security attributes
2060  nullptr, // primary thread security attributes
2061  TRUE, // handles are inherited
2062  CREATE_NEW_PROCESS_GROUP, // creation flags
2063  (LPVOID) chNewEnv, // use new environment list
2064  strWorkdir.c_str(), // working directory
2065  &cmd_startup_info, // STARTUPINFO pointer
2066  &cmd_process_info); // receives PROCESS_INFORMATION
2067  }
2068 
2069  // deleting old environment variable
2070  FreeEnvironmentStrings(chOldEnv);
2071 
2072  if (!bSuccess)
2073  {
2074  result.addInt32(YARPRUN_ERROR);
2075 
2076  DWORD nBytes;
2077  std::string line1=std::string("ABORTED: server=")+mPortName
2078  +std::string(" alias=")+strAlias
2079  +std::string(" cmd=")+strCmd
2080  +std::string("pid=")+int2String(cmd_process_info.dwProcessId)
2081  +std::string("\n");
2082 
2083  WriteFile(write_to_pipe_cmd_to_stdout, line1.c_str(), line1.length(), &nBytes, 0);
2084 
2085  std::string line2=std::string("Can't execute command because ")+lastError2String()+std::string("\n");
2086  WriteFile(write_to_pipe_cmd_to_stdout, line1.c_str(), line2.length(), &nBytes, 0);
2087  FlushFileBuffers(write_to_pipe_cmd_to_stdout);
2088 
2089  std::string out=line1+line2;
2090  result.addString(out.c_str());
2091  fprintf(stderr, "%s", out.c_str());
2092  fflush(stderr);
2093 
2094  CloseHandle(write_to_pipe_cmd_to_stdout);
2095  CloseHandle(read_from_pipe_cmd_to_stdout);
2096 
2097  TerminateProcess(stdout_process_info.hProcess, YARPRUN_ERROR);
2098 
2099  CloseHandle(stdout_process_info.hProcess);
2100 
2101  return YARPRUN_ERROR;
2102  }
2103 
2104  FlushFileBuffers(write_to_pipe_cmd_to_stdout);
2105 
2106  // EVERYTHING IS ALL RIGHT
2107  YarpRunCmdWithStdioInfo* pInf = new YarpRunCmdWithStdioInfo(strAlias,
2108  mPortName,
2109  portName,
2110  cmd_process_info.dwProcessId,
2111  stdout_process_info.dwProcessId,
2112  read_from_pipe_cmd_to_stdout,
2113  write_to_pipe_cmd_to_stdout,
2114  cmd_process_info.hProcess,
2115  false);
2116 
2117 
2118 
2119 
2120  pInf->setCmd(strCmd);
2121  if (msg.check("env"))
2122  {
2123  pInf->setEnv(msg.find("env").asString());
2124  }
2125  mProcessVector.Add(pInf);
2126 
2127  result.addInt32(cmd_process_info.dwProcessId);
2128  std::string out=std::string("STARTED: server=")+mPortName
2129  +std::string(" alias=")+strAlias
2130  +std::string(" cmd=")+strCmd
2131  +std::string(" pid=")+int2String(cmd_process_info.dwProcessId)
2132  +std::string("\n");
2133 
2134  result.addString(out.c_str());
2135  result.addString(portName.c_str());
2136  fprintf(stderr, "%s", out.c_str());
2137 
2138  return cmd_process_info.dwProcessId;
2139 }
2140 
2141 
2142 int yarp::run::Run::executeCmd(yarp::os::Bottle& msg, yarp::os::Bottle& result)
2143 {
2144  std::string strAlias=msg.find("as").asString().c_str();
2145 
2146  // RUN COMMAND
2147  PROCESS_INFORMATION cmd_process_info;
2148  ZeroMemory(&cmd_process_info, sizeof(PROCESS_INFORMATION));
2149  STARTUPINFO cmd_startup_info;
2150  ZeroMemory(&cmd_startup_info, sizeof(STARTUPINFO));
2151 
2152  cmd_startup_info.cb=sizeof(STARTUPINFO);
2153 
2154  yarp::os::Bottle botCmd=msg.findGroup("cmd").tail();
2155 
2156  std::string strCmd;
2157  for (int s=0; s<botCmd.size(); ++s)
2158  {
2159  strCmd+=botCmd.get(s).toString()+std::string(" ");
2160  }
2161 
2162  /*
2163  * setting environment variable for child process
2164  */
2165  TCHAR chNewEnv[32767];
2166 
2167  // Get a pointer to the env block.
2168  LPTCH chOldEnv = GetEnvironmentStrings();
2169 
2170  // copying parent env variables
2171  LPTSTR lpOld = (LPTSTR) chOldEnv;
2172  LPTSTR lpNew = (LPTSTR) chNewEnv;
2173  while (*lpOld)
2174  {
2175  lstrcpy(lpNew, lpOld);
2176  lpOld += lstrlen(lpOld) + 1;
2177  lpNew += lstrlen(lpNew) + 1;
2178  }
2179 
2180  // Set the YARP_IS_YARPRUN environment variable to 1, so that the child
2181  // process will now that is running inside yarprun.
2182  lstrcpy(lpNew, (LPTCH) "YARP_IS_YARPRUN=1");
2183  lpNew += lstrlen(lpNew) + 1;
2184 
2185  // Set the YARPRUN_IS_FORWARDING_LOG environment variable to 0, so that
2186  // the child process will now that yarprun is not logging the output.
2187  lstrcpy(lpNew, (LPTCH) "YARPRUN_IS_FORWARDING_LOG=0");
2188  lpNew += lstrlen(lpNew) + 1;
2189 
2190  // adding new env variables
2191  std::string cstrEnvName;
2192  if (msg.check("env"))
2193  {
2194  lstrcpy(lpNew, (LPTCH) msg.find("env").asString().c_str());
2195  lpNew += lstrlen(lpNew) + 1;
2196  }
2197 
2198  // closing env block
2199  *lpNew = (TCHAR)0;
2200 
2201  bool bWorkdir=msg.check("workdir");
2202  std::string strWorkdir=bWorkdir?msg.find("workdir").asString()+"\\":"";
2203 
2204  BOOL bSuccess=CreateProcess(nullptr, // command name
2205  (char*)(strWorkdir+strCmd).c_str(), // command line
2206  nullptr, // process security attributes
2207  nullptr, // primary thread security attributes
2208  TRUE, // handles are inherited
2209  CREATE_NEW_PROCESS_GROUP, // creation flags
2210  (LPVOID) chNewEnv, // use new environment
2211  bWorkdir ? strWorkdir.c_str() : nullptr, // working directory
2212  &cmd_startup_info, // STARTUPINFO pointer
2213  &cmd_process_info); // receives PROCESS_INFORMATION
2214 
2215  if (!bSuccess && bWorkdir)
2216  {
2217  bSuccess=CreateProcess(nullptr, // command name
2218  (char*)(strCmd.c_str()), // command line
2219  nullptr, // process security attributes
2220  nullptr, // primary thread security attributes
2221  TRUE, // handles are inherited
2222  CREATE_NEW_PROCESS_GROUP, // creation flags
2223  (LPVOID) chNewEnv, // use new environment
2224  strWorkdir.c_str(), // working directory
2225  &cmd_startup_info, // STARTUPINFO pointer
2226  &cmd_process_info); // receives PROCESS_INFORMATION
2227  }
2228 
2229  // deleting old environment variable
2230  FreeEnvironmentStrings(chOldEnv);
2231 
2232  if (!bSuccess)
2233  {
2234  result.addInt32(YARPRUN_ERROR);
2235 
2236  std::string out=std::string("ABORTED: server=")+mPortName
2237  +std::string(" alias=")+strAlias
2238  +std::string(" cmd=")+strCmd
2239  +std::string(" pid=")+int2String(cmd_process_info.dwProcessId)
2240  +std::string("\nCan't execute command because ")+lastError2String()
2241  +std::string("\n");
2242 
2243  result.addString(out.c_str());
2244  fprintf(stderr, "%s", out.c_str());
2245  fflush(stderr);
2246 
2247  return YARPRUN_ERROR;
2248  }
2249 
2250  // EVERYTHING IS ALL RIGHT
2251  YarpRunProcInfo* pInf = new YarpRunProcInfo(strAlias,
2252  mPortName,
2253  cmd_process_info.dwProcessId,
2254  cmd_process_info.hProcess,
2255  false);
2256  pInf->setCmd(strCmd);
2257  if (msg.check("env"))
2258  pInf->setEnv(msg.find("env").asString());
2259 
2260  mProcessVector.Add(pInf);
2261 
2262  result.addInt32(cmd_process_info.dwProcessId);
2263  std::string out=std::string("STARTED: server=")+mPortName
2264  +std::string(" alias=")+strAlias
2265  +std::string(" cmd=")+strCmd
2266  +std::string(" pid=")+int2String(cmd_process_info.dwProcessId)
2267  +std::string("\n");
2268 
2269  fprintf(stderr, "%s", out.c_str());
2270 
2271  return cmd_process_info.dwProcessId;
2272 }
2273 
2274 // STDIO SERVER
2275 int yarp::run::Run::userStdio(yarp::os::Bottle& msg, yarp::os::Bottle& result)
2276 {
2277  PROCESS_INFORMATION stdio_process_info;
2278  ZeroMemory(&stdio_process_info, sizeof(PROCESS_INFORMATION));
2279 
2280  STARTUPINFO stdio_startup_info;
2281  ZeroMemory(&stdio_startup_info, sizeof(STARTUPINFO));
2282  stdio_startup_info.cb=sizeof(STARTUPINFO);
2283  stdio_startup_info.wShowWindow=SW_SHOWNOACTIVATE;
2284  stdio_startup_info.dwFlags=STARTF_USESHOWWINDOW;
2285 
2286  std::string strAlias=msg.find("as").asString();
2287  std::string strUUID=msg.find("stdiouuid").asString();
2288  std::string strCmd=std::string("yarprun --readwrite ")+strUUID;
2289  if (msg.check("forward")) strCmd+=std::string(" --forward ")+msg.findGroup("forward").get(1).asString()+std::string(" ")+msg.findGroup("forward").get(2).asString();
2290 
2291  BOOL bSuccess=CreateProcess(nullptr, // command name
2292  (char*)strCmd.c_str(), // command line
2293  nullptr, // process security attributes
2294  nullptr, // primary thread security attributes
2295  TRUE, // handles are inherited
2296  CREATE_NEW_CONSOLE, // creation flags
2297  nullptr, // use parent's environment
2298  nullptr, // use parent's current directory
2299  &stdio_startup_info, // STARTUPINFO pointer
2300  &stdio_process_info); // receives PROCESS_INFORMATION
2301 
2302  std::string out;
2303 
2304  if (bSuccess)
2305  {
2306  mStdioVector.Add(new YarpRunProcInfo(strAlias,
2307  mPortName,
2308  stdio_process_info.dwProcessId,
2309  stdio_process_info.hProcess,
2310  false));
2311 
2312  out=std::string("STARTED: server=")+mPortName
2313  +std::string(" alias=")+strAlias
2314  +std::string(" cmd=stdio pid=")+int2String(stdio_process_info.dwProcessId)
2315  +std::string("\n");
2316  }
2317  else
2318  {
2319  stdio_process_info.dwProcessId=YARPRUN_ERROR;
2320 
2321  out=std::string("ABORTED: server=")+mPortName
2322  +std::string(" alias=")+strAlias
2323  +std::string(" cmd=stdio\n")
2324  +std::string("Can't open stdio window because ")+lastError2String()
2325  +std::string("\n");
2326  }
2327 
2328  result.clear();
2329  result.addInt32(stdio_process_info.dwProcessId);
2330  result.addString(out.c_str());
2331  fprintf(stderr, "%s", out.c_str());
2332  fflush(stderr);
2333 
2334  return stdio_process_info.dwProcessId;
2335 }
2336 
2338 #else // LINUX
2340 
2343 void splitLine(char *pLine, char **pArgs)
2344 {
2345  char *pTmp = strchr(pLine, ' ');
2346 
2347  if (pTmp) {
2348  *pTmp = '\0';
2349  pTmp++;
2350  while ((*pTmp) && (*pTmp == ' ')) {
2351  pTmp++;
2352  }
2353  if (*pTmp == '\0') {
2354  pTmp = nullptr;
2355  }
2356  }
2357  *pArgs = pTmp;
2358 }
2359 
2363 void parseArguments(char *io_pLine, int *o_pArgc, char **o_pArgv)
2364 {
2365  char *pNext = io_pLine;
2366  size_t i;
2367  int j;
2368  int quoted = 0;
2369  size_t len = strlen(io_pLine);
2370 
2371  // Protect spaces inside quotes, but lose the quotes
2372  for(i = 0; i < len; i++) {
2373  if ((!quoted) && ('"' == io_pLine[i])) {
2374  quoted = 1;
2375  io_pLine[i] = ' ';
2376  } else if ((quoted) && ('"' == io_pLine[i])) {
2377  quoted = 0;
2378  io_pLine[i] = ' ';
2379  } else if ((quoted) && (' ' == io_pLine[i])) {
2380  io_pLine[i] = '\1';
2381  }
2382  }
2383 
2384  // init
2385  memset(o_pArgv, 0x00, sizeof(char*) * C_MAXARGS);
2386  *o_pArgc = 1;
2387  o_pArgv[0] = io_pLine;
2388 
2389  while ((nullptr != pNext) && (*o_pArgc < C_MAXARGS)) {
2390  splitLine(pNext, &(o_pArgv[*o_pArgc]));
2391  pNext = o_pArgv[*o_pArgc];
2392 
2393  if (nullptr != o_pArgv[*o_pArgc]) {
2394  *o_pArgc += 1;
2395  }
2396  }
2397 
2398  for(j = 0; j < *o_pArgc; j++) {
2399  len = strlen(o_pArgv[j]);
2400  for(i = 0; i < len; i++) {
2401  if ('\1' == o_pArgv[j][i]) {
2402  o_pArgv[j][i] = ' ';
2403  }
2404  }
2405  }
2406 }
2407 
2408 void yarp::run::Run::CleanZombie(int pid)
2409 {
2410  bool bFound=mProcessVector && mProcessVector->CleanZombie(pid);
2411 
2412  if (!bFound) if (mStdioVector) mStdioVector->CleanZombie(pid);
2413 }
2414 
2416 
2417 int yarp::run::Run::executeCmdAndStdio(yarp::os::Bottle& msg, yarp::os::Bottle& result)
2418 {
2419  std::string strAlias=msg.find("as").asString();
2420  std::string strCmd=msg.find("cmd").asString();
2421  std::string strStdio=msg.find("stdio").asString();
2422  std::string strStdioUUID=msg.find("stdiouuid").asString();
2423 
2424  int pipe_stdin_to_cmd[2];
2425  int ret_stdin_to_cmd=yarp::run::impl::pipe(pipe_stdin_to_cmd);
2426 
2427  int pipe_cmd_to_stdout[2];
2428  int ret_cmd_to_stdout=yarp::run::impl::pipe(pipe_cmd_to_stdout);
2429 
2430  int pipe_child_to_parent[2];
2431  int ret_child_to_parent=yarp::run::impl::pipe(pipe_child_to_parent);
2432 
2433  if (ret_child_to_parent!=0 || ret_cmd_to_stdout!=0 || ret_stdin_to_cmd!=0)
2434  {
2435  int error=errno;
2436 
2437  std::string out=std::string("ABORTED: server=")+mPortName
2438  +std::string(" alias=")+strAlias
2439  +std::string(" cmd=stdout\n")
2440  +std::string("Can't create pipes ")+strerror(error)
2441  +std::string("\n");
2442 
2443  result.addInt32(YARPRUN_ERROR);
2444  result.addString(out.c_str());
2445  fprintf(stderr, "%s", out.c_str());
2446 
2447  return YARPRUN_ERROR;
2448  }
2449 
2450  int pid_stdout=yarp::run::impl::fork();
2451 
2452  if (IS_INVALID(pid_stdout))
2453  {
2454  int error=errno;
2455 
2456  CLOSE(pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2457  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2458  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2459  CLOSE(pipe_cmd_to_stdout[READ_FROM_PIPE]);
2460  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2461  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2462 
2463  std::string out=std::string("ABORTED: server=")+mPortName
2464  +std::string(" alias=")+strAlias
2465  +std::string(" cmd=stdout\n")
2466  +std::string("Can't fork stdout process because ")+strerror(error)
2467  +std::string("\n");
2468 
2469  result.addInt32(YARPRUN_ERROR);
2470  result.addString(out.c_str());
2471  fprintf(stderr, "%s", out.c_str());
2472 
2473  return YARPRUN_ERROR;
2474  }
2475 
2476  if (IS_NEW_PROCESS(pid_stdout)) // STDOUT IMPLEMENTED HERE
2477  {
2478  REDIRECT_TO(STDIN_FILENO, pipe_cmd_to_stdout[READ_FROM_PIPE]);
2479 
2480  CLOSE(pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2481  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2482  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2483  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2484 
2485  //Why removing vectors and stop threads?
2486  //exec* never returns and memory is claimed by the system
2487  //furthermore after fork() only the thread which called fork() is forked!
2488  // cleanBeforeExec();
2489 
2490  //yarp::os::impl::signal(SIGPIPE, SIG_DFL);
2491 
2492  int ret = yarp::run::impl::execlp("yarprun", "yarprun", "--write", strStdioUUID.c_str(), static_cast<char*>(nullptr));
2493 
2494  CLOSE(pipe_cmd_to_stdout[READ_FROM_PIPE]);
2495 
2496  if (ret==YARPRUN_ERROR)
2497  {
2498  int error=errno;
2499 
2500  std::string out=std::string("ABORTED: server=")+mPortName
2501  +std::string(" alias=")+strAlias
2502  +std::string(" cmd=stdout\n")
2503  +std::string("Can't execute stdout because ")+strerror(error)
2504  +std::string("\n");
2505 
2506  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
2507  fprintf(out_to_parent, "%s", out.c_str());
2508  fflush(out_to_parent);
2509  fclose(out_to_parent);
2510 
2511  fprintf(stderr, "%s", out.c_str());
2512  }
2513 
2514  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2515 
2516  std::exit(ret);
2517  }
2518 
2519  if (IS_PARENT_OF(pid_stdout))
2520  {
2521  CLOSE(pipe_cmd_to_stdout[READ_FROM_PIPE]);
2522 
2523  fprintf(stderr, "STARTED: server=%s alias=%s cmd=stdout pid=%d\n", mPortName.c_str(), strAlias.c_str(), pid_stdout);
2524 
2525  int pid_stdin=yarp::run::impl::fork();
2526 
2527  if (IS_INVALID(pid_stdin))
2528  {
2529  int error=errno;
2530 
2531  CLOSE(pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2532  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2533  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2534  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2535  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2536 
2537  std::string out=std::string("ABORTED: server=")+mPortName
2538  +std::string(" alias=")+strAlias
2539  +std::string(" cmd=stdin\n")
2540  +std::string("Can't fork stdin process because ")+strerror(error)
2541  +std::string("\n");
2542 
2543  result.addInt32(YARPRUN_ERROR);
2544  result.addString(out.c_str());
2545  fprintf(stderr, "%s", out.c_str());
2546 
2547  SIGNAL(pid_stdout, SIGTERM);
2548  fprintf(stderr, "TERMINATING stdout (%d)\n", pid_stdout);
2549 
2550  return YARPRUN_ERROR;
2551  }
2552 
2553  if (IS_NEW_PROCESS(pid_stdin)) // STDIN IMPLEMENTED HERE
2554  {
2555  REDIRECT_TO(STDOUT_FILENO, pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2556  REDIRECT_TO(STDERR_FILENO, pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2557 
2558  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2559  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2560  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2561 
2562  //Why removing vectors and stop threads?
2563  //exec* never returns and memory is claimed by the system
2564  //furthermore after fork() only the thread which called fork() is forked!
2565  // cleanBeforeExec();
2566 
2567  //yarp::os::impl::signal(SIGPIPE, SIG_DFL);
2568 
2569  int ret = yarp::run::impl::execlp("yarprun", "yarprun", "--read", strStdioUUID.c_str(), static_cast<char*>(nullptr));
2570 
2571  CLOSE(pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2572 
2573  if (ret==YARPRUN_ERROR)
2574  {
2575  int error=errno;
2576 
2577  std::string out=std::string("ABORTED: server=")+mPortName
2578  +std::string(" alias=")+strAlias
2579  +std::string(" cmd=stdin\n")
2580  +std::string("Can't execute stdin because ")+strerror(error)
2581  +std::string("\n");
2582 
2583 
2584  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
2585  fprintf(out_to_parent, "%s", out.c_str());
2586  fflush(out_to_parent);
2587  fclose(out_to_parent);
2588  fprintf(stderr, "%s", out.c_str());
2589  }
2590 
2591  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2592 
2593  std::exit(ret);
2594  }
2595 
2596  if (IS_PARENT_OF(pid_stdin))
2597  {
2598  // connect yarp read and write
2599  CLOSE(pipe_stdin_to_cmd[WRITE_TO_PIPE]);
2600 
2601  fprintf(stderr, "STARTED: server=%s alias=%s cmd=stdin pid=%d\n", mPortName.c_str(), strAlias.c_str(), pid_stdin);
2602 
2603  int pid_cmd=yarp::run::impl::fork();
2604 
2605  if (IS_INVALID(pid_cmd))
2606  {
2607  int error=errno;
2608 
2609  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2610  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2611  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2612 
2613  std::string out=std::string("ABORTED: server=")+mPortName
2614  +std::string(" alias=")+strAlias
2615  +std::string(" cmd=")+strCmd
2616  +std::string("\nCan't fork command process because ")+strerror(error)
2617  +std::string("\n");
2618 
2619  result.addInt32(YARPRUN_ERROR);
2620  result.addString(out.c_str());
2621  fprintf(stderr, "%s", out.c_str());
2622 
2623  FILE* to_yarp_stdout=fdopen(pipe_cmd_to_stdout[WRITE_TO_PIPE], "w");
2624  fprintf(to_yarp_stdout, "%s", out.c_str());
2625  fflush(to_yarp_stdout);
2626  fclose(to_yarp_stdout);
2627 
2628  SIGNAL(pid_stdout, SIGTERM);
2629  fprintf(stderr, "TERMINATING stdout (%d)\n", pid_stdout);
2630  SIGNAL(pid_stdin, SIGTERM);
2631  fprintf(stderr, "TERMINATING stdin (%d)\n", pid_stdin);
2632 
2633  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2634 
2635  return YARPRUN_ERROR;
2636  }
2637 
2638  if (IS_NEW_PROCESS(pid_cmd)) // RUN COMMAND HERE
2639  {
2640  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2641 
2642  char *cmd_str=new char[strCmd.length()+1];
2643  strcpy(cmd_str, strCmd.c_str());
2644  /*
2645  int nargs=CountArgs(cmd_str);
2646  char **arg_str=new char*[nargs+1];
2647  ParseCmd(cmd_str, arg_str);
2648  arg_str[nargs]=0;
2649  */
2650  int nargs = 0;
2651  char **arg_str = new char*[C_MAXARGS + 1];
2652  parseArguments(cmd_str, &nargs, arg_str);
2653  arg_str[nargs]=nullptr;
2654 
2655  setvbuf(stdout, nullptr, _IONBF, 0);
2656 
2657  REDIRECT_TO(STDIN_FILENO, pipe_stdin_to_cmd[READ_FROM_PIPE]);
2658  REDIRECT_TO(STDOUT_FILENO, pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2659  REDIRECT_TO(STDERR_FILENO, pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2660 
2661  // Set the YARP_IS_YARPRUN environment variable to 1, so that the child
2662  // process will now that is running inside yarprun.
2663  yarp::conf::environment::set_string("YARP_IS_YARPRUN", "1");
2664 
2665  // Set the YARPRUN_IS_FORWARDING_LOG environment variable to 1, so that
2666  // the child process will now that yarprun is not logging the output.
2667  yarp::conf::environment::set_string("YARPRUN_IS_FORWARDING_LOG", "1");
2668 
2669  if (msg.check("env"))
2670  {
2671  auto ss = yarp::conf::string::split(msg.find("env").asString(), ';');
2672  for (const auto& s : ss) {
2673  char* szenv = new char[s.size()+1];
2674  strcpy(szenv, s.c_str());
2675  yarp::run::impl::putenv(szenv); // putenv doesn't make copy of the string
2676  }
2677  //delete [] szenv;
2678  }
2679 
2680  if (msg.check("workdir"))
2681  {
2682  int ret = yarp::os::impl::chdir(msg.find("workdir").asString().c_str());
2683 
2684  if (ret!=0)
2685  {
2686  int error=errno;
2687 
2688  std::string out=std::string("ABORTED: server=")+mPortName
2689  +std::string(" alias=")+strAlias
2690  +std::string(" cmd=")+strCmd
2691  +std::string("\nCan't execute command, cannot set working directory ")+strerror(error)
2692  +std::string("\n");
2693 
2694  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
2695  fprintf(out_to_parent, "%s", out.c_str());
2696  fflush(out_to_parent);
2697  fclose(out_to_parent);
2698  fprintf(stderr, "%s", out.c_str());
2699 
2700  std::exit(ret);
2701  }
2702  }
2703 
2704  int ret=YARPRUN_ERROR;
2705 
2706  char currWorkDirBuff[1024];
2707  char *currWorkDir=yarp::os::impl::getcwd(currWorkDirBuff, 1024);
2708 
2709  if (currWorkDir)
2710  {
2711  char **cwd_arg_str=new char*[nargs+1];
2712  for (int i=1; i<nargs; ++i) cwd_arg_str[i]=arg_str[i];
2713  cwd_arg_str[nargs]=nullptr;
2714  cwd_arg_str[0]=new char[strlen(currWorkDir)+strlen(arg_str[0])+16];
2715 
2716  strcpy(cwd_arg_str[0], currWorkDir);
2717  strcat(cwd_arg_str[0], "/");
2718  strcat(cwd_arg_str[0], arg_str[0]);
2719 
2720  //Why removing vectors and stop threads?
2721  //exec* never returns and memory is claimed by the system
2722  //furthermore after fork() only the thread which called fork() is forked!
2723  // cleanBeforeExec();
2724 
2725  ret = yarp::run::impl::execvp(cwd_arg_str[0], cwd_arg_str);
2726 
2727  delete [] cwd_arg_str[0];
2728  delete [] cwd_arg_str;
2729  }
2730 
2731  if (ret==YARPRUN_ERROR)
2732  {
2733  //Why removing vectors and stop threads?
2734  //exec* never returns and memory is claimed by the system
2735  //furthermore after fork() only the thread which called fork() is forked!
2736  // cleanBeforeExec();
2737 
2738  ret = yarp::run::impl::execvp(arg_str[0], arg_str);
2739  }
2740 
2741  fflush(stdout);
2742 
2743  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2744  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2745 
2746  if (ret==YARPRUN_ERROR)
2747  {
2748  int error=errno;
2749 
2750  std::string out=std::string("ABORTED: server=")+mPortName
2751  +std::string(" alias=")+strAlias
2752  +std::string(" cmd=")+strCmd
2753  +std::string("\nCan't execute command because ")+strerror(error)
2754  +std::string("\n");
2755 
2756  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
2757  fprintf(out_to_parent, "%s", out.c_str());
2758  fflush(out_to_parent);
2759  fclose(out_to_parent);
2760  fprintf(stderr, "%s", out.c_str());
2761  }
2762 
2763  delete [] cmd_str;
2764  delete [] arg_str;
2765 
2766  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2767 
2768  std::exit(ret);
2769  }
2770 
2771 
2772  if (IS_PARENT_OF(pid_cmd))
2773  {
2774  CLOSE(pipe_stdin_to_cmd[READ_FROM_PIPE]);
2775  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2776  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2777 
2778  auto* pInf = new YarpRunCmdWithStdioInfo(
2779  strAlias,
2780  mPortName,
2781  strStdio,
2782  pid_cmd,
2783  strStdioUUID,
2784  mStdioVector,
2785  pid_stdin,
2786  pid_stdout,
2787  pipe_stdin_to_cmd[READ_FROM_PIPE],
2788  pipe_stdin_to_cmd[WRITE_TO_PIPE],
2789  pipe_cmd_to_stdout[READ_FROM_PIPE],
2790  pipe_cmd_to_stdout[WRITE_TO_PIPE],
2791  nullptr,
2792  false
2793  );
2794 
2795  pInf->setCmd(strCmd);
2796 
2797  if (msg.check("env"))
2798  {
2799  pInf->setEnv(msg.find("env").asString());
2800  }
2801 
2802  mProcessVector->Add(pInf);
2803 
2805 
2806  FILE* in_from_child=fdopen(pipe_child_to_parent[READ_FROM_PIPE], "r");
2807  int flags=fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_GETFL, 0);
2808  fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_SETFL, flags|O_NONBLOCK);
2809 
2810  std::string out;
2811 
2812  if (in_from_child)
2813  {
2814  char buff[1024];
2815 
2816  while(true)
2817  {
2818  if (!fgets(buff, 1024, in_from_child) || ferror(in_from_child) || feof(in_from_child)) break;
2819 
2820  out+=std::string(buff);
2821  }
2822 
2823  fclose(in_from_child);
2824  }
2825 
2826  if (out.length()>0)
2827  {
2828  pid_cmd=YARPRUN_ERROR;
2829  }
2830  else
2831  {
2832  out=std::string("STARTED: server=")+mPortName
2833  +std::string(" alias=")+strAlias
2834  +std::string(" cmd=")+strCmd
2835  +std::string(" pid=")+int2String(pid_cmd)
2836  +std::string("\n");
2837  }
2838 
2839  result.addInt32(pid_cmd);
2840  result.addString(out.c_str());
2841  result.addString(strStdioUUID.c_str());
2842 
2843  fprintf(stderr, "%s", out.c_str());
2844 
2845  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2846 
2847  return pid_cmd;
2848  }
2849  }
2850  }
2851 
2852  result.addInt32(YARPRUN_ERROR);
2853  result.addString("I should never reach this point!!!\n");
2854 
2855  return YARPRUN_ERROR;
2856 }
2857 
2858 int yarp::run::Run::executeCmdStdout(yarp::os::Bottle& msg, yarp::os::Bottle& result, std::string& loggerName)
2859 {
2860  std::string strAlias=msg.find("as").asString();
2861  std::string strCmd=msg.find("cmd").asString();
2862 
2863  std::string portName="/log";
2864  portName+=mPortName+"/";
2865 
2866  std::string command = strCmd;
2867  command = command.substr(0, command.find(' '));
2868  command = command.substr(command.find_last_of("\\/") + 1);
2869 
2870  portName+=command;
2871 
2872 
2873 
2874  int pipe_cmd_to_stdout[2];
2875  int ret_cmd_to_stdout=yarp::run::impl::pipe(pipe_cmd_to_stdout);
2876 
2877  int pipe_child_to_parent[2];
2878  int ret_child_to_parent=yarp::run::impl::pipe(pipe_child_to_parent);
2879 
2880  if (ret_child_to_parent!=0 || ret_cmd_to_stdout!=0)
2881  {
2882  int error=errno;
2883 
2884  std::string out=std::string("ABORTED: server=")+mPortName
2885  +std::string(" alias=")+strAlias
2886  +std::string(" cmd=stdout\n")
2887  +std::string("Can't create pipes ")+strerror(error)
2888  +std::string("\n");
2889 
2890  result.addInt32(YARPRUN_ERROR);
2891  result.addString(out.c_str());
2892  fprintf(stderr, "%s", out.c_str());
2893 
2894  return YARPRUN_ERROR;
2895  }
2896 
2897  int pid_stdout=yarp::run::impl::fork();
2898 
2899  if (IS_INVALID(pid_stdout))
2900  {
2901  int error=errno;
2902 
2903  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2904  CLOSE(pipe_cmd_to_stdout[READ_FROM_PIPE]);
2905  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2906  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2907 
2908  std::string out=std::string("ABORTED: server=")+mPortName
2909  +std::string(" alias=")+strAlias
2910  +std::string(" cmd=stdout\n")
2911  +std::string("Can't fork stdout process because ")+strerror(error)
2912  +std::string("\n");
2913 
2914  result.addInt32(YARPRUN_ERROR);
2915  result.addString(out.c_str());
2916  fprintf(stderr, "%s", out.c_str());
2917 
2918  return YARPRUN_ERROR;
2919  }
2920 
2921  if (IS_NEW_PROCESS(pid_stdout)) // STDOUT IMPLEMENTED HERE
2922  {
2923  REDIRECT_TO(STDIN_FILENO, pipe_cmd_to_stdout[READ_FROM_PIPE]);
2924 
2925  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2926  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2927 
2928  //Why removing vectors and stop threads?
2929  //exec* never returns and memory is claimed by the system
2930  //furthermore after fork() only the thread which called fork() is forked!
2931  // cleanBeforeExec();
2932 
2933  //yarp::os::impl::signal(SIGPIPE, SIG_DFL);
2934 
2935  int ret = yarp::run::impl::execlp("yarprun", "yarprun", "--write", portName.c_str(), "--log", loggerName.c_str(), static_cast<char*>(nullptr));
2936 
2937  CLOSE(pipe_cmd_to_stdout[READ_FROM_PIPE]);
2938 
2939  if (ret==YARPRUN_ERROR)
2940  {
2941  int error=errno;
2942 
2943  std::string out=std::string("ABORTED: server=")+mPortName
2944  +std::string(" alias=")+strAlias
2945  +std::string(" cmd=stdout\n")
2946  +std::string("Can't execute stdout because ")+strerror(error)
2947  +std::string("\n");
2948 
2949  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
2950  fprintf(out_to_parent, "%s", out.c_str());
2951  fflush(out_to_parent);
2952  fclose(out_to_parent);
2953 
2954  fprintf(stderr, "%s", out.c_str());
2955  }
2956 
2957  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2958 
2959  std::exit(ret);
2960  }
2961 
2962  if (IS_PARENT_OF(pid_stdout))
2963  {
2964  CLOSE(pipe_cmd_to_stdout[READ_FROM_PIPE]);
2965 
2966  fprintf(stderr, "STARTED: server=%s alias=%s cmd=stdout pid=%d\n", mPortName.c_str(), strAlias.c_str(), pid_stdout);
2967 
2969 
2970  //if (IS_PARENT_OF(pid_stdin))
2971  {
2972  int pid_cmd=yarp::run::impl::fork();
2973 
2974  if (IS_INVALID(pid_cmd))
2975  {
2976  int error=errno;
2977 
2978  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
2979  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
2980 
2981  std::string out=std::string("ABORTED: server=")+mPortName
2982  +std::string(" alias=")+strAlias
2983  +std::string(" cmd=")+strCmd
2984  +std::string("\nCan't fork command process because ")+strerror(error)
2985  +std::string("\n");
2986 
2987  result.addInt32(YARPRUN_ERROR);
2988  result.addString(out.c_str());
2989  fprintf(stderr, "%s", out.c_str());
2990 
2991  FILE* to_yarp_stdout=fdopen(pipe_cmd_to_stdout[WRITE_TO_PIPE], "w");
2992  fprintf(to_yarp_stdout, "%s", out.c_str());
2993  fflush(to_yarp_stdout);
2994  fclose(to_yarp_stdout);
2995 
2996  SIGNAL(pid_stdout, SIGTERM);
2997  fprintf(stderr, "TERMINATING stdout (%d)\n", pid_stdout);
2998  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
2999 
3000  return YARPRUN_ERROR;
3001  }
3002 
3003  if (IS_NEW_PROCESS(pid_cmd)) // RUN COMMAND HERE
3004  {
3005  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
3006 
3007  char *cmd_str=new char[strCmd.length()+1];
3008  strcpy(cmd_str, strCmd.c_str());
3009  /*
3010  int nargs=CountArgs(cmd_str);
3011  char **arg_str=new char*[nargs+1];
3012  ParseCmd(cmd_str, arg_str);
3013  arg_str[nargs]=0;
3014  */
3015  int nargs = 0;
3016  char **arg_str = new char*[C_MAXARGS + 1];
3017  parseArguments(cmd_str, &nargs, arg_str);
3018  arg_str[nargs]=nullptr;
3019 
3020  setvbuf(stdout, nullptr, _IONBF, 0);
3021 
3022  REDIRECT_TO(STDOUT_FILENO, pipe_cmd_to_stdout[WRITE_TO_PIPE]);
3023  REDIRECT_TO(STDERR_FILENO, pipe_cmd_to_stdout[WRITE_TO_PIPE]);
3024 
3025  // Set the YARP_IS_YARPRUN environment variable to 1, so that the child
3026  // process will now that is running inside yarprun.
3027  yarp::conf::environment::set_string("YARP_IS_YARPRUN", "1");
3028 
3029  // Set the YARPRUN_IS_FORWARDING_LOG environment variable to 1, so that
3030  // the child process will now that yarprun is not logging the output.
3031  yarp::conf::environment::set_string("YARPRUN_IS_FORWARDING_LOG", "1");
3032 
3033  if (msg.check("env"))
3034  {
3035  auto ss = yarp::conf::string::split(msg.find("env").asString(), ';');
3036  for (const auto& s : ss) {
3037  char* szenv = new char[s.size()+1];
3038  strcpy(szenv, s.c_str());
3039  yarp::run::impl::putenv(szenv); // putenv doesn't make copy of the string
3040  }
3041  //delete [] szenv;
3042  }
3043 
3044  if (msg.check("workdir"))
3045  {
3046  int ret = yarp::os::impl::chdir(msg.find("workdir").asString().c_str());
3047 
3048  if (ret!=0)
3049  {
3050  int error=errno;
3051 
3052  std::string out=std::string("ABORTED: server=")+mPortName
3053  +std::string(" alias=")+strAlias
3054  +std::string(" cmd=")+strCmd
3055  +std::string("\nCan't execute command, cannot set working directory ")+strerror(error)
3056  +std::string("\n");
3057 
3058  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
3059  fprintf(out_to_parent, "%s", out.c_str());
3060  fflush(out_to_parent);
3061  fclose(out_to_parent);
3062  fprintf(stderr, "%s", out.c_str());
3063 
3064  std::exit(ret);
3065  }
3066  }
3067 
3068  int ret=YARPRUN_ERROR;
3069 
3070  char currWorkDirBuff[1024];
3071  char *currWorkDir=getcwd(currWorkDirBuff, 1024);
3072 
3073  if (currWorkDir)
3074  {
3075  char **cwd_arg_str=new char*[nargs+1];
3076  for (int i=1; i<nargs; ++i) cwd_arg_str[i]=arg_str[i];
3077  cwd_arg_str[nargs]=nullptr;
3078  cwd_arg_str[0]=new char[strlen(currWorkDir)+strlen(arg_str[0])+16];
3079 
3080  strcpy(cwd_arg_str[0], currWorkDir);
3081  strcat(cwd_arg_str[0], "/");
3082  strcat(cwd_arg_str[0], arg_str[0]);
3083 
3084  //Why removing vectors and stop threads?
3085  //exec* never returns and memory is claimed by the system
3086  //furthermore after fork() only the thread which called fork() is forked!
3087  // cleanBeforeExec();
3088 
3089  ret = yarp::run::impl::execvp(cwd_arg_str[0], cwd_arg_str);
3090 
3091  delete [] cwd_arg_str[0];
3092  delete [] cwd_arg_str;
3093  }
3094 
3095  if (ret==YARPRUN_ERROR)
3096  {
3097  //Why removing vectors and stop threads?
3098  //exec* never returns and memory is claimed by the system
3099  //furthermore after fork() only the thread which called fork() is forked!
3100  // cleanBeforeExec();
3101 
3102  ret = yarp::run::impl::execvp(arg_str[0], arg_str);
3103  }
3104 
3105  fflush(stdout);
3106 
3107  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
3108 
3109  if (ret==YARPRUN_ERROR)
3110  {
3111  int error=errno;
3112 
3113  std::string out=std::string("ABORTED: server=")+mPortName
3114  +std::string(" alias=")+strAlias
3115  +std::string(" cmd=")+strCmd
3116  +std::string("\nCan't execute command because ")+strerror(error)
3117  +std::string("\n");
3118 
3119  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
3120  fprintf(out_to_parent, "%s", out.c_str());
3121  fflush(out_to_parent);
3122  fclose(out_to_parent);
3123  fprintf(stderr, "%s", out.c_str());
3124  }
3125 
3126  delete [] cmd_str;
3127  delete [] arg_str;
3128 
3129  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
3130 
3131  std::exit(ret);
3132  }
3133 
3134 
3135  if (IS_PARENT_OF(pid_cmd))
3136  {
3137  CLOSE(pipe_cmd_to_stdout[WRITE_TO_PIPE]);
3138  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
3139 
3140  auto* pInf = new YarpRunCmdWithStdioInfo(
3141  strAlias,
3142  mPortName,
3143  portName,
3144  pid_cmd,
3145  pid_stdout,
3146  pipe_cmd_to_stdout[READ_FROM_PIPE],
3147  pipe_cmd_to_stdout[WRITE_TO_PIPE],
3148  nullptr,
3149  false
3150  );
3151 
3152  pInf->setCmd(strCmd);
3153 
3154  if (msg.check("env"))
3155  {
3156  pInf->setEnv(msg.find("env").asString());
3157  }
3158 
3159  mProcessVector->Add(pInf);
3160 
3162 
3163  FILE* in_from_child=fdopen(pipe_child_to_parent[READ_FROM_PIPE], "r");
3164  int flags=fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_GETFL, 0);
3165  fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_SETFL, flags|O_NONBLOCK);
3166 
3167  std::string out;
3168 
3169  if (in_from_child)
3170  {
3171  char buff[1024];
3172 
3173  while(true)
3174  {
3175  if (!fgets(buff, 1024, in_from_child) || ferror(in_from_child) || feof(in_from_child)) break;
3176 
3177  out+=std::string(buff);
3178  }
3179 
3180  fclose(in_from_child);
3181  }
3182 
3183  if (out.length()>0)
3184  {
3185  pid_cmd=YARPRUN_ERROR;
3186  }
3187  else
3188  {
3189  out=std::string("STARTED: server=")+mPortName
3190  +std::string(" alias=")+strAlias
3191  +std::string(" cmd=")+strCmd
3192  +std::string(" pid=")+int2String(pid_cmd)
3193  +std::string("\n");
3194  }
3195 
3196  result.addInt32(pid_cmd);
3197  result.addString(out.c_str());
3198 
3199  fprintf(stderr, "%s", out.c_str());
3200 
3201  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
3202 
3203  return pid_cmd;
3204  }
3205  }
3206  }
3207 
3208  result.addInt32(YARPRUN_ERROR);
3209  result.addString("I should never reach this point!!!\n");
3210 
3211  return YARPRUN_ERROR;
3212 }
3213 
3214 int yarp::run::Run::userStdio(yarp::os::Bottle& msg, yarp::os::Bottle& result)
3215 {
3216  std::string strAlias=msg.find("as").asString();
3217  std::string strUUID=msg.find("stdiouuid").asString();
3218 
3219  std::string strCmd;
3220 
3221  if (msg.check("forward"))
3222  {
3223  strCmd=std::string("/bin/bash -l -c \"yarprun --readwrite ")+strUUID
3224  +std::string(" --forward ")+msg.findGroup("forward").get(1).asString()+std::string(" ")+msg.findGroup("forward").get(2).asString()+std::string("\"");
3225  }
3226  else
3227  {
3228  strCmd=std::string("/bin/bash -l -c \"yarprun --readwrite ")+strUUID+"\"";
3229  }
3230 
3231  int pipe_child_to_parent[2];
3232 
3233  if (yarp::run::impl::pipe(pipe_child_to_parent))
3234  {
3235  int error=errno;
3236 
3237  std::string out=std::string("ABORTED: server=")+mPortName
3238  +std::string(" alias=")+strAlias
3239  +std::string(" cmd=stdio\nCan't create pipe ")+strerror(error)
3240  +std::string("\n");
3241 
3242  result.clear();
3243  result.addInt32(YARPRUN_ERROR);
3244  result.addString(out.c_str());
3245  fprintf(stderr, "%s", out.c_str());
3246 
3247  return YARPRUN_ERROR;
3248  }
3249 
3250  int c=0;
3251  char *command[16];
3252  for (auto & i : command) {
3253  i = nullptr;
3254  }
3255 
3256  cmdcpy(command[c++], "xterm");
3257  cmdcpy(command[c++], msg.check("hold")?"-hold":"+hold");
3258 
3259  if (msg.check("geometry"))
3260  {
3261  cmdcpy(command[c++], "-geometry");
3262  cmdcpy(command[c++], msg.find("geometry").asString().c_str());
3263  }
3264 
3265  cmdcpy(command[c++], "-title");
3266  cmdcpy(command[c++], strAlias.c_str());
3267 
3268  cmdcpy(command[c++], "-e");
3269  cmdcpy(command[c++], strCmd.c_str());
3270 
3271  int pid_cmd=yarp::run::impl::fork();
3272 
3273  if (IS_INVALID(pid_cmd))
3274  {
3275  int error=errno;
3276 
3277  std::string out=std::string("ABORTED: server=")+mPortName
3278  +std::string(" alias=")+strAlias
3279  +std::string(" cmd=stdio\nCan't fork stdout process because ")+strerror(error)
3280  +std::string("\n");
3281 
3282  result.clear();
3283  result.addInt32(YARPRUN_ERROR);
3284  result.addString(out.c_str());
3285  fprintf(stderr, "%s", out.c_str());
3286 
3287  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
3288  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
3289 
3290  cmdclean(command);
3291 
3292  return YARPRUN_ERROR;
3293  }
3294 
3295  if (IS_NEW_PROCESS(pid_cmd)) // RUN COMMAND HERE
3296  {
3297  //yarp::os::impl::signal(SIGPIPE, SIG_IGN);
3298 
3299  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
3300 
3301  REDIRECT_TO(STDERR_FILENO, pipe_child_to_parent[WRITE_TO_PIPE]);
3302 
3303  //Why removing vectors and stop threads?
3304  //exec* never returns and memory is claimed by the system
3305  //furthermore after fork() only the thread which called fork() is forked!
3306  // cleanBeforeExec();
3307 
3308  //yarp::os::impl::signal(SIGHUP, rwSighupHandler);
3309 
3310  int ret = yarp::run::impl::execvp("xterm", command);
3311 
3312  cmdclean(command);
3313 
3314  if (ret==YARPRUN_ERROR)
3315  {
3316  int error=errno;
3317 
3318  std::string out=std::string("ABORTED: server=")+mPortName
3319  +std::string(" alias=")+strAlias
3320  +std::string(" cmd=xterm\nCan't execute command because ")+strerror(error)
3321  +std::string("\n");
3322 
3323  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
3324 
3325  fprintf(out_to_parent, "%s", out.c_str());
3326  fflush(out_to_parent);
3327  fclose(out_to_parent);
3328 
3329  fprintf(stderr, "%s", out.c_str());
3330  }
3331 
3332  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
3333 
3334  std::exit(ret);
3335  }
3336 
3337  if (IS_PARENT_OF(pid_cmd))
3338  {
3339  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
3340 
3341  mStdioVector->Add(new YarpRunProcInfo(strAlias, mPortName, pid_cmd, nullptr, msg.check("hold")));
3342 
3343  result.clear();
3344 
3345  cmdclean(command);
3346 
3348 
3349  FILE* in_from_child=fdopen(pipe_child_to_parent[READ_FROM_PIPE], "r");
3350  int flags=fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_GETFL, 0);
3351  fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_SETFL, flags|O_NONBLOCK);
3352  std::string out;
3353 
3354  if (in_from_child)
3355  {
3356  char buff[1024];
3357 
3358  while(true)
3359  {
3360  if (!fgets(buff, 1024, in_from_child) || ferror(in_from_child) || feof(in_from_child)) break;
3361 
3362  out+=std::string(buff);
3363  }
3364 
3365  fclose(in_from_child);
3366  }
3367 
3368  result.clear();
3369 
3370  //if (out.length()>0)
3371  if (out.substr(0, 14)=="xterm Xt error" || out.substr(0, 7)=="ABORTED")
3372  {
3373  pid_cmd=YARPRUN_ERROR;
3374  }
3375  else
3376  {
3377  out=std::string("STARTED: server=")+mPortName
3378  +std::string(" alias=")+strAlias
3379  +std::string(" cmd=xterm pid=")+int2String(pid_cmd)
3380  +std::string("\n");
3381 
3382  }
3383 
3384  fprintf(stderr, "%s", out.c_str());
3385 
3386  result.addInt32(pid_cmd);
3387  result.addString(out.c_str());
3388 
3389  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
3390 
3391  return pid_cmd;
3392  }
3393 
3394  result.clear();
3395  result.addInt32(YARPRUN_ERROR);
3396 
3397  return YARPRUN_ERROR;
3398 }
3399 
3400 int yarp::run::Run::executeCmd(yarp::os::Bottle& msg, yarp::os::Bottle& result)
3401 {
3402  std::string strAlias(msg.find("as").asString());
3403  std::string strCmd(msg.find("cmd").toString());
3404 
3405  int pipe_child_to_parent[2];
3406  int ret_pipe_child_to_parent=yarp::run::impl::pipe(pipe_child_to_parent);
3407 
3408  if (ret_pipe_child_to_parent!=0)
3409  {
3410  int error=errno;
3411 
3412  std::string out=std::string("ABORTED: server=")+mPortName
3413  +std::string(" alias=")+strAlias
3414  +std::string(" cmd=stdio\nCan't create pipe ")+strerror(error)
3415  +std::string("\n");
3416 
3417 
3418  result.addInt32(YARPRUN_ERROR);
3419  result.addString(out.c_str());
3420  fprintf(stderr, "%s", out.c_str());
3421 
3422  return YARPRUN_ERROR;
3423  }
3424 
3425  int pid_cmd=yarp::run::impl::fork();
3426 
3427  if (IS_INVALID(pid_cmd))
3428  {
3429  int error=errno;
3430 
3431  std::string out=std::string("ABORTED: server=")+mPortName
3432  +std::string(" alias=")+strAlias
3433  +std::string(" cmd=")+strCmd
3434  +std::string("\nCan't fork command process because ")+strerror(error)
3435  +std::string("\n");
3436 
3437  result.addInt32(YARPRUN_ERROR);
3438  result.addString(out.c_str());
3439  fprintf(stderr, "%s", out.c_str());
3440 
3441  return YARPRUN_ERROR;
3442  }
3443 
3444  if (IS_NEW_PROCESS(pid_cmd)) // RUN COMMAND HERE
3445  {
3446  int saved_stderr = yarp::run::impl::dup(STDERR_FILENO);
3447  int null_file=open("/dev/null", O_WRONLY);
3448  if (null_file >= 0)
3449  {
3450  REDIRECT_TO(STDOUT_FILENO, null_file);
3451  REDIRECT_TO(STDERR_FILENO, null_file);
3452  close(null_file);
3453  }
3454  char *cmd_str=new char[strCmd.length()+1];
3455  strcpy(cmd_str, strCmd.c_str());
3456  /*
3457  int nargs=CountArgs(cmd_str);
3458  char **arg_str=new char*[nargs+1];
3459  ParseCmd(cmd_str, arg_str);
3460  arg_str[nargs]=0;
3461  */
3462  int nargs = 0;
3463  char **arg_str = new char*[C_MAXARGS + 1];
3464  parseArguments(cmd_str, &nargs, arg_str);
3465  arg_str[nargs]=nullptr;
3466 
3467  // Set the YARP_IS_YARPRUN environment variable to 1, so that the child
3468  // process will now that is running inside yarprun.
3469  yarp::conf::environment::set_string("YARP_IS_YARPRUN", "1");
3470 
3471  // Set the YARPRUN_IS_FORWARDING_LOG environment variable to 0, so that
3472  // the child process will now that yarprun is not logging the output.
3473  yarp::conf::environment::set_string("YARPRUN_IS_FORWARDING_LOG", "0");
3474 
3475  if (msg.check("env"))
3476  {
3477  auto ss = yarp::conf::string::split(msg.find("env").asString(), ';');
3478  for (const auto& s : ss) {
3479  char* szenv = new char[s.size()+1];
3480  strcpy(szenv, s.c_str());
3481  yarp::run::impl::putenv(szenv); // putenv doesn't make copy of the string
3482  }
3483  }
3484 
3485  if (msg.check("workdir"))
3486  {
3487  int ret = yarp::os::impl::chdir(msg.find("workdir").asString().c_str());
3488 
3489  if (ret!=0)
3490  {
3491  int error=errno;
3492 
3493  std::string out=std::string("ABORTED: server=")+mPortName
3494  +std::string(" alias=")+strAlias
3495  +std::string(" cmd=")+strCmd
3496  +std::string("\nCan't execute command, cannot set working directory ")+strerror(error)
3497  +std::string("\n");
3498 
3499  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
3500  fprintf(out_to_parent, "%s", out.c_str());
3501  fflush(out_to_parent);
3502  fclose(out_to_parent);
3503 
3504  REDIRECT_TO(STDERR_FILENO, saved_stderr);
3505  fprintf(stderr, "%s", out.c_str());
3506  }
3507  }
3508 
3509  int ret=YARPRUN_ERROR;
3510 
3511  char currWorkDirBuff[1024];
3512  char *currWorkDir=getcwd(currWorkDirBuff, 1024);
3513 
3514  if (currWorkDir)
3515  {
3516  char **cwd_arg_str=new char*[nargs+1];
3517  for (int i=1; i<nargs; ++i) cwd_arg_str[i]=arg_str[i];
3518  cwd_arg_str[nargs]=nullptr;
3519  cwd_arg_str[0]=new char[strlen(currWorkDir)+strlen(arg_str[0])+16];
3520 
3521 
3522  strcpy(cwd_arg_str[0], currWorkDir);
3523  strcat(cwd_arg_str[0], "/");
3524  strcat(cwd_arg_str[0], arg_str[0]);
3525 
3526  //Why removing vectors and stop threads?
3527  //exec* never returns and memory is claimed by the system
3528  //furthermore after fork() only the thread which called fork() is forked!
3529 // cleanBeforeExec();
3530 
3531  ret = yarp::run::impl::execvp(cwd_arg_str[0], cwd_arg_str);
3532 
3533  delete [] cwd_arg_str[0];
3534  delete [] cwd_arg_str;
3535  }
3536 
3537  if (ret==YARPRUN_ERROR)
3538  {
3539  //Why removing vectors and stop threads?
3540  //exec* never returns and memory is claimed by the system
3541  //furthermore after fork() only the thread which called fork() is forked!
3542  // cleanBeforeExec();
3543  ret = yarp::run::impl::execvp(arg_str[0], arg_str);
3544  }
3545 
3546  if (ret==YARPRUN_ERROR)
3547  {
3548  int error=errno;
3549 
3550  std::string out=std::string("ABORTED: server=")+mPortName
3551  +std::string(" alias=")+strAlias
3552  +std::string(" cmd=")+strCmd
3553  +std::string("\nCan't execute command because ")+strerror(error)
3554  +std::string("\n");
3555 
3556  FILE* out_to_parent=fdopen(pipe_child_to_parent[WRITE_TO_PIPE], "w");
3557  fprintf(out_to_parent, "%s", out.c_str());
3558  fflush(out_to_parent);
3559  fclose(out_to_parent);
3560 
3561  if (saved_stderr >= 0)
3562  {
3563  REDIRECT_TO(STDERR_FILENO, saved_stderr);
3564  }
3565  fprintf(stderr, "%s", out.c_str());
3566  }
3567 
3568  delete [] cmd_str;
3569  delete [] arg_str;
3570 
3571  std::exit(ret);
3572  }
3573 
3574  if (IS_PARENT_OF(pid_cmd))
3575  {
3576  auto* pInf = new YarpRunProcInfo(strAlias, mPortName, pid_cmd, nullptr, false);
3577  pInf->setCmd(strCmd);
3578  if (msg.check("env")) pInf->setEnv(msg.find("env").asString());
3579  mProcessVector->Add(pInf);
3580  char pidstr[16];
3581  sprintf(pidstr, "%d", pid_cmd);
3582 
3584 
3585  FILE* in_from_child=fdopen(pipe_child_to_parent[READ_FROM_PIPE], "r");
3586  int flags=fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_GETFL, 0);
3587  fcntl(pipe_child_to_parent[READ_FROM_PIPE], F_SETFL, flags|O_NONBLOCK);
3588 
3589  std::string out;
3590 
3591  if (in_from_child)
3592  {
3593  char buff[1024];
3594 
3595  while(true)
3596  {
3597  if (!fgets(buff, 1024, in_from_child) || ferror(in_from_child) || feof(in_from_child)) break;
3598 
3599  out+=std::string(buff);
3600  }
3601 
3602  fclose(in_from_child);
3603  }
3604 
3605  if (out.length()>0)
3606  {
3607  pid_cmd=YARPRUN_ERROR;
3608  }
3609  else
3610  {
3611  out=std::string("STARTED: server=")+mPortName
3612  +std::string(" alias=")+strAlias
3613  +std::string(" cmd=")+strCmd
3614  +std::string(" pid=")+int2String(pid_cmd)
3615  +std::string("\n");
3616  }
3617 
3618  fprintf(stderr, "%s", out.c_str());
3619 
3620  result.addInt32(pid_cmd);
3621  result.addString(out.c_str());
3622 
3623  CLOSE(pipe_child_to_parent[READ_FROM_PIPE]);
3624  CLOSE(pipe_child_to_parent[WRITE_TO_PIPE]);
3625 
3626  return pid_cmd;
3627  }
3628 
3629  result.addInt32(YARPRUN_ERROR);
3630 
3631  return YARPRUN_ERROR;
3632 }
3633 
3634 #endif
3635 
3637 // API
3639 
3640 int yarp::run::Run::start(const std::string &node, yarp::os::Property &command, std::string &keyv)
3641 {
3642  yarp::os::Bottle msg, grp, response;
3643 
3644  grp.clear();
3645  grp.addString("on");
3646  grp.addString(node.c_str());
3647  msg.addList()=grp;
3648 
3649  std::string dest_srv=node;
3650 
3651  if (command.check("stdio"))
3652  {
3653  dest_srv=std::string(command.find("stdio").asString());
3654 
3655  grp.clear();
3656  grp.addString("stdio");
3657  grp.addString(dest_srv.c_str());
3658  msg.addList()=grp;
3659 
3660  if (command.check("geometry"))
3661  {
3662  grp.clear();
3663  grp.addString("geometry");
3664  grp.addString(command.find("geometry").asString().c_str());
3665  msg.addList()=grp;
3666  }
3667 
3668  if (command.check("hold"))
3669  {
3670  grp.clear();
3671  grp.addString("hold");
3672  msg.addList()=grp;
3673  }
3674  }
3675 
3676  grp.clear();
3677  grp.addString("as");
3678  grp.addString(keyv.c_str());
3679  msg.addList()=grp;
3680 
3681  grp.clear();
3682  grp.addString("cmd");
3683  grp.addString(command.find("name").asString().c_str());
3684  grp.addString(command.find("parameters").asString().c_str());
3685  msg.addList()=grp;
3686 
3687  printf(":: %s\n", msg.toString().c_str());
3688 
3689  response=sendMsg(msg, dest_srv);
3690 
3691  char buff[16];
3692  sprintf(buff, "%d", response.get(0).asInt32());
3693  keyv=std::string(buff);
3694 
3695  return response.get(0).asInt32()>0?0:YARPRUN_ERROR;
3696 }
3697 
3698 int yarp::run::Run::sigterm(const std::string &node, const std::string &keyv)
3699 {
3700  yarp::os::Bottle msg, grp, response;
3701 
3702  grp.clear();
3703  grp.addString("on");
3704  grp.addString(node.c_str());
3705  msg.addList()=grp;
3706 
3707  grp.clear();
3708  grp.addString("sigterm");
3709  grp.addString(keyv.c_str());
3710  msg.addList()=grp;
3711 
3712  printf(":: %s\n", msg.toString().c_str());
3713 
3714  response=sendMsg(msg, node);
3715 
3716  return response.get(0).asString()=="sigterm OK"?0:YARPRUN_ERROR;
3717 }
3718 
3719 int yarp::run::Run::sigterm(const std::string &node)
3720 {
3721  yarp::os::Bottle msg, grp, response;
3722 
3723  grp.clear();
3724  grp.addString("on");
3725  grp.addString(node.c_str());
3726  msg.addList()=grp;
3727 
3728  grp.clear();
3729  grp.addString("sigtermall");
3730  msg.addList()=grp;
3731 
3732  printf(":: %s\n", msg.toString().c_str());
3733 
3734  response=sendMsg(msg, node);
3735 
3736  return response.get(0).asString()=="sigtermall OK"?0:YARPRUN_ERROR;
3737 }
3738 
3739 int yarp::run::Run::kill(const std::string &node, const std::string &keyv, int s)
3740 {
3741  yarp::os::Bottle msg, grp, response;
3742 
3743  grp.clear();
3744  grp.addString("on");
3745  grp.addString(node.c_str());
3746  msg.addList()=grp;
3747 
3748  grp.clear();
3749  grp.addString("kill");
3750  grp.addString(keyv.c_str());
3751  grp.addInt32(s);
3752  msg.addList()=grp;
3753 
3754  printf(":: %s\n", msg.toString().c_str());
3755 
3756  response=sendMsg(msg, node);
3757 
3758  return response.get(0).asString()=="kill OK"?0:YARPRUN_ERROR;
3759 }
3760 
3761 bool yarp::run::Run::isRunning(const std::string &node, std::string &keyv)
3762 {
3763  yarp::os::Bottle msg, grp, response;
3764 
3765  grp.clear();
3766  grp.addString("on");
3767  grp.addString(node.c_str());
3768  msg.addList()=grp;
3769 
3770  grp.clear();
3771  grp.addString("isrunning");
3772  grp.addString(keyv.c_str());
3773  msg.addList()=grp;
3774 
3775  printf(":: %s\n", msg.toString().c_str());
3776 
3777  response=sendMsg(msg, node);
3778 
3779  if (!response.size()) return false;
3780 
3781  return response.get(0).asString()=="running";
3782 }
3783 
3784 // end API
float t
bool ret
#define yInfo(...)
Definition: Log.h:260
#define yError(...)
Definition: Log.h:282
#define RUNLOG(msg)
int SIGNAL(int pid, int signum)
int CLOSE(int h)
#define YARPRUN_ERROR
void * HANDLE
std::string int2String(int x)
static yarp::os::Bottle parsePaths(const std::string &txt)
Definition: Run.cpp:102
static void sigchld_handler(int sig)
Definition: Run.cpp:821
void sigstdio_handler(int sig)
Definition: Run.cpp:87
void sigint_handler(int sig)
Definition: Run.cpp:462
constexpr fs::value_type slash
Definition: Run.cpp:99
constexpr auto sep
Definition: Run.cpp:100
#define WRITE_TO_PIPE
Definition: Run.cpp:63
#define READ_FROM_PIPE
Definition: Run.cpp:62
void parseArguments(char *io_pLine, int *o_pArgc, char **o_pArgv)
Breaks up a line into multiple arguments.
Definition: Run.cpp:2363
static bool fileExists(const char *fname)
Definition: Run.cpp:125
void splitLine(char *pLine, char **pArgs)
Split a line into separate words.
Definition: Run.cpp:2343
#define REDIRECT_TO(from, to)
Definition: Run.cpp:64
static RunTerminator * pTerminator
Definition: Run.cpp:85
#define C_MAXARGS
Definition: Run.cpp:46
void setEnv(const std::string &env)
void setCmd(const std::string &cmd)
bool reply(PortWriter &writer) override
Send an object as a reply to an object read from the port.
bool write(const PortWriter &writer, const PortWriter *callback=nullptr) const override
Write an object to the port.
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
std::string getName() const override
Get name of port.
void close() override
Stop port activity.
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
void append(const Bottle &alt)
Append the content of the given bottle to the current list.
Definition: Bottle.cpp:383
Bottle & addList()
Places an empty nested list in the bottle, at the end of the list.
Definition: Bottle.cpp:185
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:254
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Bottle.cpp:305
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Bottle.cpp:280
Bottle tail() const
Get all but the first element of a bottle.
Definition: Bottle.cpp:391
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
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:214
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Bottle.cpp:290
Preferences for how to communicate with a contact.
Definition: ContactStyle.h:27
bool persistent
Specify whether a requested connection should be persistent.
Definition: ContactStyle.h:66
static bool getLocalMode()
Get current value of flag "localMode", see setLocalMode function.
Definition: Network.cpp:1057
static bool connect(const std::string &src, const std::string &dest, const std::string &carrier="", bool quiet=true)
Request that an output port connect to an input port.
Definition: Network.cpp:685
static bool checkNetwork()
Check if the YARP Network is up and running.
Definition: Network.cpp:1380
static bool disconnect(const std::string &src, const std::string &dest, bool quiet)
Request that an output port disconnect from an input port.
Definition: Network.cpp:703
A class for storing options and configuration information.
Definition: Property.h:37
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Property.cpp:1054
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Property.cpp:1072
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:1018
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Property.cpp:1044
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Property.cpp:1145
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Definition: Property.cpp:1049
void fromCommand(int argc, char *argv[], bool skipFirst=true, bool wipe=true)
Interprets a list of command arguments as a list of properties.
Definition: Property.cpp:1077
A port that is specialized as an RPC client.
Definition: RpcClient.h:26
A port that is specialized as an RPC server.
Definition: RpcServer.h:27
A class for thread synchronization and mutual exclusion.
Definition: Semaphore.h:29
static void delaySystem(double seconds)
Definition: SystemClock.cpp:32
A helper class to pass the SystemInfo object around the YARP network.
yarp::os::SystemInfo::LoadInfo load
current cpu load information
yarp::os::SystemInfo::UserInfo user
current user information
yarp::os::SystemInfo::StorageInfo storage
system storage information
yarp::os::SystemInfo::PlatformInfo platform
operating system information
yarp::os::SystemInfo::MemoryInfo memory
system memory information
yarp::os::SystemInfo::ProcessorInfo processor
system processor type information
bool stop()
Stop the thread.
Definition: Thread.cpp:84
bool start()
Start the new thread running.
Definition: Thread.cpp:96
A single value (typically within a Bottle).
Definition: Value.h:47
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:207
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Value.cpp:359
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
static NameClient & getNameClient()
Get an instance of the name client.
Definition: NameClient.cpp:128
std::string send(const std::string &cmd, bool multi=true, const ContactStyle &style=ContactStyle())
Send a text message to the nameserver, and return the result.
Definition: NameClient.cpp:302
static int kill(const std::string &node, const std::string &keyv, int s)
Send a SIGNAL to an application running on a yarprun server (Linux only).
Definition: Run.cpp:3739
static int start(const std::string &node, yarp::os::Property &command, std::string &keyv)
Launch a yarprun server.
Definition: Run.cpp:3640
static bool isRunning(const std::string &node, std::string &keyv)
Get a report of all applications running on a yarprun server.
Definition: Run.cpp:3761
static int sigterm(const std::string &node, const std::string &keyv)
Terminate an application running on a yarprun server.
Definition: Run.cpp:3698
static int client(yarp::os::Property &config)
Send a property object to a run server, bundling up all the settings usually specified on the command...
Definition: Run.cpp:1235
std::string toString(const T &value)
convert an arbitrary type to string.
static constexpr char path_separator
Definition: environment.h:32
bool set_string(const std::string &key, const std::string &value)
Set a string to an environment variable.
Definition: environment.h:214
std::string get_string(const std::string &key, bool *found=nullptr)
Read a string from an environment variable.
Definition: environment.h:71
static constexpr value_type preferred_separator
Definition: filesystem.h:26
ContainerT split(const typename ContainerT::value_type &s, std::basic_regex< typename ContainerT::value_type::value_type > regex)
Utility to split a string by a separator, into a vector of strings.
Definition: string.h:30
void useSystemClock()
Configure YARP to use system time (this is the default).
Definition: Time.cpp:147
char * getcwd(char *buf, size_t size)
Portable wrapper for the getcwd() function.
Definition: Os.cpp:115
int getpid()
Portable wrapper for the getppid() function.
Definition: Os.cpp:94
int fork()
Portable wrapper for the fork() function.
Definition: Os.cpp:162
bool read(ImageOf< PixelRgb > &dest, const std::string &src, image_fileformat format=FORMAT_ANY)
Definition: ImageFile.cpp:920
bool write(const ImageOf< PixelRgb > &src, const std::string &dest, image_fileformat format=FORMAT_PPM)
Definition: ImageFile.cpp:1096
The main, catch-all namespace for YARP.
Definition: environment.h:25
yarp::os::Property environmentVars
Definition: SystemInfo.h:90
#define YARP_UNUSED(var)
Definition: api.h:159
int main(int argc, char *argv[])
Definition: yarpros.cpp:261