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
21 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
23 #include <cstring> // strsignal
27 #include <zypp/base/Logger.h>
28 #include <zypp/base/String.h>
29 #include <zypp/base/Gettext.h>
30 #include <zypp/ExternalProgram.h>
31 #include <zypp/base/CleanerThread_p.h>
35 #undef ZYPP_BASE_LOGGER_LOGGROUP
36 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::exec"
40 ExternalProgram::ExternalProgram()
46 ExternalProgram::ExternalProgram( std::string commandline,
47 Stderr_Disposition stderr_disp,
51 const Pathname & root )
58 argv[2] = commandline.c_str();
61 start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
65 ExternalProgram::ExternalProgram( const Arguments & argv,
66 Stderr_Disposition stderr_disp,
70 const Pathname & root )
74 const char * argvp[argv.size() + 1];
76 for_( i, argv.begin(), argv.end() )
78 argvp[c] = i->c_str();
83 start_program( argvp, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
87 ExternalProgram::ExternalProgram( const Arguments & argv,
88 const Environment & environment,
89 Stderr_Disposition stderr_disp,
93 const Pathname & root )
97 const char * argvp[argv.size() + 1];
99 for_( i, argv.begin(), argv.end() )
101 argvp[c] = i->c_str();
106 start_program( argvp, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
111 ExternalProgram::ExternalProgram( const char *const *argv,
112 Stderr_Disposition stderr_disp,
116 const Pathname & root )
120 start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
124 ExternalProgram::ExternalProgram( const char *const * argv,
125 const Environment & environment,
126 Stderr_Disposition stderr_disp,
130 const Pathname & root )
134 start_program( argv, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
138 ExternalProgram::ExternalProgram( const char *binpath,
139 const char *const *argv_1,
147 const char *argv[i + 1];
149 memcpy( &argv[1], argv_1, (i - 1) * sizeof (char *) );
150 start_program( argv, Environment() );
154 ExternalProgram::ExternalProgram( const char *binpath,
155 const char *const *argv_1,
156 const Environment & environment,
164 const char *argv[i + 1];
166 memcpy( &argv[1], argv_1, (i - 1) * sizeof (char *) );
167 start_program( argv, environment );
171 ExternalProgram::~ExternalProgram()
174 // we got destructed while the external process is still alive
175 // make sure the zombie is cleaned up once it exits
176 CleanerThread::watchPID( pid );
182 void ExternalProgram::start_program(const char *const *argv,
183 const Environment & environment,
184 Stderr_Disposition stderr_disp,
187 const char * root , bool switch_pgid, bool die_with_parent )
191 int to_external[2], from_external[2]; // fds for pair of pipes
192 int master_tty, slave_tty; // fds for pair of ttys
194 // retrieve options at beginning of arglist
195 const char * redirectStdin = nullptr; // <[file]
196 const char * redirectStdout = nullptr; // >[file]
197 const char * chdirTo = nullptr; // #/[path]
201 if ( root[0] == '\0' )
203 root = nullptr; // ignore empty root
205 else if ( root[0] == '/' && root[1] == '\0' )
207 // If root is '/' do not chroot, but chdir to '/'
208 // unless arglist defines another dir.
214 for ( bool strip = false; argv[0]; ++argv )
217 switch ( argv[0][0] )
221 redirectStdin = argv[0]+1;
222 if ( *redirectStdin == '\0' )
223 redirectStdin = "/dev/null";
228 redirectStdout = argv[0]+1;
229 if ( *redirectStdout == '\0' )
230 redirectStdout = "/dev/null";
235 if ( argv[0][1] == '/' ) // #/[path]
243 // do not remove the single quotes around every argument, copy&paste of
244 // command to shell will not work otherwise!
246 std::stringstream cmdstr;
247 for (int i = 0; argv[i]; i++)
249 if (i>0) cmdstr << ' ';
255 cmdstr << " < '" << redirectStdin << "'";
256 if ( redirectStdout )
257 cmdstr << " > '" << redirectStdout << "'";
258 _command = cmdstr.str();
260 DBG << "Executing" << (default_locale?"[C] ":" ") << _command << endl;
265 // Create pair of ttys
266 DBG << "Using ttys for communication with " << argv[0] << endl;
267 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
269 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
271 ERR << _execError << endl;
277 // Create pair of pipes
278 if (pipe (to_external) != 0 || pipe (from_external) != 0)
280 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
282 ERR << _execError << endl;
287 pid_t ppid_before_fork = ::getpid();
289 // Create module process
290 if ((pid = fork()) == 0)
292 //////////////////////////////////////////////////////////////////////
293 // Don't write to the logfile after fork!
294 //////////////////////////////////////////////////////////////////////
299 dup2 (slave_tty, 1); // set new stdout
300 renumber_fd (slave_tty, 0); // set new stdin
301 ::close(master_tty); // Belongs to father process
303 // We currently have no controlling terminal (due to setsid).
304 // The first open call will also set the new ctty (due to historical
305 // unix guru knowledge ;-) )
308 ttyname_r(slave_tty, name, sizeof(name));
309 ::close(open(name, O_RDONLY));
315 renumber_fd (to_external[0], 0); // set new stdin
316 ::close(from_external[0]); // Belongs to father process
318 renumber_fd (from_external[1], 1); // set new stdout
319 ::close(to_external [1]); // Belongs to father process
325 int inp_fd = open( redirectStdin, O_RDONLY );
329 if ( redirectStdout )
332 int inp_fd = open( redirectStdout, O_WRONLY|O_CREAT|O_APPEND, 0600 );
337 if (stderr_disp == Discard_Stderr)
339 int null_fd = open("/dev/null", O_WRONLY);
343 else if (stderr_disp == Stderr_To_Stdout)
347 else if (stderr_disp == Stderr_To_FileDesc)
349 // Note: We don't have to close anything regarding stderr_fd.
350 // Our caller is responsible for that.
354 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
355 setenv( it->first.c_str(), it->second.c_str(), 1 );
359 setenv("LC_ALL","C",1);
363 if(chroot(root) == -1)
365 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
366 std::cerr << _execError << endl;// After fork log on stderr too
367 _exit (128); // No sense in returning! I am forked away!!
373 if ( chdirTo && chdir( chdirTo ) == -1 )
375 _execError = root ? str::form( _("Can't chdir to '%s' inside chroot '%s' (%s)."), chdirTo, root, strerror(errno) )
376 : str::form( _("Can't chdir to '%s' (%s)."), chdirTo, strerror(errno) );
377 std::cerr << _execError << endl;// After fork log on stderr too
378 _exit (128); // No sense in returning! I am forked away!!
381 // close all filedesctiptors above stderr
382 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
386 if ( die_with_parent ) {
387 // process dies with us
388 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
390 //ignore if it did not work, worst case the process lives on after the parent dies
391 std::cerr << "Failed to set PR_SET_PDEATHSIG" << endl;// After fork log on stderr too
394 // test in case the original parent exited just
395 // before the prctl() call
396 pid_t ppidNow = getppid();
397 if (ppidNow != ppid_before_fork) {
398 std::cerr << "PPID changed from "<<ppid_before_fork<<" to "<< ppidNow << endl;// After fork log on stderr too
403 execvp(argv[0], const_cast<char *const *>(argv));
404 // don't want to get here
405 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
406 std::cerr << _execError << endl;// After fork log on stderr too
407 _exit (129); // No sense in returning! I am forked away!!
408 //////////////////////////////////////////////////////////////////////
411 else if (pid == -1) // Fork failed, close everything.
413 _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
415 ERR << _execError << endl;
422 ::close(to_external[0]);
423 ::close(to_external[1]);
424 ::close(from_external[0]);
425 ::close(from_external[1]);
432 ::close(slave_tty); // belongs to child process
433 inputfile = fdopen(master_tty, "r");
434 outputfile = fdopen(master_tty, "w");
438 ::close(to_external[0]); // belongs to child process
439 ::close(from_external[1]); // belongs to child process
440 inputfile = fdopen(from_external[0], "r");
441 outputfile = fdopen(to_external[1], "w");
444 DBG << "pid " << pid << " launched" << endl;
446 if (!inputfile || !outputfile)
448 ERR << "Cannot create streams to external program " << argv[0] << endl;
456 ExternalProgram::close()
462 // Discard any output instead of closing the pipe,
463 // but watch out for the command exiting while some
464 // subprocess keeps the filedescriptor open.
465 setBlocking( false );
466 FILE * inputfile = inputFile();
467 int inputfileFd = ::fileno( inputfile );
471 /* Watch inputFile to see when it has input. */
474 FD_SET( inputfileFd, &rfds );
476 /* Wait up to 1 seconds. */
478 tv.tv_sec = (delay < 0 ? 1 : 0);
479 tv.tv_usec = (delay < 0 ? 0 : delay*100000);
480 if ( delay >= 0 && ++delay > 9 )
482 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
486 if ( errno != EINTR ) {
487 ERR << "select error: " << strerror(errno) << endl;
493 // Data is available now.
494 static size_t linebuffer_size = 0; // static because getline allocs
495 static char * linebuffer = 0; // and reallocs if buffer is too small
496 getline( &linebuffer, &linebuffer_size, inputfile );
497 // ::feof check is important as select returns
498 // positive if the file was closed.
499 if ( ::feof( inputfile ) )
501 clearerr( inputfile );
505 // No data within time.
512 if ( pid > 0 ) // bsc#1109877: must re-check! running() in the loop above may have already waited.
514 // Wait for child to exit
519 ret = waitpid(pid, &status, 0);
521 while (ret == -1 && errno == EINTR);
525 _exitStatus = checkStatus( status );
535 int ExternalProgram::checkStatus( int status )
537 if (WIFEXITED (status))
539 status = WEXITSTATUS (status);
542 DBG << "Pid " << pid << " exited with status " << status << endl;
543 _execError = str::form( _("Command exited with status %d."), status );
547 // if 'launch' is logged, completion should be logged,
548 // even if successfull.
549 DBG << "Pid " << pid << " successfully completed" << endl;
550 _execError.clear(); // empty if running or successfully completed
553 else if (WIFSIGNALED (status))
555 status = WTERMSIG (status);
556 WAR << "Pid " << pid << " was killed by signal " << status
557 << " (" << strsignal(status);
558 if (WCOREDUMP (status))
560 WAR << ", core dumped";
563 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
567 ERR << "Pid " << pid << " exited with unknown error" << endl;
568 _execError = _("Command exited with unknown error.");
575 ExternalProgram::kill()
579 ::kill(pid, SIGKILL);
585 bool ExternalProgram::kill(int sig)
595 ExternalProgram::running()
597 if ( pid < 0 ) return false;
600 int p = waitpid( pid, &status, WNOHANG );
604 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
608 return true; // still running
612 // Here: completed...
613 _exitStatus = checkStatus( status );
618 // origfd will be accessible as newfd and closed (unless they were equal)
619 void ExternalProgram::renumber_fd (int origfd, int newfd)
621 // It may happen that origfd is already the one we want
622 // (Although in our circumstances, that would mean somebody has closed
623 // our stdin or stdout... weird but has appened to Cray, #49797)
626 dup2 (origfd, newfd);
631 std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
634 for ( std::string line = receiveLine(); line.length(); line = receiveLine() )
639 //////////////////////////////////////////////////////////////////////
641 // class ExternalProgramWithStderr
643 //////////////////////////////////////////////////////////////////////
645 namespace externalprogram
647 EarlyPipe::EarlyPipe()
649 _fds[R] = _fds[W] = -1;
651 ::pipe2( _fds, O_NONBLOCK );
654 ::fcntl(_fds[R], F_SETFD, O_NONBLOCK );
655 ::fcntl(_fds[W], F_SETFD, O_NONBLOCK );
657 _stderr = ::fdopen( _fds[R], "r" );
660 EarlyPipe::~EarlyPipe()
666 } // namespace externalprogram
668 bool ExternalProgramWithStderr::stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r )
672 if ( delim_r && ! _buffer.empty() )
674 // check for delim already in buffer
675 std::string::size_type pos( _buffer.find( delim_r ) );
676 if ( pos != std::string::npos )
678 retval_r = _buffer.substr( 0, returnDelim_r ? pos+1 : pos );
679 _buffer.erase( 0, pos+1 );
683 ::clearerr( _stderr );
685 int ch = fgetc( _stderr );
688 if ( ch != delim_r || ! delim_r )
689 _buffer.push_back( ch );
693 _buffer.push_back( delim_r );
697 else if ( ::feof( _stderr ) )
699 if ( _buffer.empty() )
703 else if ( errno != EINTR )
706 // HERE: we left after readig at least one char (\n)
707 retval_r.swap( _buffer );