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