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 #include "../parser/iceparser.h"
28 #include "../parser/DependenciesGenerator.h"
34 
35 #include <SimoxUtility/algorithm/string/string_tools.h>
36 #include <boost/process.hpp>
37 
38 #include <filesystem>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <syslog.h>
46 #include <fcntl.h>
47 #include <future>
48 #include <thread>
49 #include <string>
50 #include <iostream>
51 #include <sstream>
52 #include <iostream>
53 #include <sys/time.h>
54 
55 using namespace ScenarioManager;
56 using namespace Exec;
57 using namespace Parser;
58 //using namespace Data_Structure;
59 using namespace armarx;
60 
61 std::string get_process_name_by_pid(const int pid)
62 {
63  char* name = (char*)calloc(1024, sizeof(char));
64  if (name)
65  {
66  sprintf(name, "/proc/%d/cmdline", pid);
67  FILE* f = fopen(name, "r");
68  if (f)
69  {
70  size_t size;
71  size = fread(name, sizeof(char), 1024, f);
72  if (size > 0)
73  {
74  if ('\n' == name[size - 1])
75  {
76  name[size - 1] = '\0';
77  }
78  }
79  fclose(f);
80  }
81  std::string result(name);
82  free(name);
83  return result;
84  }
85  return std::string();
86 }
87 
88 void LinuxStarter::startScenario(Data_Structure::ScenarioPtr scenario, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
89 {
91  {
92  scenario->setStatusWriteBlock(false);
93  };
94  std::vector<std::future<void>> futures;
95 
96  std::vector<ApplicationInstancePtr> apps = *scenario->getApplications();
97  for (auto it = apps.begin(); it != apps.end(); it++)
98  {
99  futures.push_back(startApplicationAsync(*it, statusManager, commandLineParameters, printOnly));
100  }
101 
102  for (auto future = futures.begin(); future != futures.end(); ++future)
103  {
104  future->wait();
105  }
106 }
107 
108 
109 void LinuxStarter::startApplication(ApplicationInstancePtr app, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
110 {
112  {
113  app->setStatusWriteBlock(false);
114  };
115  if (!app->getEnabled())
116  {
117  return;
118  }
119 
120  std::filesystem::path componentFolderPath = std::filesystem::path(ArmarXDataPath::GetCachePath()) / "ComponentFiles";
121 
122  if (!std::filesystem::exists(componentFolderPath))
123  {
124  if (!std::filesystem::create_directories(componentFolderPath))
125  {
126  std::cout << "Could not create Cache folder for ScenarioManagerPlugin at " << componentFolderPath.string() << std::endl;
127  }
128  }
129 
130 
131  std::filesystem::path dependenciesFilePath = componentFolderPath / std::filesystem::path("./" + app->getScenario()->getPackage()->getName() + ".dependencies.cfg");
132 
133  if (!std::filesystem::exists(dependenciesFilePath))
134  {
136  builder.generateDependenciesCfg(Data_Structure::PackagePtr(app->getScenario()->getPackage()));
137  }
138 
139  if (!std::filesystem::exists(std::filesystem::path(app->getScenario()->getGlobalConfigPath()))
140  || !std::filesystem::exists(std::filesystem::path(app->getConfigPath()))
141  || !std::filesystem::exists(dependenciesFilePath))
142  {
143  std::cout << "Launching " << app->getName() << " in " << app->getScenario()->getPackage()->getName() << " without an needed cfg file";
144  }
145  if (!std::filesystem::exists(app->getExecutableAbsPath()))
146  {
147  std::cout << "\033[1;31m" << "Warning: Could not launch " << app->getName() << " in " << app->getScenario()->getPackage()->getName() << " because the executable is missing at " << app->getPathToExecutable() << "\033[0m" << std::endl;
148  }
149  std::string args;
150  args += app->getExecutableAbsPath();
151  args += std::string(" --Ice.Config=").append(app->getScenario()->getGlobalConfigPath())
152  .append(",").append(app->getConfigPath());
153  auto configDomain = app->getConfigDomain();
154  ARMARX_CHECK_EXPRESSION(!configDomain.empty());
155  args += std::string(" --" + configDomain + ".DependenciesConfig=").append(dependenciesFilePath.string());
156  args += std::string(" --" + configDomain + ".LoggingGroup=").append(app->getScenario()->getName());
157 
158  if (!commandLineParameters.empty())
159  {
160  Ice::StringSeq additional = Split(commandLineParameters, " ");
161 
162  bool inString = false;
163  std::string inStringChar;
164  std::string toPush = "";
165  for (auto command : additional)
166  {
167  if (!inString && command.find("\"") != std::string::npos)
168  {
169  inString = true;
170  inStringChar = "\"";
171  }
172  else if (!inString && command.find("\'") != std::string::npos)
173  {
174  inString = true;
175  inStringChar = "\'";
176  }
177  else if (inString && command.find(inStringChar) != std::string::npos)
178  {
179  inString = false;
180  toPush += command;
181  args += " " + toPush;
182  toPush = "";
183  continue;
184  }
185 
186  if (inString)
187  {
188  toPush += command + " ";
189  }
190  else
191  {
192  if (command != "")
193  {
194  args += " " + command;
195  }
196  }
197  }
198  }
199  else
200  {
201  Ice::StringSeq additional = Split(this->commandLineParameters, " ");
202  for (auto arg : additional)
203  {
204  args += arg + " ";
205  }
206  }
207 
208  using namespace boost::process;
209  using namespace boost::process::initializers;
210 #if defined(BOOST_POSIX_API)
211  // prevent child to become a zombie process because the parent has reacted on a signal yet
212  signal(SIGCHLD, SIG_IGN);
213 #endif
214 
215  if (printOnly)
216  {
217  for (auto arg : args)
218  {
219  std::cout << arg;
220  }
221  std::cout << " &" << std::endl << std::endl;
222  return;
223  }
224 
225  //this apperantly prodcuces escaping errors
226  //child c = execute(set_args(args), inherit_env());
227 
228  //this is the solution
229  ARMARX_INFO << "Executing command `" << app->getExecutableAbsPath() << " "<< args;
230 
231  child c = execute(run_exe(app->getExecutableAbsPath()), set_cmd_line(args), inherit_env());
232 
233  int pid = c.pid;
234 
235  app->setPid(pid);
236  statusManager.savePid(app);
237 
238 }
239 
240 std::string LinuxStarter::getStatus(ApplicationInstancePtr application, StatusManager statusManager)
241 {
242  //if the app has no pid ask the pid manager if there is an cached Pid for this app
243  if (application->getPid() < 0)
244  {
245  application->setPid(statusManager.loadPid(application));
246 
247  //if there is none just return stopped
248  if (application->getPid() < 0)
249  {
251  }
252  else
253  {
254  std::string systemAppName = getSystemAppName(application);
255  //std::string systemAppName = get(application->getPid());
256 
257 
258  //if there is no more file there the app got stopped while checking (should only occure very rarely)
259  if (systemAppName.empty())
260  {
261  application->setPid(-1);
262  statusManager.savePid(application);
264  }
265  else
266  {
267  std::string runName = application->getExecutableName();
268  if (systemAppName.compare(runName) == 0)
269  {
270  return getStatus(application, statusManager);
271  }
272  else
273  {
274  //else delete the saved pid and inform the user
275  ARMARX_INFO_S << "The Process name with Pid (" << application->getPid() << ":" << systemAppName << ") does not correspond with app name (" << application->getName() << "). Resetting Saved Pid.";
276  application->setPid(-1);
277  statusManager.savePid(application);
279  }
280  }
281  }
282  }
283  else
284  {
285  std::string status = getSystemAppStatus(application);
287  {
288  application->setPid(-1);
289  statusManager.savePid(application);
291  }
292  else
293  {
294  return status;
295  }
296  }
297 }
298 
299 std::string LinuxStarter::getSystemAppName(Data_Structure::ApplicationInstancePtr application)
300 {
301  std::string namepluspath = get_process_name_by_pid(application->getPid());
302 
303  if (namepluspath.empty())
304  {
305  return "";
306  }
307  else
308  {
309  std::string result = namepluspath.substr(namepluspath.rfind("/") + 1);
310 
311  return result;
312  }
313 }
314 
315 std::string LinuxStarter::getSystemAppStatus(Data_Structure::ApplicationInstancePtr application)
316 {
317  std::string processFilePath = "/proc/";
318  processFilePath << application->getPid() << "/status";
319  std::ifstream t(processFilePath);
320 
321  if (!t.is_open())
322  {
324  }
325 
326  std::string stateLine;
327  std::string line;
328  while (std::getline(t, line))
329  {
330  if (Contains(line, "State:"))
331  {
332  stateLine = line;
333  break;
334  }
335  }
336  if (stateLine.empty())
337  {
339  }
340  std::string result = stateLine.substr(stateLine.find(':') + 1);
341 
342  simox::alg::trim(result);
343 
344  result = result.at(0);
345 
346  if (result == "R" || result == "S")
347  {
349  }
350  else if (result == "D" || result == "Z")
351  {
353  }
354  else if (result == "T" || result == "X")
355  {
357  }
358  else
359  {
360  ARMARX_DEBUG_S << "Uncatched app return state";
362  }
363 }
364 
365 std::future<void> LinuxStarter::startApplicationAsync(ApplicationInstancePtr application, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
366 {
367  if (application->getPid() != -1)
368  {
369  application->setStatusWriteBlock(false);
370  return std::future<void>();
371  }
372 
373  std::packaged_task<void()> task(std::bind(&ApplicationStarter::startApplication, this, application, statusManager, commandLineParameters, printOnly)); // wrap the function
374 
375  std::future<void> result = task.get_future(); // get a future
376  std::thread t(std::move(task));
377  if (printOnly)
378  {
379  t.join(); //if only the commands should be printed then we want sync behaviour
380  }
381  else
382  {
383  t.detach();
384  }
385  return result;
386 }
ScenarioManager::Exec::LinuxStarter::getStatus
virtual std::string getStatus(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager) override
Returns the status of an application.
Definition: LinuxStarter.cpp:240
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:36
ScenarioManager::Parser::DependenciesGenerator::generateDependenciesCfg
void generateDependenciesCfg(ScenarioManager::Data_Structure::PackagePtr package)
Definition: DependenciesGenerator.cpp:88
ScenarioManager::StatusManager::loadPid
int loadPid(Data_Structure::ApplicationInstancePtr app)
Definition: StatusManager.cpp:20
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:56
armarx::Contains
bool Contains(const ContainerType &container, const ElementType &searchElement)
Definition: algorithm.h:295
armarx::Split
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
Definition: StringHelperTemplates.h:35
boost::process::windows::initializers::set_cmd_line
set_cmd_line_< std::string > set_cmd_line(const char *s)
Definition: set_cmd_line.hpp:55
ScenarioManager::Data_Structure::ApplicationStatus::Stopped
static const std::string Stopped
Definition: ApplicationInstance.h:47
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:43
get_process_name_by_pid
std::string get_process_name_by_pid(const int pid)
Definition: LinuxStarter.cpp:61
ARMARX_ON_SCOPE_EXIT
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
Definition: OnScopeExit.h:112
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:7
armarx::status
status
Definition: FiniteStateMachine.h:259
boost::process::posix::initializers::run_exe
run_exe_ run_exe(const char *s)
Definition: run_exe.hpp:44
ARMARX_DEBUG_S
#define ARMARX_DEBUG_S
Definition: Logging.h:198
ScenarioManager::Data_Structure::PackagePtr
std::shared_ptr< Package > PackagePtr
Definition: Package.h:121
ScenarioManager::Parser::DependenciesGenerator
Definition: DependenciesGenerator.h:38
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:109
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:166
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:195
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:88
ArmarXDataPath.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
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.