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"
35 ExternalProgram::ExternalProgram()
42 ExternalProgram::ExternalProgram( std::string commandline,
43 Stderr_Disposition stderr_disp,
47 const Pathname & root )
54 argv[2] = commandline.c_str();
57 const char* rootdir = NULL;
58 if(!root.empty() && root != "/")
60 rootdir = root.asString().c_str();
62 Environment environment;
63 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
67 ExternalProgram::ExternalProgram (const Arguments &argv,
68 Stderr_Disposition stderr_disp,
69 bool use_pty, int stderr_fd,
75 const char * argvp[argv.size() + 1];
77 for_( i, argv.begin(), argv.end() )
79 argvp[c] = i->c_str();
84 Environment environment;
85 const char* rootdir = NULL;
86 if(!root.empty() && root != "/")
88 rootdir = root.asString().c_str();
90 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
94 ExternalProgram::ExternalProgram (const Arguments &argv,
95 const Environment & environment,
96 Stderr_Disposition stderr_disp,
97 bool use_pty, int stderr_fd,
103 const char * argvp[argv.size() + 1];
105 for_( i, argv.begin(), argv.end() )
107 argvp[c] = i->c_str();
112 const char* rootdir = NULL;
113 if(!root.empty() && root != "/")
115 rootdir = root.asString().c_str();
117 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
124 ExternalProgram::ExternalProgram( const char *const *argv,
125 Stderr_Disposition stderr_disp,
129 const Pathname & root )
133 const char* rootdir = NULL;
134 if(!root.empty() && root != "/")
136 rootdir = root.asString().c_str();
138 Environment environment;
139 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
143 ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
144 Stderr_Disposition stderr_disp, bool use_pty,
145 int stderr_fd, bool default_locale,
146 const Pathname& root)
150 const char* rootdir = NULL;
151 if(!root.empty() && root != "/")
153 rootdir = root.asString().c_str();
155 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
159 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
167 const char *argv[i + 1];
169 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
170 Environment environment;
171 start_program (argv, environment);
175 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
183 const char *argv[i + 1];
185 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
186 start_program (argv, environment);
190 ExternalProgram::~ExternalProgram()
196 ExternalProgram::start_program (const char *const *argv, const Environment & environment,
197 Stderr_Disposition stderr_disp,
198 int stderr_fd, bool default_locale, const char* root)
202 int to_external[2], from_external[2]; // fds for pair of pipes
203 int master_tty, slave_tty; // fds for pair of ttys
205 const char * redirectStdin = 0;
206 if ( argv[0] && *argv[0] == '<' )
208 redirectStdin = argv[0]+1;
209 if ( *redirectStdin == '\0' )
210 redirectStdin = "/dev/null";
214 // do not remove the single quotes around every argument, copy&paste of
215 // command to shell will not work otherwise!
218 for (int i = 0; argv[i]; i++)
220 if (i>0) cmdstr << ' ';
226 cmdstr << " < '" << redirectStdin << "'";
227 _command = cmdstr.str();
229 DBG << "Executing " << _command << endl;
234 // Create pair of ttys
235 DBG << "Using ttys for communication with " << argv[0] << endl;
236 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
238 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
240 ERR << _execError << endl;
246 // Create pair of pipes
247 if (pipe (to_external) != 0 || pipe (from_external) != 0)
249 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
251 ERR << _execError << endl;
256 // Create module process
257 if ((pid = fork()) == 0)
259 //////////////////////////////////////////////////////////////////////
260 // Don't write to the logfile after fork!
261 //////////////////////////////////////////////////////////////////////
266 dup2 (slave_tty, 1); // set new stdout
267 renumber_fd (slave_tty, 0); // set new stdin
268 ::close(master_tty); // Belongs to father process
270 // We currently have no controlling terminal (due to setsid).
271 // The first open call will also set the new ctty (due to historical
272 // unix guru knowledge ;-) )
275 ttyname_r(slave_tty, name, sizeof(name));
276 ::close(open(name, O_RDONLY));
280 renumber_fd (to_external[0], 0); // set new stdin
281 ::close(from_external[0]); // Belongs to father process
283 renumber_fd (from_external[1], 1); // set new stdout
284 ::close(to_external [1]); // Belongs to father process
290 int inp_fd = open( redirectStdin, O_RDONLY );
295 if (stderr_disp == Discard_Stderr)
297 int null_fd = open("/dev/null", O_WRONLY);
301 else if (stderr_disp == Stderr_To_Stdout)
305 else if (stderr_disp == Stderr_To_FileDesc)
307 // Note: We don't have to close anything regarding stderr_fd.
308 // Our caller is responsible for that.
312 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
313 setenv( it->first.c_str(), it->second.c_str(), 1 );
317 setenv("LC_ALL","C",1);
321 if(chroot(root) == -1)
323 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
324 std::cerr << _execError << endl;// After fork log on stderr too
325 _exit (128); // No sense in returning! I am forked away!!
329 _execError = str::form( _("Can't chdir to '/' inside chroot (%s)."), strerror(errno) );
330 std::cerr << _execError << endl;// After fork log on stderr too
331 _exit (128); // No sense in returning! I am forked away!!
335 // close all filedesctiptors above stderr
336 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
340 execvp(argv[0], const_cast<char *const *>(argv));
341 // don't want to get here
342 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
343 std::cerr << _execError << endl;// After fork log on stderr too
344 _exit (129); // No sense in returning! I am forked away!!
345 //////////////////////////////////////////////////////////////////////
348 else if (pid == -1) // Fork failed, close everything.
350 _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
352 ERR << _execError << endl;
359 ::close(to_external[0]);
360 ::close(to_external[1]);
361 ::close(from_external[0]);
362 ::close(from_external[1]);
369 ::close(slave_tty); // belongs to child process
370 inputfile = fdopen(master_tty, "r");
371 outputfile = fdopen(master_tty, "w");
375 ::close(to_external[0]); // belongs to child process
376 ::close(from_external[1]); // belongs to child process
377 inputfile = fdopen(from_external[0], "r");
378 outputfile = fdopen(to_external[1], "w");
381 DBG << "pid " << pid << " launched" << endl;
383 if (!inputfile || !outputfile)
385 ERR << "Cannot create streams to external program " << argv[0] << endl;
393 ExternalProgram::close()
399 // Discard any output instead of closing the pipe,
400 // but watch out for the command exiting while some
401 // subprocess keeps the filedescriptor open.
402 setBlocking( false );
403 FILE * inputfile = inputFile();
404 int inputfileFd = ::fileno( inputfile );
408 /* Watch inputFile to see when it has input. */
411 FD_SET( inputfileFd, &rfds );
413 /* Wait up to 1 seconds. */
415 tv.tv_sec = (delay < 0 ? 1 : 0);
416 tv.tv_usec = (delay < 0 ? 0 : delay*100000);
417 if ( delay >= 0 && ++delay > 9 )
419 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
423 ERR << "select error: " << strerror(errno) << endl;
424 if ( errno != EINTR )
429 // Data is available now.
430 static size_t linebuffer_size = 0; // static because getline allocs
431 static char * linebuffer = 0; // and reallocs if buffer is too small
432 getline( &linebuffer, &linebuffer_size, inputfile );
433 // ::feof check is important as select returns
434 // positive if the file was closed.
435 if ( ::feof( inputfile ) )
437 clearerr( inputfile );
441 // No data within time.
448 // Wait for child to exit
453 ret = waitpid(pid, &status, 0);
455 while (ret == -1 && errno == EINTR);
459 _exitStatus = checkStatus( status );
468 int ExternalProgram::checkStatus( int status )
470 if (WIFEXITED (status))
472 status = WEXITSTATUS (status);
475 DBG << "Pid " << pid << " exited with status " << status << endl;
476 _execError = str::form( _("Command exited with status %d."), status );
480 // if 'launch' is logged, completion should be logged,
481 // even if successfull.
482 DBG << "Pid " << pid << " successfully completed" << endl;
483 _execError.clear(); // empty if running or successfully completed
486 else if (WIFSIGNALED (status))
488 status = WTERMSIG (status);
489 WAR << "Pid " << pid << " was killed by signal " << status
490 << " (" << strsignal(status);
491 if (WCOREDUMP (status))
493 WAR << ", core dumped";
496 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
500 ERR << "Pid " << pid << " exited with unknown error" << endl;
501 _execError = _("Command exited with unknown error.");
508 ExternalProgram::kill()
512 ::kill(pid, SIGKILL);
520 ExternalProgram::running()
522 if ( pid < 0 ) return false;
525 int p = waitpid( pid, &status, WNOHANG );
529 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
533 return true; // still running
537 // Here: completed...
538 _exitStatus = checkStatus( status );
543 // origfd will be accessible as newfd and closed (unless they were equal)
544 void ExternalProgram::renumber_fd (int origfd, int newfd)
546 // It may happen that origfd is already the one we want
547 // (Although in our circumstances, that would mean somebody has closed
548 // our stdin or stdout... weird but has appened to Cray, #49797)
551 dup2 (origfd, newfd);
556 std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
559 for ( std::string line = receiveLine(); line.length(); line = receiveLine() )
564 //////////////////////////////////////////////////////////////////////
566 // class ExternalProgramWithStderr
568 //////////////////////////////////////////////////////////////////////
570 namespace _ExternalProgram
572 EarlyPipe::EarlyPipe()
574 _fds[R] = _fds[W] = -1;
575 ::pipe2( _fds, O_NONBLOCK );
576 _stderr = ::fdopen( _fds[R], "r" );
579 EarlyPipe::~EarlyPipe()
587 bool ExternalProgramWithStderr::stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r )
591 if ( delim_r && ! _buffer.empty() )
593 // check for delim already in buffer
594 std::string::size_type pos( _buffer.find( delim_r ) );
595 if ( pos != std::string::npos )
597 retval_r = _buffer.substr( 0, returnDelim_r ? pos+1 : pos );
598 _buffer.erase( 0, pos+1 );
602 ::clearerr( _stderr );
604 int ch = fgetc( _stderr );
607 if ( ch != delim_r || ! delim_r )
608 _buffer.push_back( ch );
612 _buffer.push_back( delim_r );
616 else if ( ::feof( _stderr ) )
618 if ( _buffer.empty() )
622 else if ( errno != EINTR )
625 // HERE: we left after readig at least one char (\n)
626 retval_r.swap( _buffer );