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::deployApplication(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
89 {
90  application->setStatusWriteBlock(false);
91 }
92 
93 void LinuxStarter::deployScenario(Data_Structure::ScenarioPtr scenario, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
94 {
95  scenario->setStatusWriteBlock(false);
96 }
97 
98 
99 void LinuxStarter::startScenario(Data_Structure::ScenarioPtr scenario, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
100 {
102  {
103  scenario->setStatusWriteBlock(false);
104  };
105  std::vector<std::future<void>> futures;
106 
107  std::vector<ApplicationInstancePtr> apps = *scenario->getApplications();
108  for (auto it = apps.begin(); it != apps.end(); it++)
109  {
110  futures.push_back(startApplicationAsync(*it, statusManager, commandLineParameters, printOnly));
111  }
112 
113  for (auto future = futures.begin(); future != futures.end(); ++future)
114  {
115  future->wait();
116  }
117 }
118 
119 
120 void LinuxStarter::startApplication(ApplicationInstancePtr app, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
121 {
123  {
124  app->setStatusWriteBlock(false);
125  };
126  if (!app->getEnabled())
127  {
128  return;
129  }
130 
131  std::filesystem::path componentFolderPath = std::filesystem::path(ArmarXDataPath::GetCachePath()) / "ComponentFiles";
132 
133  if (!std::filesystem::exists(componentFolderPath))
134  {
135  if (!std::filesystem::create_directories(componentFolderPath))
136  {
137  std::cout << "Could not create Cache folder for ScenarioManagerPlugin at " << componentFolderPath.string() << std::endl;
138  }
139  }
140 
141 
142  std::filesystem::path dependenciesFilePath = componentFolderPath / std::filesystem::path("./" + app->getScenario()->getPackage()->getName() + ".dependencies.cfg");
143 
144  if (!std::filesystem::exists(dependenciesFilePath))
145  {
147  builder.generateDependenciesCfg(Data_Structure::PackagePtr(app->getScenario()->getPackage()));
148  }
149 
150  if (!std::filesystem::exists(std::filesystem::path(app->getScenario()->getGlobalConfigPath()))
151  || !std::filesystem::exists(std::filesystem::path(app->getConfigPath()))
152  || !std::filesystem::exists(dependenciesFilePath))
153  {
154  std::cout << "Launching " << app->getName() << " in " << app->getScenario()->getPackage()->getName() << " without an needed cfg file";
155  }
156  if (!std::filesystem::exists(app->getExecutableAbsPath()))
157  {
158  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;
159  }
160  std::string args;
161  args += app->getExecutableAbsPath();
162  args += std::string(" --Ice.Config=").append(app->getScenario()->getGlobalConfigPath())
163  .append(",").append(app->getConfigPath());
164  auto configDomain = app->getConfigDomain();
165  ARMARX_CHECK_EXPRESSION(!configDomain.empty());
166  args += std::string(" --" + configDomain + ".DependenciesConfig=").append(dependenciesFilePath.string());
167  args += std::string(" --" + configDomain + ".LoggingGroup=").append(app->getScenario()->getName());
168 
169  if (!commandLineParameters.empty())
170  {
171  Ice::StringSeq additional = Split(commandLineParameters, " ");
172 
173  bool inString = false;
174  std::string inStringChar;
175  std::string toPush = "";
176  for (auto command : additional)
177  {
178  if (!inString && command.find("\"") != std::string::npos)
179  {
180  inString = true;
181  inStringChar = "\"";
182  }
183  else if (!inString && command.find("\'") != std::string::npos)
184  {
185  inString = true;
186  inStringChar = "\'";
187  }
188  else if (inString && command.find(inStringChar) != std::string::npos)
189  {
190  inString = false;
191  toPush += command;
192  args += " " + toPush;
193  toPush = "";
194  continue;
195  }
196 
197  if (inString)
198  {
199  toPush += command + " ";
200  }
201  else
202  {
203  if (command != "")
204  {
205  args += " " + command;
206  }
207  }
208  }
209  }
210  else
211  {
212  Ice::StringSeq additional = Split(this->commandLineParameters, " ");
213  for (auto arg : additional)
214  {
215  args += arg + " ";
216  }
217  }
218 
219  using namespace boost::process;
220  using namespace boost::process::initializers;
221 #if defined(BOOST_POSIX_API)
222  // prevent child to become a zombie process because the parent has reacted on a signal yet
223  signal(SIGCHLD, SIG_IGN);
224 #endif
225 
226  if (printOnly)
227  {
228  for (auto arg : args)
229  {
230  std::cout << arg;
231  }
232  std::cout << " &" << std::endl << std::endl;
233  return;
234  }
235 
236  //this apperantly prodcuces escaping errors
237  //child c = execute(set_args(args), inherit_env());
238 
239  //this is the solution
240  ARMARX_INFO << "Executing command `" << app->getExecutableAbsPath() << " "<< args;
241 
242  child c = execute(run_exe(app->getExecutableAbsPath()), set_cmd_line(args), inherit_env());
243 
244  int pid = c.pid;
245 
246  app->setPid(pid);
247  statusManager.savePid(app);
248 
249 }
250 
251 std::string LinuxStarter::getStatus(ApplicationInstancePtr application, StatusManager statusManager)
252 {
253  //if the app has no pid ask the pid manager if there is an cached Pid for this app
254  if (application->getPid() < 0)
255  {
256  application->setPid(statusManager.loadPid(application));
257 
258  //if there is none just return stopped
259  if (application->getPid() < 0)
260  {
262  }
263  else
264  {
265  std::string systemAppName = getSystemAppName(application);
266  //std::string systemAppName = get(application->getPid());
267 
268 
269  //if there is no more file there the app got stopped while checking (should only occure very rarely)
270  if (systemAppName.empty())
271  {
272  application->setPid(-1);
273  statusManager.savePid(application);
275  }
276  else
277  {
278  std::string runName = application->getExecutableName();
279  if (systemAppName.compare(runName) == 0)
280  {
281  return getStatus(application, statusManager);
282  }
283  else
284  {
285  //else delete the saved pid and inform the user
286  ARMARX_INFO_S << "The Process name with Pid (" << application->getPid() << ":" << systemAppName << ") does not correspond with app name (" << application->getName() << "). Resetting Saved Pid.";
287  application->setPid(-1);
288  statusManager.savePid(application);
290  }
291  }
292  }
293  }
294  else
295  {
296  std::string status = getSystemAppStatus(application);
298  {
299  application->setPid(-1);
300  statusManager.savePid(application);
302  }
303  else
304  {
305  return status;
306  }
307  }
308 }
309 
310 std::string LinuxStarter::getSystemAppName(Data_Structure::ApplicationInstancePtr application)
311 {
312  std::string namepluspath = get_process_name_by_pid(application->getPid());
313 
314  if (namepluspath.empty())
315  {
316  return "";
317  }
318  else
319  {
320  std::string result = namepluspath.substr(namepluspath.rfind("/") + 1);
321 
322  return result;
323  }
324 }
325 
326 std::string LinuxStarter::getSystemAppStatus(Data_Structure::ApplicationInstancePtr application)
327 {
328  std::string processFilePath = "/proc/";
329  processFilePath << application->getPid() << "/status";
330  std::ifstream t(processFilePath);
331 
332  if (!t.is_open())
333  {
335  }
336 
337  std::string stateLine;
338  std::string line;
339  while (std::getline(t, line))
340  {
341  if (Contains(line, "State:"))
342  {
343  stateLine = line;
344  break;
345  }
346  }
347  if (stateLine.empty())
348  {
350  }
351  std::string result = stateLine.substr(stateLine.find(':') + 1);
352 
353  simox::alg::trim(result);
354 
355  result = result.at(0);
356 
357  if (result == "R" || result == "S")
358  {
360  }
361  else if (result == "D" || result == "Z")
362  {
364  }
365  else if (result == "T" || result == "X")
366  {
368  }
369  else
370  {
371  ARMARX_DEBUG_S << "Uncatched app return state";
373  }
374 }
375 
376 std::future<void> LinuxStarter::startApplicationAsync(ApplicationInstancePtr application, StatusManager statusManager, const std::string& commandLineParameters, bool printOnly)
377 {
378  if (application->getPid() != -1)
379  {
380  application->setStatusWriteBlock(false);
381  return std::future<void>();
382  }
383 
384  std::packaged_task<void()> task(std::bind(&ApplicationStarter::startApplication, this, application, statusManager, commandLineParameters, printOnly)); // wrap the function
385 
386  std::future<void> result = task.get_future(); // get a future
387  std::thread t(std::move(task));
388  if (printOnly)
389  {
390  t.join(); //if only the commands should be printed then we want sync behaviour
391  }
392  else
393  {
394  t.detach();
395  }
396  return result;
397 }
398 
400 {
401  return false;
402 }
403 
405 {
406  return false;
407 }
ScenarioManager::Exec::LinuxStarter::getStatus
virtual std::string getStatus(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager) override
Returns the status of an application.
Definition: LinuxStarter.cpp:251
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::Exec::LinuxStarter::deployApplication
virtual void deployApplication(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false) override
Definition: LinuxStarter.cpp:88
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
ScenarioManager::Exec::LinuxStarter::deployScenario
virtual void deployScenario(Data_Structure::ScenarioPtr scenario, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false) override
Definition: LinuxStarter.cpp:93
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:120
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::Exec::LinuxStarter::isApplicationDeployed
virtual bool isApplicationDeployed(Data_Structure::ApplicationInstancePtr application) override
Definition: LinuxStarter.cpp:399
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:99
ArmarXDataPath.h
ScenarioManager::Exec::LinuxStarter::isScenarioDeployed
virtual bool isScenarioDeployed(Data_Structure::ScenarioPtr scenario) override
Definition: LinuxStarter.cpp:404
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.