Project
Loading...
Searching...
No Matches
GeneratorFileOrCmd.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
16// For fifo's and system call
17#include <cstdlib>
18#include <sys/types.h> // POSIX only
19#include <sys/stat.h> // POSIX only
20#include <csignal>
21#include <sys/wait.h>
22#include <cstdio>
23// For filesystem operations
24#include <filesystem>
25// Waits
26#include <thread>
27// Log messages
28#include <fairlogger/Logger.h>
29#include <SimConfig/SimConfig.h>
30
31namespace
32{
33std::string ltrim(const std::string& s, const std::string& what = "\" ' ")
34{
35 auto start = s.find_first_not_of(what);
36 if (start == std::string::npos) {
37 return "";
38 }
39
40 return s.substr(start);
41}
42
43std::string rtrim(const std::string& s, const std::string& what = "\"' ")
44{
45 auto end = s.find_last_not_of(what);
46 return s.substr(0, end + 1);
47}
48
49std::string trim(const std::string& s, const std::string& what = "\"' ")
50{
51 return rtrim(ltrim(s, what), what);
52}
53} // namespace
54
55namespace o2
56{
57namespace eventgen
58{
59// -----------------------------------------------------------------
61 const conf::SimConfig& config)
62{
63 setFileNames(param.fileNames);
64 setCmd(param.cmd);
65 setOutputSwitch(trim(param.outputSwitch));
66 setSeedSwitch(trim(param.seedSwitch));
67 setBmaxSwitch(trim(param.bMaxSwitch));
68 setNEventsSwitch(trim(param.nEventsSwitch));
69 setBackgroundSwitch(trim(param.backgroundSwitch));
70 setSeed(config.getStartSeed());
71 setNEvents(config.getNEvents());
72 setBmax(config.getBMax());
73}
74// -----------------------------------------------------------------
75// Switches are permanently set to default values
77 const conf::SimConfig& config)
78{
79 setFileNames(param.fileNames);
80 setCmd(param.cmd);
81 setOutputSwitch(">");
82 setSeedSwitch("-s");
83 setBmaxSwitch("-b");
84 setNEventsSwitch("-n");
86 setSeed(config.getStartSeed());
87 setNEvents(config.getNEvents());
88 setBmax(config.getBMax());
89}
90// -----------------------------------------------------------------
91void GeneratorFileOrCmd::setFileNames(const std::string& filenames)
92{
93 std::stringstream s(filenames);
94 std::string f;
95 while (std::getline(s, f, ',')) {
96 mFileNames.push_back(f);
97 }
98}
99// -----------------------------------------------------------------
101{
102 std::string fileName = mFileNames.front();
103 std::stringstream s;
104 s << mCmd << " ";
105 if (not mSeedSwitch.empty() and mSeedSwitch != "none") {
106 s << mSeedSwitch << " " << mSeed << " ";
107 }
108 if (not mNEventsSwitch.empty() and mNEventsSwitch != "none") {
109 s << mNEventsSwitch << " " << mNEvents << " ";
110 }
111 if (not mBmaxSwitch.empty() and mBmax >= 0 and mBmaxSwitch != "none") {
112 s << mBmaxSwitch.c_str() << " " << mBmax << " ";
113 }
114
115 s << mOutputSwitch << " " << fileName << " "
117 return s.str();
118}
119// -----------------------------------------------------------------
120bool GeneratorFileOrCmd::executeCmdLine(const std::string& cmd)
121{
122 LOG(info) << "Command line to execute: \"" << cmd << "\"";
123 // Fork a new process
124 pid_t pid = fork();
125 if (pid == -1) {
126 LOG(fatal) << "Failed to fork process: " << std::strerror(errno);
127 return false;
128 }
129
130 if (pid == 0) {
131 // Child process
132 setsid();
133 execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)nullptr);
134 // If execl returns, there was an error, otherwise following lines will not be executed
135 LOG(fatal) << "Failed to execute command: " << std::strerror(errno);
136 _exit(EXIT_FAILURE);
137 } else {
138 // Parent process
139 setCmdPid(pid);
140 LOG(info) << "Child spawned process group is running with PID: " << mCmdPid;
141 }
142 return true;
143}
144// -----------------------------------------------------------------
145bool GeneratorFileOrCmd::terminateCmd(unsigned int graceMillis)
146{
147 if (mCmdPid == -1) {
148 LOG(info) << "No command is currently running";
149 return false;
150 }
151
152 // Let the command finish by itself if it is about to: killing the process
153 // group first would deprive an intermediate shell of the chance to reap the
154 // actual generator, and with it this process of the generator's CPU time,
155 // which only reaches RUSAGE_CHILDREN through that reap.
156 constexpr unsigned int pollMillis = 10;
157 for (unsigned int waited = 0; waited < graceMillis; waited += pollMillis) {
158 int status;
159 pid_t reaped = waitpid(mCmdPid, &status, WNOHANG);
160 if (reaped == mCmdPid) {
161 LOG(info) << "Command with process ID " << mCmdPid << " exited by itself";
162 mCmdPid = -1;
163 return true;
164 }
165 if (reaped == -1) {
166 break; // not our child (any more): let the kill path report it
167 }
168 std::this_thread::sleep_for(std::chrono::milliseconds(pollMillis));
169 }
170
171 LOG(info) << "Terminating process ID group " << mCmdPid;
172 if (kill(-mCmdPid, SIGKILL) == -1) {
173 LOG(fatal) << "Failed to kill process: " << std::strerror(errno);
174 return false;
175 }
176
177 // Wait for the process to terminate
178 int status;
179 if (waitpid(mCmdPid, &status, 0) == -1) {
180 LOG(fatal) << "Failed to wait for process termination: " << std::strerror(errno);
181 return false;
182 }
183
184 mCmdPid = -1; // Reset the process ID
185 return true;
186}
187// -----------------------------------------------------------------
188bool GeneratorFileOrCmd::makeTemp(const bool& fromName)
189{
190 if (fromName) {
191 if (mFileNames.empty()) {
192 LOG(fatal) << "No file names to make temporary file from";
193 return false;
194 } else if (mFileNames.size() > 1) {
195 LOG(warning) << "More than one file name to make temporary file from";
196 LOG(warning) << "Using the first one: " << mFileNames.front();
197 LOG(warning) << "Removing all the others";
198 mFileNames.erase(++mFileNames.begin(), mFileNames.end());
199 } else {
200 LOG(debug) << "Making temporary file from: " << mFileNames.front();
201 }
202 std::ofstream ofs(mFileNames.front().c_str());
203 if (!ofs) {
204 LOG(fatal) << "Failed to create temporary file: " << mFileNames.front();
205 return false;
206 }
207 mTemporary = std::string(mFileNames.front());
208 ofs.close();
209 } else {
210 mFileNames.clear();
211 char buf[] = "generatorFifoXXXXXX";
212 auto fp = mkstemp(buf);
213 if (fp < 0) {
214 LOG(fatal) << "Failed to make temporary file: "
215 << std::strerror(errno);
216 return false;
217 }
218 mTemporary = std::string(buf);
219 mFileNames.push_back(mTemporary);
220 close(fp);
221 }
222 return true;
223}
224// -----------------------------------------------------------------
226{
227 if (mTemporary.empty()) {
228 LOG(info) << "Temporary file name empty, nothing to remove";
229 return false;
230 }
231
232 // Get the file we're reading from
233 std::filesystem::path p(mTemporary);
234
235 // Check if the file exists
236 if (not std::filesystem::exists(p)) {
237 LOG(info) << "Temporary file " << p << " does not exist";
238 return true;
239 }
240
241 // Remove temporary file
242 std::error_code ec;
243 std::filesystem::remove(p, ec);
244 if (ec) {
245 LOG(warn) << "When removing " << p << ": " << ec.message();
246 }
247
248 // Ignore errors when removing the temporary file
249 return true;
250}
251// -----------------------------------------------------------------
253{
254 // First remove the temporary file if it exists,
255 // otherwise we may not be able to make the FIFO
256 removeTemp();
257
258 std::string fileName = mFileNames.front();
259
260 int ret = mkfifo(fileName.c_str(), 0600);
261 if (ret != 0) {
262 LOG(fatal) << "Failed to make fifo \"" << fileName << "\": "
263 << std::strerror(errno);
264 return false;
265 }
266
267 return true;
268}
269// -----------------------------------------------------------------
271{
272 try {
273 for (auto& f : mFileNames) {
274 auto c = std::filesystem::canonical(std::filesystem::path(f));
275 f = c.c_str();
276 }
277 } catch (std::exception& e) {
278 LOG(error) << e.what();
279 return false;
280 }
281 return true;
282}
283// -----------------------------------------------------------------
284void GeneratorFileOrCmd::waitForData(const std::string& filename) const
285{
286 using namespace std::chrono_literals;
287
288 // Get the file we're reading from
289 std::filesystem::path p(filename);
290
291 LOG(debug) << "Waiting for data on " << p;
292
293 // Wait until child process creates the file
294 while (not std::filesystem::exists(p)) {
295 std::this_thread::sleep_for(mWait * 1ms);
296 }
297
298 // Wait until we have more data in the file than just the file
299 // header
300 while (std::filesystem::file_size(p) <= 256) {
301 std::this_thread::sleep_for(mWait * 1ms);
302 }
303
304 // Give the child process 1 second to post the data to the file
305 LOG(debug) << "Got data in " << p << ", sleeping for a while";
306 std::this_thread::sleep_for(mWait * 2ms);
307}
308
309} /* namespace eventgen */
310} /* namespace o2 */
std::ostringstream debug
Utility functions for MC particles.
uint16_t pid
Definition RawData.h:2
uint32_t c
Definition RawData.h:2
ULong_t getStartSeed() const
Definition SimConfig.h:171
float getBMax() const
Definition SimConfig.h:162
unsigned int getNEvents() const
Definition SimConfig.h:157
GLuint GLuint end
Definition glcorearb.h:469
GLdouble f
Definition glcorearb.h:310
GLuint start
Definition glcorearb.h:469
GLenum GLfloat param
Definition glcorearb.h:271
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
std::string trim(std::string const &str)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string filename()
void setCmdPid(const pid_t cmdPid)
void setBackgroundSwitch(const std::string &opt)
void setSeedSwitch(const std::string &opt)
void setFileNames(const std::string &filenames)
void setup(const GeneratorFileOrCmdParam &param, const conf::SimConfig &config)
std::list< std::string > mFileNames
void setSeed(unsigned long seed)
void setNEventsSwitch(const std::string &opt)
void setOutputSwitch(const std::string &opt)
virtual bool terminateCmd(unsigned int graceMillis=0)
void setCmd(const std::string &cmd)
virtual std::string makeCmdLine() const
virtual void waitForData(const std::string &filename) const
virtual bool executeCmdLine(const std::string &cmd)
virtual bool makeTemp(const bool &)
void setBmaxSwitch(const std::string &opt)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"