12 #define _GNU_SOURCE 1 // for ::getline
35 ExternalProgram::ExternalProgram()
46 const Pathname & root )
53 argv[2] = commandline.c_str();
65 const Pathname & root )
69 const char * argvp[argv.size() + 1];
71 for_( i, argv.begin(), argv.end() )
73 argvp[c] = i->c_str();
88 const Pathname & root )
92 const char * argvp[argv.size() + 1];
94 for_( i, argv.begin(), argv.end() )
96 argvp[c] = i->c_str();
101 start_program( argvp, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
111 const Pathname & root )
125 const Pathname & root )
129 start_program( argv, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
134 const char *
const *argv_1,
142 const char *argv[i + 1];
144 memcpy( &argv[1], argv_1, (i - 1) *
sizeof (
char *) );
150 const char *
const *argv_1,
159 const char *argv[i + 1];
161 memcpy( &argv[1], argv_1, (i - 1) *
sizeof (
char *) );
180 int to_external[2], from_external[2];
181 int master_tty, slave_tty;
184 const char * redirectStdin =
nullptr;
185 const char * redirectStdout =
nullptr;
186 const char * chdirTo =
nullptr;
190 if ( root[0] ==
'\0' )
194 else if ( root[0] ==
'/' && root[1] ==
'\0' )
203 for (
bool strip =
false; argv[0]; ++argv )
206 switch ( argv[0][0] )
210 redirectStdin = argv[0]+1;
211 if ( *redirectStdin ==
'\0' )
212 redirectStdin =
"/dev/null";
217 redirectStdout = argv[0]+1;
218 if ( *redirectStdout ==
'\0' )
219 redirectStdout =
"/dev/null";
224 if ( argv[0][1] ==
'/' )
236 for (
int i = 0; argv[i]; i++)
238 if (i>0) cmdstr <<
' ';
244 cmdstr <<
" < '" << redirectStdin <<
"'";
245 if ( redirectStdout )
246 cmdstr <<
" > '" << redirectStdout <<
"'";
255 DBG <<
"Using ttys for communication with " << argv[0] << endl;
256 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
267 if (pipe (to_external) != 0 || pipe (from_external) != 0)
277 if ((
pid = fork()) == 0)
295 ttyname_r(slave_tty, name,
sizeof(name));
310 int inp_fd = open( redirectStdin, O_RDONLY );
314 if ( redirectStdout )
317 int inp_fd = open( redirectStdout, O_WRONLY|O_CREAT|O_APPEND, 0600 );
324 int null_fd = open(
"/dev/null", O_WRONLY);
339 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
340 setenv( it->first.c_str(), it->second.c_str(), 1 );
344 setenv(
"LC_ALL",
"C",1);
348 if(chroot(root) == -1)
358 if ( chdirTo && chdir( chdirTo ) == -1 )
367 for (
int i = ::getdtablesize() - 1; i > 2; --i ) {
371 execvp(argv[0], const_cast<char *const *>(argv));
408 inputfile = fdopen(from_external[0],
"r");
412 DBG <<
"pid " <<
pid <<
" launched" << endl;
416 ERR <<
"Cannot create streams to external program " << argv[0] << endl;
435 int inputfileFd = ::fileno( inputfile );
442 FD_SET( inputfileFd, &rfds );
446 tv.tv_sec = (delay < 0 ? 1 : 0);
447 tv.tv_usec = (delay < 0 ? 0 : delay*100000);
448 if ( delay >= 0 && ++delay > 9 )
450 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
454 ERR <<
"select error: " <<
strerror(errno) << endl;
455 if ( errno != EINTR )
463 getline( &linebuffer, &linebuffer_size, inputfile );
466 if ( ::feof( inputfile ) )
468 clearerr( inputfile );
486 ret = waitpid(
pid, &status, 0);
488 while (ret == -1 && errno == EINTR);
504 if (WIFEXITED (status))
506 status = WEXITSTATUS (status);
509 DBG <<
"Pid " <<
pid <<
" exited with status " << status << endl;
516 DBG <<
"Pid " <<
pid <<
" successfully completed" << endl;
520 else if (WIFSIGNALED (status))
522 status = WTERMSIG (status);
523 WAR <<
"Pid " <<
pid <<
" was killed by signal " << status
524 <<
" (" << strsignal(status);
525 if (WCOREDUMP (status))
527 WAR <<
", core dumped";
530 _execError =
str::form(
_(
"Command was killed by signal %d (%s)."), status, strsignal(status) );
534 ERR <<
"Pid " <<
pid <<
" exited with unknown error" << endl;
535 _execError =
_(
"Command exited with unknown error.");
556 if (
pid < 0 )
return false;
559 int p = waitpid(
pid, &status, WNOHANG );
563 ERR <<
"waitpid( " <<
pid <<
") returned error '" <<
strerror(errno) <<
"'" << endl;
585 dup2 (origfd, newfd);
604 namespace externalprogram
610 ::pipe2(
_fds, O_NONBLOCK );
613 ::fcntl(
_fds[
R], F_SETFD, O_NONBLOCK );
614 ::fcntl(
_fds[
W], F_SETFD, O_NONBLOCK );
631 if ( delim_r && !
_buffer.empty() )
635 if ( pos != std::string::npos )
637 retval_r =
_buffer.substr( 0, returnDelim_r ? pos+1 : pos );
647 if ( ch != delim_r || ! delim_r )
662 else if ( errno != EINTR )
ExternalProgram()
Start an external program by giving the arguments as an arry of char *pointers.
bool use_pty
Set to true, if a pair of ttys is used for communication instead of a pair of pipes.
std::ostream & operator>>(std::ostream &out_r)
Redirect all command output to an ostream.
bool kill()
Kill the program.
void start_program(const char *const *argv, const Environment &environment, Stderr_Disposition stderr_disp=Normal_Stderr, int stderr_fd=-1, bool default_locale=false, const char *root=NULL)
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
bool running()
Return whether program is running.
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
std::vector< std::string > Arguments
std::string getline(std::istream &str)
Read one line from stream.
std::string receiveLine()
Read one line from the input stream.
Stderr_Disposition
Define symbols for different policies on the handling of stderr.
FILE * inputFile() const
Return the input stream.
std::map< std::string, std::string > Environment
For passing additional environment variables to set.
bool stderrGetUpTo(std::string &retval_r, const char delim_r, bool returnDelim_r=false)
Read data up to delim_r from stderr (nonblocking).
std::string _execError
Remember execution errors like failed fork/exec.
int close()
Wait for the progamm to complete.
std::string _command
Store the command we're executing.
static void renumber_fd(int origfd, int newfd)
origfd will be accessible as newfd and closed (unless they were equal)
void setBlocking(bool mode)
Set the blocking mode of the input stream.
std::string strerror(int errno_r)
Return string describing the error_r code.