LinuxStarter.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package ArmarXCore::core
19  * @author Nicola Miskowiec
20  * @date 2016
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 
26 #include "LinuxStarter.h"
27 
28 #include <errno.h>
29 #include <signal.h>
30 #include <stdlib.h>
31 
32 #include <filesystem>
33 #include <future>
34 #include <iostream>
35 #include <sstream>
36 #include <string>
37 #include <thread>
38 
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 
46 #include <boost/process.hpp>
47 
48 #include <SimoxUtility/algorithm/string/string_tools.h>
49 
55 
56 #include "../parser/DependenciesGenerator.h"
57 #include "../parser/iceparser.h"
58 
59 using namespace ScenarioManager;
60 using namespace Exec;
61 using namespace Parser;
62 //using namespace Data_Structure;
63 using namespace armarx;
64 
65 std::string
67 {
68  char* name = (char*)calloc(1024, sizeof(char));
69  if (name)
70  {
71  sprintf(name, "/proc/%d/cmdline", pid);
72  FILE* f = fopen(name, "r");
73  if (f)
74  {
75  size_t size;
76  size = fread(name, sizeof(char), 1024, f);
77  if (size > 0)
78  {
79  if ('\n' == name[size - 1])
80  {
81  name[size - 1] = '\0';
82  }
83  }
84  fclose(f);
85  }
86  std::string result(name);
87  free(name);
88  return result;
89  }
90  return std::string();
91 }
92 
93 void
95  StatusManager statusManager,
96  const std::string& commandLineParameters,
97  bool printOnly)
98 {
100  {
101  scenario->setStatusWriteBlock(false);
102  };
103  std::vector<std::future<void>> futures;
104 
105  std::vector<ApplicationInstancePtr> apps = *scenario->getApplications();
106  for (auto it = apps.begin(); it != apps.end(); it++)
107  {
108  futures.push_back(
109  startApplicationAsync(*it, statusManager, commandLineParameters, printOnly));
110  }
111 
112  for (auto future = futures.begin(); future != futures.end(); ++future)
113  {
114  future->wait();
115  }
116 }
117 
118 void
120  StatusManager statusManager,
121  const std::string& commandLineParameters,
122  bool printOnly)
123 {
125  {
126  app->setStatusWriteBlock(false);
127  };
128  if (!app->getEnabled())
129  {
130  return;
131  }
132 
133  std::filesystem::path componentFolderPath =
134  std::filesystem::path(ArmarXDataPath::GetCachePath()) / "ComponentFiles";
135 
136  if (!std::filesystem::exists(componentFolderPath))
137  {
138  if (!std::filesystem::create_directories(componentFolderPath))
139  {
140  std::cout << "Could not create Cache folder for ScenarioManagerPlugin at "
141  << componentFolderPath.string() << std::endl;
142  }
143  }
144 
145 
146  std::filesystem::path dependenciesFilePath =
147  componentFolderPath /
148  std::filesystem::path("./" + app->getScenario()->getPackage()->getName() +
149  ".dependencies.cfg");
150 
151  if (!std::filesystem::exists(dependenciesFilePath))
152  {
154  builder.generateDependenciesCfg(
155  Data_Structure::PackagePtr(app->getScenario()->getPackage()));
156  }
157 
158  if (!std::filesystem::exists(
159  std::filesystem::path(app->getScenario()->getGlobalConfigPath())) ||
160  !std::filesystem::exists(std::filesystem::path(app->getConfigPath())) ||
161  !std::filesystem::exists(dependenciesFilePath))
162  {
163  std::cout << "Launching " << app->getName() << " in "
164  << app->getScenario()->getPackage()->getName() << " without an needed cfg file";
165  }
166  if (!std::filesystem::exists(app->getExecutableAbsPath()))
167  {
168  std::cout << "\033[1;31m"
169  << "Warning: Could not launch " << app->getName() << " in "
170  << app->getScenario()->getPackage()->getName()
171  << " because the executable is missing at " << app->getPathToExecutable()
172  << "\033[0m" << std::endl;
173  }
174  std::string args;
175  args += app->getExecutableAbsPath();
176  args += std::string(" --Ice.Config=")
177  .append(app->getScenario()->getGlobalConfigPath())
178  .append(",")
179  .append(app->getConfigPath());
180  auto configDomain = app->getConfigDomain();
181  ARMARX_CHECK_EXPRESSION(!configDomain.empty());
182  args += std::string(" --" + configDomain + ".DependenciesConfig=")
183  .append(dependenciesFilePath.string());
184  args +=
185  std::string(" --" + configDomain + ".LoggingGroup=").append(app->getScenario()->getName());
186 
187  if (!commandLineParameters.empty())
188  {
189  Ice::StringSeq additional = Split(commandLineParameters, " ");
190 
191  bool inString = false;
192  std::string inStringChar;
193  std::string toPush = "";
194  for (auto command : additional)
195  {
196  if (!inString && command.find("\"") != std::string::npos)
197  {
198  inString = true;
199  inStringChar = "\"";
200  }
201  else if (!inString && command.find("\'") != std::string::npos)
202  {
203  inString = true;
204  inStringChar = "\'";
205  }
206  else if (inString && command.find(inStringChar) != std::string::npos)
207  {
208  inString = false;
209  toPush += command;
210  args += " " + toPush;
211  toPush = "";
212  continue;
213  }
214 
215  if (inString)
216  {
217  toPush += command + " ";
218  }
219  else
220  {
221  if (command != "")
222  {
223  args += " " + command;
224  }
225  }
226  }
227  }
228  else
229  {
230  Ice::StringSeq additional = Split(this->commandLineParameters, " ");
231  for (auto arg : additional)
232  {
233  args += arg + " ";
234  }
235  }
236 
237  using namespace boost::process;
238  using namespace boost::process::initializers;
239 #if defined(BOOST_POSIX_API)
240  // prevent child to become a zombie process because the parent has reacted on a signal yet
241  signal(SIGCHLD, SIG_IGN);
242 #endif
243 
244  if (printOnly)
245  {
246  for (auto arg : args)
247  {
248  std::cout << arg;
249  }
250  std::cout << " &" << std::endl << std::endl;
251  return;
252  }
253 
254  //this apperantly prodcuces escaping errors
255  //child c = execute(set_args(args), inherit_env());
256 
257  //this is the solution
258  ARMARX_INFO << "Executing command `" << app->getExecutableAbsPath() << " " << args;
259 
260  child c = execute(run_exe(app->getExecutableAbsPath()), set_cmd_line(args), inherit_env());
261 
262  int pid = c.pid;
263 
264  app->setPid(pid);
265  statusManager.savePid(app);
266 }
267 
268 std::string
270 {
271  //if the app has no pid ask the pid manager if there is an cached Pid for this app
272  if (application->getPid() < 0)
273  {
274  application->setPid(statusManager.loadPid(application));
275 
276  //if there is none just return stopped
277  if (application->getPid() < 0)
278  {
280  }
281  else
282  {
283  std::string systemAppName = getSystemAppName(application);
284  //std::string systemAppName = get(application->getPid());
285 
286 
287  //if there is no more file there the app got stopped while checking (should only occure very rarely)
288  if (systemAppName.empty())
289  {
290  application->setPid(-1);
291  statusManager.savePid(application);
293  }
294  else
295  {
296  std::string runName = application->getExecutableName();
297  if (systemAppName.compare(runName) == 0)
298  {
299  return getStatus(application, statusManager);
300  }
301  else
302  {
303  //else delete the saved pid and inform the user
304  ARMARX_INFO_S << "The Process name with Pid (" << application->getPid() << ":"
305  << systemAppName << ") does not correspond with app name ("
306  << application->getName() << "). Resetting Saved Pid.";
307  application->setPid(-1);
308  statusManager.savePid(application);
310  }
311  }
312  }
313  }
314  else
315  {
316  std::string status = getSystemAppStatus(application);
318  {
319  application->setPid(-1);
320  statusManager.savePid(application);
322  }
323  else
324  {
325  return status;
326  }
327  }
328 }
329 
330 std::string
331 LinuxStarter::getSystemAppName(Data_Structure::ApplicationInstancePtr application)
332 {
333  std::string namepluspath = get_process_name_by_pid(application->getPid());
334 
335  if (namepluspath.empty())
336  {
337  return "";
338  }
339  else
340  {
341  std::string result = namepluspath.substr(namepluspath.rfind("/") + 1);
342 
343  return result;
344  }
345 }
346 
347 std::string
348 LinuxStarter::getSystemAppStatus(Data_Structure::ApplicationInstancePtr application)
349 {
350  std::string processFilePath = "/proc/";
351  processFilePath << application->getPid() << "/status";
352  std::ifstream t(processFilePath);
353 
354  if (!t.is_open())
355  {
357  }
358 
359  std::string stateLine;
360  std::string line;
361  while (std::getline(t, line))
362  {
363  if (Contains(line, "State:"))
364  {
365  stateLine = line;
366  break;
367  }
368  }
369  if (stateLine.empty())
370  {
372  }
373  std::string result = stateLine.substr(stateLine.find(':') + 1);
374 
375  simox::alg::trim(result);
376 
377  result = result.at(0);
378 
379  if (result == "R" || result == "S")
380  {
382  }
383  else if (result == "D" || result == "Z")
384  {
386  }
387  else if (result == "T" || result == "X")
388  {
390  }
391  else
392  {
393  ARMARX_DEBUG_S << "Uncatched app return state";
395  }
396 }
397 
398 std::future<void>
399 LinuxStarter::startApplicationAsync(ApplicationInstancePtr application,
400  StatusManager statusManager,
401  const std::string& commandLineParameters,
402  bool printOnly)
403 {
404  if (application->getPid() != -1)
405  {
406  application->setStatusWriteBlock(false);
407  return std::future<void>();
408  }
409 
410  std::packaged_task<void()> task(std::bind(&ApplicationStarter::startApplication,
411  this,
412  application,
413  statusManager,
414  commandLineParameters,
415  printOnly)); // wrap the function
416 
417  std::future<void> result = task.get_future(); // get a future
418  std::thread t(std::move(task));
419  if (printOnly)
420  {
421  t.join(); //if only the commands should be printed then we want sync behaviour
422  }
423  else
424  {
425  t.detach();
426  }
427  return result;
428 }
ScenarioManager::Exec::LinuxStarter::getStatus
virtual std::string getStatus(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager) override
Returns the status of an application.
Definition: LinuxStarter.cpp:269
boost::process
Definition: mitigate.hpp:25
process.hpp
ScenarioManager::Data_Structure::ApplicationInstancePtr
std::shared_ptr< ApplicationInstance > ApplicationInstancePtr
Definition: ApplicationInstance.h:33
ScenarioManager::Data_Structure::ScenarioPtr
std::shared_ptr< Scenario > ScenarioPtr
Definition: Scenario.h:35
ScenarioManager::Parser::DependenciesGenerator::generateDependenciesCfg
void generateDependenciesCfg(ScenarioManager::Data_Structure::PackagePtr package)
Definition: DependenciesGenerator.cpp:91
ScenarioManager::StatusManager::loadPid
int loadPid(Data_Structure::ApplicationInstancePtr app)
Definition: StatusManager.cpp:19
OnScopeExit.h
execute
Use of this software is granted under one of the following two to be chosen freely by the user Boost Software License Version Marcin Kalicinski Permission is hereby free of to any person or organization obtaining a copy of the software and accompanying documentation covered by this execute
Definition: license.txt:12
ScenarioManager::StatusManager::savePid
void savePid(Data_Structure::ApplicationInstancePtr app)
Definition: StatusManager.cpp:55
armarx::Contains
bool Contains(const ContainerType &container, const ElementType &searchElement)
Definition: algorithm.h:330
armarx::Split
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
Definition: StringHelperTemplates.h:36
boost::process::windows::initializers::set_cmd_line
set_cmd_line_< std::string > set_cmd_line(const char *s)
Definition: set_cmd_line.hpp:59
ScenarioManager::Data_Structure::ApplicationStatus::Stopped
static const std::string Stopped
Definition: ApplicationInstance.h:47
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:46
get_process_name_by_pid
std::string get_process_name_by_pid(const int pid)
Definition: LinuxStarter.cpp:66
ARMARX_ON_SCOPE_EXIT
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
Definition: OnScopeExit.h:120
ScenarioManager::Data_Structure::ApplicationStatus::Running
static const std::string Running
Definition: ApplicationInstance.h:48
ApplicationInstancePtr
std::shared_ptr< ScenarioManager::Data_Structure::ApplicationInstance > ApplicationInstancePtr
Definition: StopStrategy.h:8
armarx::status
status
Definition: FiniteStateMachine.h:244
boost::process::posix::initializers::run_exe
run_exe_ run_exe(const char *s)
Definition: run_exe.hpp:47
ARMARX_DEBUG_S
#define ARMARX_DEBUG_S
Definition: Logging.h:205
ScenarioManager::Data_Structure::PackagePtr
std::shared_ptr< Package > PackagePtr
Definition: Package.h:122
ScenarioManager::Parser::DependenciesGenerator
Definition: DependenciesGenerator.h:40
ScenarioManager::Data_Structure::ApplicationStatus::Unknown
static const std::string Unknown
Definition: ApplicationInstance.h:51
ScenarioManager::StatusManager
Definition: StatusManager.h:7
ScenarioManager::Exec::LinuxStarter::startApplication
virtual void startApplication(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false) override
Starts an application.
Definition: LinuxStarter.cpp:119
ExpressionException.h
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
ScenarioManager
Definition: Application.cpp:180
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:202
Logging.h
ScenarioManager::Exec::LinuxStarter::startScenario
virtual void startScenario(Data_Structure::ScenarioPtr scenario, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false) override
Definition: LinuxStarter.cpp:94
ArmarXDataPath.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
Exception.h
LinuxStarter.h
ScenarioManager::Exec::ApplicationStarter::startApplication
virtual void startApplication(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false)=0
Starts an application.