1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/ExternalProgram.cc
12 #define _GNU_SOURCE 1 // for ::getline
19 #include <pty.h> // openpty
20 #include <stdlib.h> // setenv
22 #include <cstring> // strsignal
26 #include "zypp/base/Logger.h"
27 #include "zypp/base/String.h"
28 #include "zypp/base/Gettext.h"
29 #include "zypp/ExternalProgram.h"
30 #include "zypp/base/CleanerThread_p.h"
34 #undef ZYPP_BASE_LOGGER_LOGGROUP
35 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::exec"
39 ExternalProgram::ExternalProgram()
45 ExternalProgram::ExternalProgram( std::string commandline,
46 Stderr_Disposition stderr_disp,
50 const Pathname & root )
57 argv[2] = commandline.c_str();
60 start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
64 ExternalProgram::ExternalProgram( const Arguments & argv,
65 Stderr_Disposition stderr_disp,
69 const Pathname & root )
73 const char * argvp[argv.size() + 1];
75 for_( i, argv.begin(), argv.end() )
77 argvp[c] = i->c_str();
82 start_program( argvp, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
86 ExternalProgram::ExternalProgram( const Arguments & argv,
87 const Environment & environment,
88 Stderr_Disposition stderr_disp,
92 const Pathname & root )
96 const char * argvp[argv.size() + 1];
98 for_( i, argv.begin(), argv.end() )
100 argvp[c] = i->c_str();
105 start_program( argvp, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
110 ExternalProgram::ExternalProgram( const char *const *argv,
111 Stderr_Disposition stderr_disp,
115 const Pathname & root )
119 start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
123 ExternalProgram::ExternalProgram( const char *const * argv,
124 const Environment & environment,
125 Stderr_Disposition stderr_disp,
129 const Pathname & root )
133 start_program( argv, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
137 ExternalProgram::ExternalProgram( const char *binpath,
138 const char *const *argv_1,
146 const char *argv[i + 1];
148 memcpy( &argv[1], argv_1, (i - 1) * sizeof (char *) );
149 start_program( argv, Environment() );
153 ExternalProgram::ExternalProgram( const char *binpath,
154 const char *const *argv_1,
155 const Environment & environment,
163 const char *argv[i + 1];
165 memcpy( &argv[1], argv_1, (i - 1) * sizeof (char *) );
166 start_program( argv, environment );
170 ExternalProgram::~ExternalProgram()
173 // we got destructed while the external process is still alive
174 // make sure the zombie is cleaned up once it exits
175 CleanerThread::watchPID( pid );
181 void ExternalProgram::start_program(const char *const *argv,
182 const Environment & environment,
183 Stderr_Disposition stderr_disp,
186 const char * root , bool switch_pgid)
190 int to_external[2], from_external[2]; // fds for pair of pipes
191 int master_tty, slave_tty; // fds for pair of ttys
193 // retrieve options at beginning of arglist
194 const char * redirectStdin = nullptr; // <[file]
195 const char * redirectStdout = nullptr; // >[file]
196 const char * chdirTo = nullptr; // #/[path]
200 if ( root[0] == '\0' )
202 root = nullptr; // ignore empty root
204 else if ( root[0] == '/' && root[1] == '\0' )
206 // If root is '/' do not chroot, but chdir to '/'
207 // unless arglist defines another dir.
213 for ( bool strip = false; argv[0]; ++argv )
216 switch ( argv[0][0] )
220 redirectStdin = argv[0]+1;
221 if ( *redirectStdin == '\0' )
222 redirectStdin = "/dev/null";
227 redirectStdout = argv[0]+1;
228 if ( *redirectStdout == '\0' )
229 redirectStdout = "/dev/null";
234 if ( argv[0][1] == '/' ) // #/[path]
242 // do not remove the single quotes around every argument, copy&paste of
243 // command to shell will not work otherwise!
246 for (int i = 0; argv[i]; i++)
248 if (i>0) cmdstr << ' ';
254 cmdstr << " < '" << redirectStdin << "'";
255 if ( redirectStdout )
256 cmdstr << " > '" << redirectStdout << "'";
257 _command = cmdstr.str();
259 DBG << "Executing " << _command << endl;
264 // Create pair of ttys
265 DBG << "Using ttys for communication with " << argv[0] << endl;
266 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
268 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
270 ERR << _execError << endl;
276 // Create pair of pipes
277 if (pipe (to_external) != 0 || pipe (from_external) != 0)
279 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
281 ERR << _execError << endl;
286 // Create module process
287 if ((pid = fork()) == 0)
289 //////////////////////////////////////////////////////////////////////
290 // Don't write to the logfile after fork!
291 //////////////////////////////////////////////////////////////////////
296 dup2 (slave_tty, 1); // set new stdout
297 renumber_fd (slave_tty, 0); // set new stdin
298 ::close(master_tty); // Belongs to father process
300 // We currently have no controlling terminal (due to setsid).
301 // The first open call will also set the new ctty (due to historical
302 // unix guru knowledge ;-) )
305 ttyname_r(slave_tty, name, sizeof(name));
306 ::close(open(name, O_RDONLY));
312 renumber_fd (to_external[0], 0); // set new stdin
313 ::close(from_external[0]); // Belongs to father process
315 renumber_fd (from_external[1], 1); // set new stdout
316 ::close(to_external [1]); // Belongs to father process
322 int inp_fd = open( redirectStdin, O_RDONLY );
326 if ( redirectStdout )
329 int inp_fd = open( redirectStdout, O_WRONLY|O_CREAT|O_APPEND, 0600 );
334 if (stderr_disp == Discard_Stderr)
336 int null_fd = open("/dev/null", O_WRONLY);
340 else if (stderr_disp == Stderr_To_Stdout)
344 else if (stderr_disp == Stderr_To_FileDesc)
346 // Note: We don't have to close anything regarding stderr_fd.
347 // Our caller is responsible for that.
351 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
352 setenv( it->first.c_str(), it->second.c_str(), 1 );
356 setenv("LC_ALL","C",1);
360 if(chroot(root) == -1)
362 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
363 std::cerr << _execError << endl;// After fork log on stderr too
364 _exit (128); // No sense in returning! I am forked away!!
370 if ( chdirTo && chdir( chdirTo ) == -1 )
372 _execError = root ? str::form( _("Can't chdir to '%s' inside chroot '%s' (%s)."), chdirTo, root, strerror(errno) )
373 : str::form( _("Can't chdir to '%s' (%s)."), chdirTo, strerror(errno) );
374 std::cerr << _execError << endl;// After fork log on stderr too
375 _exit (128); // No sense in returning! I am forked away!!
378 // close all filedesctiptors above stderr
379 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
383 execvp(argv[0], const_cast<char *const *>(argv));
384 // don't want to get here
385 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
386 std::cerr << _execError << endl;// After fork log on stderr too
387 _exit (129); // No sense in returning! I am forked away!!
388 //////////////////////////////////////////////////////////////////////
391 else if (pid == -1) // Fork failed, close everything.
393 _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
395 ERR << _execError << endl;
402 ::close(to_external[0]);
403 ::close(to_external[1]);
404 ::close(from_external[0]);
405 ::close(from_external[1]);
412 ::close(slave_tty); // belongs to child process
413 inputfile = fdopen(master_tty, "r");
414 outputfile = fdopen(master_tty, "w");
418 ::close(to_external[0]); // belongs to child process
419 ::close(from_external[1]); // belongs to child process
420 inputfile = fdopen(from_external[0], "r");
421 outputfile = fdopen(to_external[1], "w");
424 DBG << "pid " << pid << " launched" << endl;
426 if (!inputfile || !outputfile)
428 ERR << "Cannot create streams to external program " << argv[0] << endl;
436 ExternalProgram::close()
442 // Discard any output instead of closing the pipe,
443 // but watch out for the command exiting while some
444 // subprocess keeps the filedescriptor open.
445 setBlocking( false );
446 FILE * inputfile = inputFile();
447 int inputfileFd = ::fileno( inputfile );
451 /* Watch inputFile to see when it has input. */
454 FD_SET( inputfileFd, &rfds );
456 /* Wait up to 1 seconds. */
458 tv.tv_sec = (delay < 0 ? 1 : 0);
459 tv.tv_usec = (delay < 0 ? 0 : delay*100000);
460 if ( delay >= 0 && ++delay > 9 )
462 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
466 ERR << "select error: " << strerror(errno) << endl;
467 if ( errno != EINTR )
472 // Data is available now.
473 static size_t linebuffer_size = 0; // static because getline allocs
474 static char * linebuffer = 0; // and reallocs if buffer is too small
475 getline( &linebuffer, &linebuffer_size, inputfile );
476 // ::feof check is important as select returns
477 // positive if the file was closed.
478 if ( ::feof( inputfile ) )
480 clearerr( inputfile );
484 // No data within time.
491 if ( pid > 0 ) // bsc#1109877: must re-check! running() in the loop above may have already waited.
493 // Wait for child to exit
498 ret = waitpid(pid, &status, 0);
500 while (ret == -1 && errno == EINTR);
504 _exitStatus = checkStatus( status );
514 int ExternalProgram::checkStatus( int status )
516 if (WIFEXITED (status))
518 status = WEXITSTATUS (status);
521 DBG << "Pid " << pid << " exited with status " << status << endl;
522 _execError = str::form( _("Command exited with status %d."), status );
526 // if 'launch' is logged, completion should be logged,
527 // even if successfull.
528 DBG << "Pid " << pid << " successfully completed" << endl;
529 _execError.clear(); // empty if running or successfully completed
532 else if (WIFSIGNALED (status))
534 status = WTERMSIG (status);
535 WAR << "Pid " << pid << " was killed by signal " << status
536 << " (" << strsignal(status);
537 if (WCOREDUMP (status))
539 WAR << ", core dumped";
542 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
546 ERR << "Pid " << pid << " exited with unknown error" << endl;
547 _execError = _("Command exited with unknown error.");
554 ExternalProgram::kill()
558 ::kill(pid, SIGKILL);
566 ExternalProgram::running()
568 if ( pid < 0 ) return false;
571 int p = waitpid( pid, &status, WNOHANG );
575 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
579 return true; // still running
583 // Here: completed...
584 _exitStatus = checkStatus( status );
589 // origfd will be accessible as newfd and closed (unless they were equal)
590 void ExternalProgram::renumber_fd (int origfd, int newfd)
592 // It may happen that origfd is already the one we want
593 // (Although in our circumstances, that would mean somebody has closed
594 // our stdin or stdout... weird but has appened to Cray, #49797)
597 dup2 (origfd, newfd);
602 std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
605 for ( std::string line = receiveLine(); line.length(); line = receiveLine() )
610 //////////////////////////////////////////////////////////////////////
612 // class ExternalProgramWithStderr
614 //////////////////////////////////////////////////////////////////////
616 namespace externalprogram
618 EarlyPipe::EarlyPipe()
620 _fds[R] = _fds[W] = -1;
622 ::pipe2( _fds, O_NONBLOCK );
625 ::fcntl(_fds[R], F_SETFD, O_NONBLOCK );
626 ::fcntl(_fds[W], F_SETFD, O_NONBLOCK );
628 _stderr = ::fdopen( _fds[R], "r" );
631 EarlyPipe::~EarlyPipe()
637 } // namespace externalprogram
639 bool ExternalProgramWithStderr::stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r )
643 if ( delim_r && ! _buffer.empty() )
645 // check for delim already in buffer
646 std::string::size_type pos( _buffer.find( delim_r ) );
647 if ( pos != std::string::npos )
649 retval_r = _buffer.substr( 0, returnDelim_r ? pos+1 : pos );
650 _buffer.erase( 0, pos+1 );
654 ::clearerr( _stderr );
656 int ch = fgetc( _stderr );
659 if ( ch != delim_r || ! delim_r )
660 _buffer.push_back( ch );
664 _buffer.push_back( delim_r );
668 else if ( ::feof( _stderr ) )
670 if ( _buffer.empty() )
674 else if ( errno != EINTR )
677 // HERE: we left after readig at least one char (\n)
678 retval_r.swap( _buffer );