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
57#include "../parser/iceparser.h"
58
59using namespace ScenarioManager;
60using namespace Exec;
61using namespace Parser;
62//using namespace Data_Structure;
63using namespace armarx;
64
65std::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
93void
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
118void
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 {
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
268std::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
330std::string
331LinuxStarter::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
347std::string
348LinuxStarter::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
398std::future<void>
399LinuxStarter::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,
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}
std::string get_process_name_by_pid(const int pid)
std::shared_ptr< ScenarioManager::Data_Structure::ApplicationInstance > ApplicationInstancePtr
Definition StopStrategy.h:7
constexpr T c
virtual void startApplication(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false)=0
Starts an application.
virtual std::string getStatus(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager) override
Returns the status of an application.
virtual void startScenario(Data_Structure::ScenarioPtr scenario, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false) override
virtual void startApplication(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager, const std::string &commandLineParameters="", bool printOnly=false) override
Starts an application.
void generateDependenciesCfg(ScenarioManager::Data_Structure::PackagePtr package)
void savePid(Data_Structure::ApplicationInstancePtr app)
int loadPid(Data_Structure::ApplicationInstancePtr app)
static std::string GetCachePath()
The base Cache directory of ArmarX.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_DEBUG_S
The logging level for output that is only interesting while debugging.
Definition Logging.h:205
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
std::shared_ptr< Scenario > ScenarioPtr
Definition Scenario.h:35
std::shared_ptr< Package > PackagePtr
Definition Package.h:122
std::shared_ptr< ApplicationInstance > ApplicationInstancePtr
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
bool Contains(const ContainerType &container, const ElementType &searchElement)
Definition algorithm.h:330
Convenience header which includes all public Boost.Process header files.