Line data Source code
1 : /*
2 : * Executable.cxx
3 : * ers
4 : *
5 : * Created by Matthias Wiesmann on 06.01.05.
6 : * Copyright 2005 CERN. All rights reserved.
7 : *
8 : */
9 :
10 : #include <pthread.h>
11 : #include <signal.h>
12 : #include <sys/types.h>
13 : #include <sys/uio.h>
14 : #include <sys/stat.h>
15 : #include <fcntl.h>
16 : #include <cstdlib>
17 : #include <unistd.h>
18 : #include <cstdio>
19 : #include <sstream>
20 : #include <iostream>
21 :
22 : #include "ers/Assertion.hpp"
23 : #include "ers/ers.hpp"
24 :
25 : #include "okssystem/Environment.hpp"
26 : #include "okssystem/Executable.hpp"
27 : #include "okssystem/Process.hpp"
28 : #include "okssystem/Descriptor.hpp"
29 : #include "okssystem/exceptions.hpp"
30 :
31 : const char* const OksSystem::Executable::SHELL_COMMAND = "/bin/sh";
32 : const char* const OksSystem::Executable::SHELL_COMMAND_PARAM = "-c";
33 :
34 : /** This method is a safe replacement for the okssystem function.
35 : * It basically offers the same functionality with better error handling.
36 : * The command is executed in a shell via \c /bin/sh \c -c.
37 : * If the command is successfull (return value 0), this method returns the resulting string.
38 : * If the command fails, this method throws an exception of type \c okssystem::ExecutionIssue.
39 : * This exception contains the return code and the content of stderr.
40 : * \param command the command to execute
41 : * \return the content of the stdout stream for the executed command
42 : * \see pipe_in()
43 : */
44 :
45 :
46 0 : std::string OksSystem::Executable::okssystem(const std::string &command) {
47 0 : OksSystem::Executable shell(SHELL_COMMAND);
48 0 : std::vector<std::string> params;
49 0 : params.push_back(SHELL_COMMAND_PARAM);
50 0 : params.push_back(command);
51 0 : return shell.pipe_in(params);
52 0 : } // std::string
53 :
54 0 : OksSystem::Executable::Executable(const OksSystem::File &file) : OksSystem::File(file) {}
55 0 : OksSystem::Executable::Executable(const char* filename) : OksSystem::File(filename) {}
56 0 : OksSystem::Executable::Executable(const std::string &filename) : OksSystem::File(filename) {}
57 0 : OksSystem::Executable::~Executable() {}
58 :
59 :
60 : /** Core execution method.
61 : * This method assumes the datastructures have been setup correctly for \c execv.
62 : * It basically adds some error handling to \c execv
63 : * \param argv array of arguments, with name of the executable in first position
64 : * \note this method should not be called directly
65 : */
66 :
67 0 : void OksSystem::Executable::exec(char** argv) const {
68 0 : ERS_PRECONDITION(argv);
69 0 : ERS_PRECONDITION(argv[0]);
70 0 : const int status = ::execv(argv[0],argv);
71 0 : throw OksSystem::ExecutionIssue( ERS_HERE, errno, argv[0], status );
72 : } // exec
73 :
74 : /** Core execution method.
75 : * This method assumes the datastructures have been setup correctly for \c execve.
76 : * It basically adds some error handling to \c execve.
77 : * Look at the \c execve man pages for details.
78 : * \param argv array of arguments, with name of the executable in first position
79 : * \param env array containing the environment the process will be started with
80 : */
81 0 : void OksSystem::Executable::exec(char** const argv, char** const env) const {
82 0 : ERS_PRECONDITION(argv);
83 0 : ERS_PRECONDITION(argv[0]);
84 0 : ERS_PRECONDITION(env);
85 0 : const int status = ::execve(argv[0],argv,env);
86 0 : throw OksSystem::ExecutionIssue( ERS_HERE, errno, argv[0], status );
87 : }
88 :
89 : /** Simple execution method.
90 : * This methods converts the vector of strings into the correct data structures for \c execv.
91 : * The \c argv structure is allocated dynamically and new string copied into it.
92 : */
93 :
94 0 : void OksSystem::Executable::exec(const param_collection ¶ms) const {
95 0 : const int argc = params.size(); // number of parameters
96 0 : const int argclen = argc+2; // size of array parameters + program name + null pointer
97 0 : char **argv = (char **) calloc(sizeof(char*),argclen);
98 0 : OKSSYSTEM_ALLOC_CHECK(argv,sizeof(char*)*argclen);
99 0 : const char* name = *this;
100 0 : argv[0] = strdup(name);
101 0 : OKSSYSTEM_ALLOC_CHECK(argv[0],strlen(name));
102 0 : for(int i=0;i<argc;i++) {
103 0 : argv[i+1] = strdup(params[i].c_str());
104 : } // loop over args
105 0 : argv[argc+1] = 0;
106 0 : try {
107 0 : exec(argv);
108 : }
109 0 : catch(OksSystem::ExecutionIssue &ex) {
110 0 : for(int i=0;argv[i]!=0;i++) {
111 0 : free(argv[i]);
112 : } // for
113 0 : free(argv);
114 0 : throw;
115 0 : }
116 0 : catch (ers::Issue &issue) { // there was a problem so we deallocate the argc array.
117 0 : for(int i=0;argv[i]!=0;i++) {
118 0 : free(argv[i]);
119 : } // for
120 0 : free(argv);
121 0 : throw;
122 0 : } // catch
123 0 : } // exec
124 :
125 : /** This execution method calls the 'execve' function executing the
126 : * \param params the parameters to use when launching the executable
127 : * \param envs table of the environnement variables set for the executable
128 : */
129 :
130 0 : void OksSystem::Executable::exec(const param_collection ¶ms, const env_collection &envs) const {
131 :
132 : // Elaborate the environment
133 0 : const unsigned int envArraySize = envs.size() + 1; // The last elements must be NULL
134 0 : char** const env = new char*[envArraySize];
135 :
136 0 : {
137 0 : const env_collection::const_iterator b = envs.begin();
138 0 : const env_collection::const_iterator e = envs.end();
139 0 : env_collection::const_iterator it;
140 :
141 0 : unsigned int counter = 0;
142 0 : for(it = b; it != e; ++it) {
143 0 : const std::string entry = it->first + "=" + it->second;
144 0 : env[counter] = new char[entry.size() + 1]; // Add the NULL terminator
145 0 : ::strcpy(env[counter], entry.c_str());
146 0 : ++counter;
147 0 : }
148 :
149 0 : env[envArraySize - 1] = (char*) 0;
150 : }
151 :
152 : // Elaborate the paramenters
153 0 : const unsigned int paramArraySize = params.size() + 2; // Add the executable name and the NULL terminator
154 0 : char** const par = new char*[paramArraySize];
155 :
156 0 : {
157 0 : const std::string& binName = this->full_name();
158 0 : par[0] = new char[binName.size() + 1]; // Add the NULL terminator
159 0 : ::strcpy(par[0], binName.c_str());
160 :
161 0 : const param_collection::const_iterator b = params.begin();
162 0 : const param_collection::const_iterator e = params.end();
163 0 : param_collection::const_iterator it;
164 :
165 0 : unsigned int counter = 1;
166 0 : for(it = b; it != e; ++it) {
167 0 : const std::string& value = *it;
168 0 : par[counter] = new char[value.size() + 1]; // Add the NULL terminator
169 0 : ::strcpy(par[counter], value.c_str());
170 0 : ++counter;
171 : }
172 :
173 0 : par[paramArraySize - 1] = (char*) 0;
174 : }
175 :
176 0 : try {
177 0 : exec(par, env);
178 : }
179 0 : catch(OksSystem::ExecutionIssue& ex) {
180 : // if we are here it means that the exec call failed!!!
181 : // Free the allocated memory
182 0 : for(unsigned int i = 0; i < envArraySize; ++i) {
183 0 : delete[] env[i];
184 : }
185 0 : delete[] env;
186 :
187 0 : for(unsigned int i = 0; i < paramArraySize; ++i) {
188 0 : delete[] par[i];
189 : }
190 0 : delete[] par;
191 :
192 : // Re-throw the exception
193 0 : throw ex;
194 0 : }
195 :
196 0 : }
197 :
198 :
199 : /** This method starts the executable in another process using \c fork
200 : * \param params the parameters to use when launching the executable
201 : * \return A process object representing the started process
202 : */
203 :
204 0 : OksSystem::Process OksSystem::Executable::start(const param_collection ¶ms) const {
205 :
206 : // Block all signals before fork()
207 0 : sigset_t new_set;
208 0 : sigset_t old_set;
209 0 : sigfillset(&new_set);
210 0 : pthread_sigmask(SIG_SETMASK, &new_set, &old_set);
211 :
212 0 : const pid_t child_id = fork();
213 0 : if (0 == child_id) { // we are the child
214 :
215 : // Put some signals to their default
216 0 : signal(SIGTERM, SIG_DFL);
217 0 : signal(SIGINT, SIG_DFL);
218 :
219 : // Restore the original signal mask in child
220 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
221 :
222 0 : try {
223 0 : exec(params);
224 : }
225 0 : catch(OksSystem::ExecutionIssue &ex) {
226 0 : ers::warning(ex);
227 0 : _exit(EXIT_FAILURE);
228 0 : }
229 0 : catch(ers::Issue &ex) {
230 0 : ers::warning(ex);
231 0 : _exit(EXIT_FAILURE);
232 0 : }
233 :
234 : } // we are the child
235 0 : if (child_id > 0) { // we are the parent
236 :
237 : // Restore the original signal mask in parent
238 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
239 :
240 0 : return Process(child_id,to_string(params));
241 :
242 : } // we are the parent
243 :
244 : // Restore the original signal mask in case of a fork() failure
245 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
246 :
247 0 : throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "fork", "" );// We are screwed up
248 : } // start
249 :
250 :
251 : /** This method starts the executable in another process using \c fork. The parent will not wait on the child termination.
252 : * \param params the parameters to use when launching the executable
253 : * \return A process object representing the started process
254 : */
255 :
256 0 : OksSystem::Process OksSystem::Executable::start_and_forget(const param_collection ¶ms) const {
257 :
258 : // Block all signals before fork()
259 0 : sigset_t new_set;
260 0 : sigset_t old_set;
261 0 : sigfillset(&new_set);
262 0 : pthread_sigmask(SIG_SETMASK, &new_set, &old_set);
263 :
264 0 : signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */
265 :
266 0 : const pid_t child_id = fork();
267 0 : if (0 == child_id) { // we are the child
268 :
269 : // Put some signals to their default
270 0 : signal(SIGTERM, SIG_DFL);
271 0 : signal(SIGINT, SIG_DFL);
272 0 : signal(SIGCLD, SIG_DFL); /* Restore default SIGCLD handling in the child process */
273 :
274 : // Restore the original signal mask in child
275 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
276 :
277 0 : try {
278 0 : exec(params);
279 : }
280 0 : catch(OksSystem::ExecutionIssue &ex) {
281 0 : ers::warning(ex);
282 0 : _exit(EXIT_FAILURE);
283 0 : }
284 0 : catch(ers::Issue &ex) {
285 0 : ers::warning(ex);
286 0 : _exit(EXIT_FAILURE);
287 0 : }
288 :
289 : } // we are the child
290 0 : if (child_id > 0) { // we are the parent
291 :
292 : // Restore the original signal mask in parent
293 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
294 :
295 0 : return Process(child_id,to_string(params));
296 : } // we are the parent
297 :
298 : // Restore the original signal mask in case of a fork() failure
299 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
300 :
301 0 : throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "fork", "" );// We are screwed up
302 : } // start
303 :
304 :
305 : /** Copies the content of a file descriptor into a STL stream
306 : * \param fd the source file descriptor
307 : * \param target the target stream
308 : * \note a more efficient version could use the underlying STL buffer
309 : */
310 :
311 0 : void OksSystem::Executable::copy_fd(int fd, std::ostream &target) {
312 0 : while(true) {
313 0 : char buffer[256];
314 0 : long status = read(fd,buffer,sizeof(buffer));
315 0 : if (status <= 0) return;
316 0 : ERS_ASSERT( status<=(long) sizeof(buffer) );
317 0 : for(int i=0;i<status;i++) {
318 0 : target << (char) buffer[i];
319 : } // for
320 0 : } // while
321 : } // copy_fd
322 :
323 : /** \overload */
324 :
325 0 : std::string OksSystem::Executable::pipe_in(const param_collection ¶ms) const {
326 :
327 0 : int input_pipe[2];
328 0 : int data_pipe[2];
329 0 : int error_pipe[2];
330 0 : const int input_pipe_status = pipe(input_pipe);
331 0 : if (input_pipe_status<0) throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "pipe", "" );
332 0 : const int data_pipe_status = pipe(data_pipe);
333 0 : if (data_pipe_status<0) throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "pipe", "" );
334 0 : const int error_pipe_status = pipe(error_pipe);
335 0 : if (error_pipe_status<0) throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "pipe", "" );
336 :
337 : // Block all signals before fork()
338 0 : sigset_t new_set;
339 0 : sigset_t old_set;
340 0 : sigfillset(&new_set);
341 0 : pthread_sigmask(SIG_SETMASK, &new_set, &old_set);
342 :
343 0 : const pid_t child_id = fork();
344 0 : if (0 == child_id) { // we are the child
345 :
346 : // Put some signals to their default
347 0 : signal(SIGTERM, SIG_DFL);
348 0 : signal(SIGINT, SIG_DFL);
349 :
350 : // Restore the original signal mask in child
351 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
352 :
353 0 : dup2(input_pipe[1], fileno(stdin));
354 0 : dup2(data_pipe[1], fileno(stdout));
355 0 : dup2(error_pipe[1], fileno(stderr));
356 :
357 0 : try {
358 0 : this->exec(params);
359 : }
360 0 : catch(OksSystem::ExecutionIssue &ex) {
361 0 : ers::warning(ex);
362 0 : _exit(EXIT_FAILURE);
363 0 : }
364 0 : catch(ers::Issue &ex) {
365 0 : ers::warning(ex);
366 0 : _exit(EXIT_FAILURE);
367 0 : }
368 :
369 : } // we are the child
370 0 : if (child_id > 0) { // we are the parent
371 :
372 : // Restore the original signal mask in parent
373 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
374 :
375 0 : Process child_process(child_id,to_string(params));
376 0 : close(input_pipe[1]);
377 0 : close(data_pipe[1]);
378 0 : close(error_pipe[1]);
379 0 : const int child_status = child_process.join();
380 0 : const int in_fd = input_pipe[0];
381 0 : const int err_fd = error_pipe[0];
382 0 : const int out_fd = data_pipe[0];
383 0 : std::ostringstream in_stream;
384 0 : std::ostringstream out_stream;
385 0 : std::ostringstream err_stream;
386 0 : copy_fd(in_fd,in_stream);
387 0 : copy_fd(out_fd,out_stream);
388 0 : copy_fd(err_fd,err_stream);
389 0 : close(in_fd);
390 0 : close(out_fd);
391 0 : close(err_fd);
392 0 : if (0==child_status) {
393 0 : return out_stream.str();
394 : } // if
395 0 : std::string command = to_string(params);
396 0 : std::string error_str = err_stream.str();
397 0 : throw OksSystem::ExecutionIssue(ERS_HERE,errno,command.c_str(),child_status);
398 0 : } // we are the parent
399 :
400 : // Restore the original signal mask in case of a fork() failure
401 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
402 :
403 0 : return std::string();
404 : } // pipe_in
405 :
406 : /** This method executes the process and pipes stdout and stderr back and waits for execution termination.
407 : * If the return value is zero then the content of stdout is copied into a string and returned.
408 : * In case of a non-zero value, an exception is thrown.
409 : * This exception contains the return code and the content of stderr.
410 : * \return content of stdout
411 : * \param params the parameters to the executable
412 : * \param envs the environnement variables to set
413 : * \throw OksSystem::ExecutionIssue if command execution did not return 0.
414 : * \throw OksSystem::ExecFail if the exec okssystem call failed.
415 : * \throw OksSystem::PipeIssue if the pipe okssystem call failed.
416 : */
417 :
418 0 : std::string OksSystem::Executable::pipe_in(const param_collection ¶ms, const env_collection &envs) const {
419 0 : OksSystem::Environment::set(envs);
420 0 : return pipe_in(params);
421 : } // pipe_in
422 :
423 : /** \overload */
424 :
425 0 : OksSystem::Process OksSystem::Executable::pipe_out(const param_collection ¶ms, const File &input_file, const File &output_file, const File &error_file,mode_t perm) const {
426 :
427 : // Block all signals before fork()
428 0 : sigset_t new_set;
429 0 : sigset_t old_set;
430 0 : sigfillset(&new_set);
431 0 : pthread_sigmask(SIG_SETMASK, &new_set, &old_set);
432 :
433 0 : OksSystem::Descriptor in(&input_file,OksSystem::Descriptor::flags(true,false),perm); // May throw OksSystem::OpenFileIssue
434 0 : OksSystem::Descriptor out(&output_file,OksSystem::Descriptor::flags(false,true),perm);
435 0 : OksSystem::Descriptor err(&error_file,OksSystem::Descriptor::flags(false,true),perm);
436 :
437 0 : const pid_t child_id = fork();
438 0 : if (0 == child_id) { // we are the child
439 :
440 : // Put some signals to their default
441 0 : signal(SIGTERM, SIG_DFL);
442 0 : signal(SIGINT, SIG_DFL);
443 :
444 : // Restore the original signal mask in child
445 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
446 :
447 0 : try {
448 0 : ::dup2(in,::fileno(stdin));
449 0 : ::dup2(out,::fileno(stdout));
450 0 : ::dup2(err,::fileno(stderr));
451 0 : ::close(in.fd());
452 0 : ::close(out.fd());
453 0 : ::close(err.fd());
454 0 : this->exec(params);
455 : }
456 0 : catch(OksSystem::ExecutionIssue &ex) {
457 0 : ers::warning(ex);
458 0 : _exit(EXIT_FAILURE);
459 0 : }
460 0 : catch(OksSystem::PosixIssue &ex) {
461 0 : ers::warning(ex);
462 0 : _exit(EXIT_FAILURE);
463 0 : }
464 0 : catch(ers::Issue &ex) {
465 0 : ers::warning(ex);
466 0 : _exit(EXIT_FAILURE);
467 0 : }
468 :
469 : } // we are the child
470 0 : if (child_id > 0) { // we are the parent
471 :
472 : // Restore the original signal mask in parent
473 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
474 :
475 0 : return Process(child_id,to_string(params));
476 : } // we are the parent
477 :
478 : // Restore the original signal mask in case of a fork() failure
479 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
480 :
481 0 : throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "fork", "" );// We are screwed up
482 0 : } // pipe_out
483 :
484 : /** Runs the executable and redirects the two output streams
485 : * \param params the parameters for the executable
486 : * \param envs the environnements for the executable
487 : * \param input_file the input file for stdin
488 : * \param output_file the output file for stdout
489 : * \param error_file the output file for stderr
490 : * \param perm permissions for both output streams
491 : */
492 :
493 0 : OksSystem::Process OksSystem::Executable::pipe_out(const param_collection ¶ms, const env_collection &envs, const File &input_file, const File &output_file, const File &error_file, mode_t perm) const {
494 :
495 : // Block all signals before fork()
496 0 : sigset_t new_set;
497 0 : sigset_t old_set;
498 0 : sigfillset(&new_set);
499 0 : pthread_sigmask(SIG_SETMASK, &new_set, &old_set);
500 :
501 0 : OksSystem::Descriptor in(&input_file,OksSystem::Descriptor::flags(true,false),perm); // May throw OksSystem::OpenFileIssue
502 0 : OksSystem::Descriptor out(&output_file,OksSystem::Descriptor::flags(false,true),perm);
503 0 : OksSystem::Descriptor err(&error_file,OksSystem::Descriptor::flags(false,true),perm);
504 :
505 0 : const pid_t child_id = fork();
506 0 : if (0 == child_id) { // we are the child
507 :
508 : // Put some signals to their default
509 0 : signal(SIGTERM, SIG_DFL);
510 0 : signal(SIGINT, SIG_DFL);
511 :
512 : // Restore the original signal mask in child
513 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
514 :
515 0 : try {
516 0 : ::dup2(in,::fileno(stdin));
517 0 : ::dup2(out,::fileno(stdout));
518 0 : ::dup2(err,::fileno(stderr));
519 0 : ::close(in.fd());
520 0 : ::close(out.fd());
521 0 : ::close(err.fd());
522 0 : this->exec(params,envs);
523 : }
524 0 : catch(OksSystem::ExecutionIssue &ex) {
525 0 : ers::warning(ex);
526 0 : _exit(EXIT_FAILURE);
527 0 : }
528 0 : catch(OksSystem::PosixIssue &ex) {
529 0 : ers::warning(ex);
530 0 : _exit(EXIT_FAILURE);
531 0 : }
532 0 : catch(ers::Issue &ex) {
533 0 : ers::warning(ex);
534 0 : _exit(EXIT_FAILURE);
535 0 : }
536 :
537 : } // we are the child
538 0 : if (child_id > 0) { // we are the parent
539 :
540 : // Restore the original signal mask in parent
541 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
542 :
543 0 : return Process(child_id,to_string(params));
544 : } // we are the parent
545 :
546 : // Restore the original signal mask in case of a fork() failure
547 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
548 :
549 0 : throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "fork", "" );// We are screwed up
550 :
551 0 : } //pipe_out
552 :
553 : /** This method starts the executable in another process using \c fork
554 : * \param params the parameters to use when launching the executable
555 : * \param envs table of the environnement variables set for the executable
556 : */
557 :
558 0 : OksSystem::Process OksSystem::Executable::start(const param_collection ¶ms, const env_collection &envs) const {
559 :
560 : // Block all signals before fork()
561 0 : sigset_t new_set;
562 0 : sigset_t old_set;
563 0 : sigfillset(&new_set);
564 0 : pthread_sigmask(SIG_SETMASK, &new_set, &old_set);
565 :
566 0 : const pid_t child_id = fork();
567 0 : if (child_id == 0) { // we are the child
568 :
569 : // Put some signals to their default
570 0 : signal(SIGTERM, SIG_DFL);
571 0 : signal(SIGINT, SIG_DFL);
572 :
573 : // Restore the original signal mask in child
574 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
575 :
576 0 : try {
577 0 : exec(params,envs);
578 : }
579 0 : catch(OksSystem::ExecutionIssue &ex) {
580 0 : ers::warning(ex);
581 0 : _exit(EXIT_FAILURE);
582 0 : }
583 0 : catch(ers::Issue &ex) {
584 0 : ers::warning(ex);
585 0 : _exit(EXIT_FAILURE);
586 0 : }
587 :
588 : } // we are the child
589 0 : if (child_id > 0) { // we are the parent
590 :
591 : // Restore the original signal mask in parent
592 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
593 :
594 0 : return Process(child_id);
595 : } // we are the parent
596 :
597 : // Restore the original signal mask in case of a fork() failure
598 0 : pthread_sigmask(SIG_SETMASK, &old_set, NULL);
599 :
600 0 : throw OksSystem::OksSystemCallIssue( ERS_HERE, errno, "fork", "" );// We are screwed up
601 : } // start
602 :
603 : /** Converts the executable name and a list of parameters into a string,
604 : * suitable for display.
605 : * \param params the list of parameters
606 : * \return a string with command name and parameters separated by spaces
607 : */
608 :
609 0 : std::string OksSystem::Executable::to_string(const param_collection ¶ms) const {
610 0 : std::ostringstream stream;
611 0 : stream << m_full_name;
612 0 : for(param_collection::const_iterator pos=params.begin();pos!=params.end();++pos) {
613 0 : stream << " " << (*pos);
614 : } // for
615 0 : return stream.str();
616 0 : } // to_string
617 :
618 :
|