Executor.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 "Executor.h"
27
28#include <signal.h>
29
30#include <chrono>
31#include <filesystem>
32#include <thread>
33
34#include <sys/types.h>
35
36#include <boost/process.hpp>
37
41
43#include "ProcessUtil.h"
44
45using namespace ScenarioManager;
46
52
54Exec::Executor::lifecycleFor(const ApplicationInstancePtr& application)
55{
56 std::lock_guard<std::mutex> guard(lifecyclesMutex);
57
58 ApplicationLifecyclePtr& lifecycle = lifecycles[application.get()];
59 if (!lifecycle)
60 {
61 lifecycle = std::make_shared<ApplicationLifecycle>(application, statusManager);
62 }
63 return lifecycle;
64}
65
66void
68 bool printOnly,
69 const std::string& commandLineParameters)
70{
71 const Data_Structure::ScenarioPtr scenario = application->getScenario();
72 lifecycleFor(application)
74 getStarter(scenario),
75 getStopStrategy(scenario),
76 commandLineParameters,
77 printOnly);
78}
79
80void
82{
83 const Data_Structure::ScenarioPtr scenario = application->getScenario();
84 lifecycleFor(application)
85 ->request(Data_Structure::Intent::Stop, getStarter(scenario), getStopStrategy(scenario));
86}
87
88void
90{
91 const Data_Structure::ScenarioPtr scenario = application->getScenario();
92 lifecycleFor(application)
94 getStarter(scenario),
95 getStopStrategy(scenario),
96 "",
97 printOnly);
98}
99
100void
102{
103 lifecycleFor(application)->wait();
104}
105
106void
108{
109 for (const ApplicationInstancePtr& application : *scenario->getApplications())
110 {
111 waitForApplication(application);
112 }
113}
114
115std::string
117{
118 // An application with an intent in flight is shown as waiting rather than by whatever its
119 // process happens to be doing halfway through a start or a stop.
120 if (application->getPendingIntent() != Data_Structure::Intent::None)
121 {
123 }
124 return getStarter(application->getScenario())->getStatus(application, statusManager);
125}
126
127void
128Exec::Executor::requestForScenario(const Data_Structure::ScenarioPtr& scenario,
130 const std::string& commandLineParameters,
131 bool printOnly)
132{
133 // Every application gets its own request, so a scenario operation is just a bulk of them and
134 // an individual click on one application composes with it under the same last-intent-wins
135 // rule. The applications are launched in no particular order; the LaunchMutex keeps the
136 // spawning itself from happening all at once.
137 for (const ApplicationInstancePtr& application : *scenario->getApplications())
138 {
139 lifecycleFor(application)
140 ->request(intent,
141 getStarter(scenario),
142 getStopStrategy(scenario),
143 commandLineParameters,
144 printOnly);
145 }
146}
147
148void
150 bool printOnly,
151 const std::string& commandLineParameters)
152{
153 requestForScenario(scenario, Data_Structure::Intent::Start, commandLineParameters, printOnly);
154}
155
156void
158{
159 requestForScenario(scenario, Data_Structure::Intent::Stop, "", false);
160}
161
162void
164{
165 requestForScenario(scenario, Data_Structure::Intent::Restart, "", printOnly);
166}
167
168void
169Exec::Executor::generatePropertiesXml(const Data_Structure::ApplicationPtr& application,
170 const std::filesystem::path& xmlFilePath)
171{
172 // Launches the application binary once so that it dumps its property definitions.
173 //
174 // This used to be a system() call. Once SIGCHLD is set to SIG_IGN - which the starter does
175 // process wide - system() still waits for the command, but can no longer reap it and so always
176 // returns -1: every generation was reported as failed even when it had worked, and a genuine
177 // failure was indistinguishable from success. Spawn the child directly and watch the process
178 // instead, which does not depend on the signal disposition, bounds the wait, and lets us check
179 // that something was actually written.
180 const std::string executable = application->getExecutableAbsPath();
181 const std::string commandLine = executable + " -p -f xml -o " + xmlFilePath.string();
182
183 using namespace boost::process;
184 using namespace boost::process::initializers;
185
186 int pid = -1;
187 try
188 {
189 child c = execute(run_exe(executable), set_cmd_line(commandLine), inherit_env());
190 pid = c.pid;
191 }
192 catch (const std::exception& e)
193 {
194 ARMARX_WARNING << "Failed to generate properties xml for " << application->getName()
195 << ": " << e.what() << "\nCommand was: " << commandLine;
196 return;
197 }
198
199 if (!ProcessUtil::WaitForExit(pid, PropertyGenerationTimeout))
200 {
201 ARMARX_WARNING << "Generating the properties xml for " << application->getName()
202 << " did not finish within " << PropertyGenerationTimeout.count()
203 << " ms, giving up on it.\nCommand was: " << commandLine;
204 ::kill(pid, SIGKILL);
205 return;
206 }
207
208 std::error_code ec;
209 if (!std::filesystem::exists(xmlFilePath, ec) ||
210 std::filesystem::file_size(xmlFilePath, ec) == 0)
211 {
212 ARMARX_WARNING << "Failed to generate properties xml for " << application->getName()
213 << "\nCommand was: " << commandLine;
214 }
215}
216
217void
219 std::string path,
220 bool reload,
221 bool set)
222{
223 std::filesystem::path xmlFilePath =
224 std::filesystem::path(path) / std::filesystem::path(application->getPackageName() + "." +
225 application->getName() + ".xml");
226
227 if (reload || !std::filesystem::exists(xmlFilePath))
228 {
229 // The properties can only be dumped by running the binary, so there is nothing to do if it
230 // is not there. Without this check we would fork and then wait out the whole generation
231 // timeout for an application that can never produce anything.
232 application->updateFound();
233 if (!application->getFound())
234 {
235 ARMARX_WARNING << "Cannot generate the properties xml for " << application->getName()
236 << ": the binary is missing at " << application->getExecutableAbsPath();
237 return;
238 }
239
240 std::filesystem::create_directories(path);
241 generatePropertiesXml(application, xmlFilePath);
242 }
243 else
244 {
245 application->updateFound();
246 if (!application->getFound())
247 {
248 return;
249 }
250 //if executable more recent that xml reload xml
251 auto xmlDate = (std::filesystem::last_write_time(xmlFilePath));
252
253 auto execDate =
254 std::filesystem::last_write_time(application->getPathToExecutable().append("/").append(
255 application->getExecutableName()));
256
257 if (execDate > xmlDate)
258 {
259 loadAndSetCachedProperties(application, path, true, set);
260 return;
261 }
262 }
263
264 if (set)
265 {
266 Parser::IceParser parser;
267 application->setProperties(parser.loadFromXml(xmlFilePath.string()));
268 }
269}
270
271void
273{
274 defaultStopStrategy = strategy;
275}
276
277void
279{
280 defaultStartStrategy = appStarter;
281}
282
283void
285{
286 this->stopStrategy[scenario] = strategy;
287}
288
289void
291{
292 this->starter[scenario] = appStarter;
293}
294
297{
298 if (starter.count(scenario) == 0)
299 {
300 return defaultStartStrategy;
301 }
302 return starter[scenario];
303}
304
307{
308 if (stopStrategy.count(scenario) == 0)
309 {
310 return defaultStopStrategy;
311 }
312 return stopStrategy[scenario];
313}
314
317{
318 return defaultStopStrategy;
319}
std::shared_ptr< ScenarioManager::Data_Structure::ApplicationInstance > ApplicationInstancePtr
Definition StopStrategy.h:9
constexpr T c
StopStrategyPtr getStopStrategy(Data_Structure::ScenarioPtr scenario)
Definition Executor.cpp:306
void restartScenario(Data_Structure::ScenarioPtr scenario, bool printOnly=false)
Restarts a scenario.
Definition Executor.cpp:163
void setDefaultStarter(ApplicationStarterPtr appStarter)
Sets the appStarter this Executor uses to start applications and request statuses.
Definition Executor.cpp:278
static void loadAndSetCachedProperties(Data_Structure::ApplicationPtr application, std::string path, bool reload=false, bool set=true)
Generates an XML file of the given application and saves it in the specified path.
Definition Executor.cpp:218
std::string getApplicationStatus(Data_Structure::ApplicationInstancePtr application)
Returns the status of an application.
Definition Executor.cpp:116
void waitForScenario(const Data_Structure::ScenarioPtr &scenario)
Blocks until no application of the scenario has an operation requested or running.
Definition Executor.cpp:107
void restartApplication(Data_Structure::ApplicationInstancePtr application, bool printOnly=false)
Requests that an application be restarted.
Definition Executor.cpp:89
void startScenario(Data_Structure::ScenarioPtr scenario, bool printOnly=false, const std::string &commandLineParameters="")
Starts a scenario.
Definition Executor.cpp:149
Executor(StopStrategyPtr strategy, ApplicationStarterPtr starter)
Constructor that sets StopStrategy and ApplicationStarter.
Definition Executor.cpp:47
void stopScenario(Data_Structure::ScenarioPtr scenario)
Stops a scenario.
Definition Executor.cpp:157
void setDefaultStopStrategy(StopStrategyPtr strategy)
Sets the strategy this Executor uses to stop applications.
Definition Executor.cpp:272
void startApplication(Data_Structure::ApplicationInstancePtr application, bool printOnly=false, const std::string &commandLineParameters="")
Requests that an application be started.
Definition Executor.cpp:67
void stopApplication(Data_Structure::ApplicationInstancePtr application)
Requests that an application be stopped.
Definition Executor.cpp:81
StopStrategyPtr getDefaultStopStrategy()
Definition Executor.cpp:316
void setStarter(ApplicationStarterPtr appStarter, Data_Structure::ScenarioPtr scenario)
Definition Executor.cpp:290
ApplicationStarterPtr getStarter(Data_Structure::ScenarioPtr scenario)
Definition Executor.cpp:296
void setStopStrategy(StopStrategyPtr strategy, Data_Structure::ScenarioPtr scenario)
Definition Executor.cpp:284
void waitForApplication(const Data_Structure::ApplicationInstancePtr &application)
Blocks until the application has no operation requested or running any more.
Definition Executor.cpp:101
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
std::shared_ptr< Scenario > ScenarioPtr
Definition Scenario.h:35
Intent
What the ScenarioManager is currently trying to do with an application.
std::shared_ptr< Application > ApplicationPtr
bool WaitForExit(int pid, std::chrono::milliseconds timeout, std::chrono::milliseconds pollInterval)
Blocks until the process with the given pid has disappeared, at most for timeout.
std::shared_ptr< StopStrategy > StopStrategyPtr
Definition Executor.h:48
std::shared_ptr< ApplicationStarter > ApplicationStarterPtr
std::shared_ptr< ApplicationLifecycle > ApplicationLifecyclePtr
Convenience header which includes all public Boost.Process header files.