libzypp
10.5.0
|
00001 /*---------------------------------------------------------------------\ 00002 | ____ _ __ __ ___ | 00003 | |__ / \ / / . \ . \ | 00004 | / / \ V /| _/ _/ | 00005 | / /__ | | | | | | | 00006 | /_____||_| |_| |_| | 00007 | | 00008 \---------------------------------------------------------------------*/ 00012 #define _GNU_SOURCE 1 // for ::getline 00013 00014 #include <signal.h> 00015 #include <errno.h> 00016 #include <unistd.h> 00017 #include <sys/wait.h> 00018 #include <fcntl.h> 00019 #include <pty.h> // openpty 00020 #include <stdlib.h> // setenv 00021 00022 #include <cstring> // strsignal 00023 #include <iostream> 00024 #include <sstream> 00025 00026 #include "zypp/base/Logger.h" 00027 #include "zypp/base/String.h" 00028 #include "zypp/base/Gettext.h" 00029 #include "zypp/ExternalProgram.h" 00030 00031 using namespace std; 00032 00033 namespace zypp { 00034 00035 ExternalProgram::ExternalProgram() 00036 : use_pty (false) 00037 , pid( -1 ) 00038 { 00039 } 00040 00041 00042 ExternalProgram::ExternalProgram( std::string commandline, 00043 Stderr_Disposition stderr_disp, 00044 bool use_pty, 00045 int stderr_fd, 00046 bool default_locale, 00047 const Pathname & root ) 00048 : use_pty (use_pty) 00049 , pid( -1 ) 00050 { 00051 const char *argv[4]; 00052 argv[0] = "/bin/sh"; 00053 argv[1] = "-c"; 00054 argv[2] = commandline.c_str(); 00055 argv[3] = 0; 00056 00057 const char* rootdir = NULL; 00058 if(!root.empty() && root != "/") 00059 { 00060 rootdir = root.asString().c_str(); 00061 } 00062 Environment environment; 00063 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir); 00064 } 00065 00066 00067 ExternalProgram::ExternalProgram (const Arguments &argv, 00068 Stderr_Disposition stderr_disp, 00069 bool use_pty, int stderr_fd, 00070 bool default_locale, 00071 const Pathname& root) 00072 : use_pty (use_pty) 00073 , pid( -1 ) 00074 { 00075 const char * argvp[argv.size() + 1]; 00076 unsigned c = 0; 00077 for_( i, argv.begin(), argv.end() ) 00078 { 00079 argvp[c] = i->c_str(); 00080 ++c; 00081 } 00082 argvp[c] = 0; 00083 00084 Environment environment; 00085 const char* rootdir = NULL; 00086 if(!root.empty() && root != "/") 00087 { 00088 rootdir = root.asString().c_str(); 00089 } 00090 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir); 00091 } 00092 00093 00094 ExternalProgram::ExternalProgram (const Arguments &argv, 00095 const Environment & environment, 00096 Stderr_Disposition stderr_disp, 00097 bool use_pty, int stderr_fd, 00098 bool default_locale, 00099 const Pathname& root) 00100 : use_pty (use_pty) 00101 , pid( -1 ) 00102 { 00103 const char * argvp[argv.size() + 1]; 00104 unsigned c = 0; 00105 for_( i, argv.begin(), argv.end() ) 00106 { 00107 argvp[c] = i->c_str(); 00108 ++c; 00109 } 00110 argvp[c] = 0; 00111 00112 const char* rootdir = NULL; 00113 if(!root.empty() && root != "/") 00114 { 00115 rootdir = root.asString().c_str(); 00116 } 00117 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir); 00118 00119 } 00120 00121 00122 00123 00124 ExternalProgram::ExternalProgram( const char *const *argv, 00125 Stderr_Disposition stderr_disp, 00126 bool use_pty, 00127 int stderr_fd, 00128 bool default_locale, 00129 const Pathname & root ) 00130 : use_pty (use_pty) 00131 , pid( -1 ) 00132 { 00133 const char* rootdir = NULL; 00134 if(!root.empty() && root != "/") 00135 { 00136 rootdir = root.asString().c_str(); 00137 } 00138 Environment environment; 00139 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir); 00140 } 00141 00142 00143 ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment, 00144 Stderr_Disposition stderr_disp, bool use_pty, 00145 int stderr_fd, bool default_locale, 00146 const Pathname& root) 00147 : use_pty (use_pty) 00148 , pid( -1 ) 00149 { 00150 const char* rootdir = NULL; 00151 if(!root.empty() && root != "/") 00152 { 00153 rootdir = root.asString().c_str(); 00154 } 00155 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir); 00156 } 00157 00158 00159 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, 00160 bool use_pty) 00161 : use_pty (use_pty) 00162 , pid( -1 ) 00163 { 00164 int i = 0; 00165 while (argv_1[i++]) 00166 ; 00167 const char *argv[i + 1]; 00168 argv[0] = binpath; 00169 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *)); 00170 Environment environment; 00171 start_program (argv, environment); 00172 } 00173 00174 00175 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment, 00176 bool use_pty) 00177 : use_pty (use_pty) 00178 , pid( -1 ) 00179 { 00180 int i = 0; 00181 while (argv_1[i++]) 00182 ; 00183 const char *argv[i + 1]; 00184 argv[0] = binpath; 00185 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *)); 00186 start_program (argv, environment); 00187 } 00188 00189 00190 ExternalProgram::~ExternalProgram() 00191 { 00192 } 00193 00194 00195 void 00196 ExternalProgram::start_program (const char *const *argv, const Environment & environment, 00197 Stderr_Disposition stderr_disp, 00198 int stderr_fd, bool default_locale, const char* root) 00199 { 00200 pid = -1; 00201 _exitStatus = 0; 00202 int to_external[2], from_external[2]; // fds for pair of pipes 00203 int master_tty, slave_tty; // fds for pair of ttys 00204 00205 const char * redirectStdin = 0; 00206 if ( argv[0] && *argv[0] == '<' ) 00207 { 00208 redirectStdin = argv[0]+1; 00209 if ( *redirectStdin == '\0' ) 00210 redirectStdin = "/dev/null"; 00211 ++argv; 00212 } 00213 00214 // do not remove the single quotes around every argument, copy&paste of 00215 // command to shell will not work otherwise! 00216 { 00217 stringstream cmdstr; 00218 for (int i = 0; argv[i]; i++) 00219 { 00220 if (i>0) cmdstr << ' '; 00221 cmdstr << '\''; 00222 cmdstr << argv[i]; 00223 cmdstr << '\''; 00224 } 00225 if ( redirectStdin ) 00226 cmdstr << " < '" << redirectStdin << "'"; 00227 _command = cmdstr.str(); 00228 } 00229 DBG << "Executing " << _command << endl; 00230 00231 00232 if (use_pty) 00233 { 00234 // Create pair of ttys 00235 DBG << "Using ttys for communication with " << argv[0] << endl; 00236 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0) 00237 { 00238 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) ); 00239 _exitStatus = 126; 00240 ERR << _execError << endl; 00241 return; 00242 } 00243 } 00244 else 00245 { 00246 // Create pair of pipes 00247 if (pipe (to_external) != 0 || pipe (from_external) != 0) 00248 { 00249 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) ); 00250 _exitStatus = 126; 00251 ERR << _execError << endl; 00252 return; 00253 } 00254 } 00255 00256 // Create module process 00257 if ((pid = fork()) == 0) 00258 { 00260 // Don't write to the logfile after fork! 00262 if (use_pty) 00263 { 00264 setsid(); 00265 if(slave_tty != 1) 00266 dup2 (slave_tty, 1); // set new stdout 00267 renumber_fd (slave_tty, 0); // set new stdin 00268 ::close(master_tty); // Belongs to father process 00269 00270 // We currently have no controlling terminal (due to setsid). 00271 // The first open call will also set the new ctty (due to historical 00272 // unix guru knowledge ;-) ) 00273 00274 char name[512]; 00275 ttyname_r(slave_tty, name, sizeof(name)); 00276 ::close(open(name, O_RDONLY)); 00277 } 00278 else 00279 { 00280 renumber_fd (to_external[0], 0); // set new stdin 00281 ::close(from_external[0]); // Belongs to father process 00282 00283 renumber_fd (from_external[1], 1); // set new stdout 00284 ::close(to_external [1]); // Belongs to father process 00285 } 00286 00287 if ( redirectStdin ) 00288 { 00289 ::close( 0 ); 00290 int inp_fd = open( redirectStdin, O_RDONLY ); 00291 dup2( inp_fd, 0 ); 00292 } 00293 00294 // Handle stderr 00295 if (stderr_disp == Discard_Stderr) 00296 { 00297 int null_fd = open("/dev/null", O_WRONLY); 00298 dup2(null_fd, 2); 00299 ::close(null_fd); 00300 } 00301 else if (stderr_disp == Stderr_To_Stdout) 00302 { 00303 dup2(1, 2); 00304 } 00305 else if (stderr_disp == Stderr_To_FileDesc) 00306 { 00307 // Note: We don't have to close anything regarding stderr_fd. 00308 // Our caller is responsible for that. 00309 dup2 (stderr_fd, 2); 00310 } 00311 00312 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) { 00313 setenv( it->first.c_str(), it->second.c_str(), 1 ); 00314 } 00315 00316 if(default_locale) 00317 setenv("LC_ALL","C",1); 00318 00319 if(root) 00320 { 00321 if(chroot(root) == -1) 00322 { 00323 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) ); 00324 std::cerr << _execError << endl;// After fork log on stderr too 00325 _exit (128); // No sense in returning! I am forked away!! 00326 } 00327 if(chdir("/") == -1) 00328 { 00329 _execError = str::form( _("Can't chdir to '/' inside chroot (%s)."), strerror(errno) ); 00330 std::cerr << _execError << endl;// After fork log on stderr too 00331 _exit (128); // No sense in returning! I am forked away!! 00332 } 00333 } 00334 00335 // close all filedesctiptors above stderr 00336 for ( int i = ::getdtablesize() - 1; i > 2; --i ) { 00337 ::close( i ); 00338 } 00339 00340 execvp(argv[0], const_cast<char *const *>(argv)); 00341 // don't want to get here 00342 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) ); 00343 std::cerr << _execError << endl;// After fork log on stderr too 00344 _exit (129); // No sense in returning! I am forked away!! 00346 } 00347 00348 else if (pid == -1) // Fork failed, close everything. 00349 { 00350 _execError = str::form( _("Can't fork (%s)."), strerror(errno) ); 00351 _exitStatus = 127; 00352 ERR << _execError << endl; 00353 00354 if (use_pty) { 00355 ::close(master_tty); 00356 ::close(slave_tty); 00357 } 00358 else { 00359 ::close(to_external[0]); 00360 ::close(to_external[1]); 00361 ::close(from_external[0]); 00362 ::close(from_external[1]); 00363 } 00364 } 00365 00366 else { 00367 if (use_pty) 00368 { 00369 ::close(slave_tty); // belongs to child process 00370 inputfile = fdopen(master_tty, "r"); 00371 outputfile = fdopen(master_tty, "w"); 00372 } 00373 else 00374 { 00375 ::close(to_external[0]); // belongs to child process 00376 ::close(from_external[1]); // belongs to child process 00377 inputfile = fdopen(from_external[0], "r"); 00378 outputfile = fdopen(to_external[1], "w"); 00379 } 00380 00381 DBG << "pid " << pid << " launched" << endl; 00382 00383 if (!inputfile || !outputfile) 00384 { 00385 ERR << "Cannot create streams to external program " << argv[0] << endl; 00386 close(); 00387 } 00388 } 00389 } 00390 00391 00392 int 00393 ExternalProgram::close() 00394 { 00395 if (pid > 0) 00396 { 00397 if ( inputFile() ) 00398 { 00399 // Discard any output instead of closing the pipe, 00400 // but watch out for the command exiting while some 00401 // subprocess keeps the filedescriptor open. 00402 setBlocking( false ); 00403 FILE * inputfile = inputFile(); 00404 int inputfileFd = ::fileno( inputfile ); 00405 long delay = 0; 00406 do 00407 { 00408 /* Watch inputFile to see when it has input. */ 00409 fd_set rfds; 00410 FD_ZERO( &rfds ); 00411 FD_SET( inputfileFd, &rfds ); 00412 00413 /* Wait up to 1 seconds. */ 00414 struct timeval tv; 00415 tv.tv_sec = (delay < 0 ? 1 : 0); 00416 tv.tv_usec = (delay < 0 ? 0 : delay*100000); 00417 if ( delay >= 0 && ++delay > 9 ) 00418 delay = -1; 00419 int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv ); 00420 00421 if ( retval == -1 ) 00422 { 00423 ERR << "select error: " << strerror(errno) << endl; 00424 if ( errno != EINTR ) 00425 break; 00426 } 00427 else if ( retval ) 00428 { 00429 // Data is available now. 00430 static size_t linebuffer_size = 0; // static because getline allocs 00431 static char * linebuffer = 0; // and reallocs if buffer is too small 00432 getline( &linebuffer, &linebuffer_size, inputfile ); 00433 // ::feof check is important as select returns 00434 // positive if the file was closed. 00435 if ( ::feof( inputfile ) ) 00436 break; 00437 clearerr( inputfile ); 00438 } 00439 else 00440 { 00441 // No data within time. 00442 if ( ! running() ) 00443 break; 00444 } 00445 } while ( true ); 00446 } 00447 00448 // Wait for child to exit 00449 int ret; 00450 int status = 0; 00451 do 00452 { 00453 ret = waitpid(pid, &status, 0); 00454 } 00455 while (ret == -1 && errno == EINTR); 00456 00457 if (ret != -1) 00458 { 00459 _exitStatus = checkStatus( status ); 00460 } 00461 pid = -1; 00462 } 00463 00464 return _exitStatus; 00465 } 00466 00467 00468 int ExternalProgram::checkStatus( int status ) 00469 { 00470 if (WIFEXITED (status)) 00471 { 00472 status = WEXITSTATUS (status); 00473 if(status) 00474 { 00475 DBG << "Pid " << pid << " exited with status " << status << endl; 00476 _execError = str::form( _("Command exited with status %d."), status ); 00477 } 00478 else 00479 { 00480 // if 'launch' is logged, completion should be logged, 00481 // even if successfull. 00482 DBG << "Pid " << pid << " successfully completed" << endl; 00483 _execError.clear(); // empty if running or successfully completed 00484 } 00485 } 00486 else if (WIFSIGNALED (status)) 00487 { 00488 status = WTERMSIG (status); 00489 WAR << "Pid " << pid << " was killed by signal " << status 00490 << " (" << strsignal(status); 00491 if (WCOREDUMP (status)) 00492 { 00493 WAR << ", core dumped"; 00494 } 00495 WAR << ")" << endl; 00496 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) ); 00497 status+=128; 00498 } 00499 else { 00500 ERR << "Pid " << pid << " exited with unknown error" << endl; 00501 _execError = _("Command exited with unknown error."); 00502 } 00503 00504 return status; 00505 } 00506 00507 bool 00508 ExternalProgram::kill() 00509 { 00510 if (pid > 0) 00511 { 00512 ::kill(pid, SIGKILL); 00513 close(); 00514 } 00515 return true; 00516 } 00517 00518 00519 bool 00520 ExternalProgram::running() 00521 { 00522 if ( pid < 0 ) return false; 00523 00524 int status = 0; 00525 int p = waitpid( pid, &status, WNOHANG ); 00526 switch ( p ) 00527 { 00528 case -1: 00529 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl; 00530 return false; 00531 break; 00532 case 0: 00533 return true; // still running 00534 break; 00535 } 00536 00537 // Here: completed... 00538 _exitStatus = checkStatus( status ); 00539 pid = -1; 00540 return false; 00541 } 00542 00543 // origfd will be accessible as newfd and closed (unless they were equal) 00544 void ExternalProgram::renumber_fd (int origfd, int newfd) 00545 { 00546 // It may happen that origfd is already the one we want 00547 // (Although in our circumstances, that would mean somebody has closed 00548 // our stdin or stdout... weird but has appened to Cray, #49797) 00549 if (origfd != newfd) 00550 { 00551 dup2 (origfd, newfd); 00552 ::close (origfd); 00553 } 00554 } 00555 00556 std::ostream & ExternalProgram::operator>>( std::ostream & out_r ) 00557 { 00558 setBlocking( true ); 00559 for ( std::string line = receiveLine(); line.length(); line = receiveLine() ) 00560 out_r << line; 00561 return out_r; 00562 } 00563 00565 // 00566 // class ExternalProgramWithStderr 00567 // 00569 00570 namespace _ExternalProgram 00571 { 00572 EarlyPipe::EarlyPipe() 00573 { 00574 _fds[R] = _fds[W] = -1; 00575 #ifdef HAVE_PIPE2 00576 ::pipe2( _fds, O_NONBLOCK ); 00577 #else 00578 ::pipe( _fds ); 00579 ::fcntl(_fds[R], F_SETFD, O_NONBLOCK ); 00580 ::fcntl(_fds[W], F_SETFD, O_NONBLOCK ); 00581 #endif 00582 _stderr = ::fdopen( _fds[R], "r" ); 00583 } 00584 00585 EarlyPipe::~EarlyPipe() 00586 { 00587 closeW(); 00588 if ( _stderr ) 00589 ::fclose( _stderr ); 00590 } 00591 } 00592 00593 bool ExternalProgramWithStderr::stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r ) 00594 { 00595 if ( ! _stderr ) 00596 return false; 00597 if ( delim_r && ! _buffer.empty() ) 00598 { 00599 // check for delim already in buffer 00600 std::string::size_type pos( _buffer.find( delim_r ) ); 00601 if ( pos != std::string::npos ) 00602 { 00603 retval_r = _buffer.substr( 0, returnDelim_r ? pos+1 : pos ); 00604 _buffer.erase( 0, pos+1 ); 00605 return true; 00606 } 00607 } 00608 ::clearerr( _stderr ); 00609 do { 00610 int ch = fgetc( _stderr ); 00611 if ( ch != EOF ) 00612 { 00613 if ( ch != delim_r || ! delim_r ) 00614 _buffer.push_back( ch ); 00615 else 00616 { 00617 if ( returnDelim_r ) 00618 _buffer.push_back( delim_r ); 00619 break; 00620 } 00621 } 00622 else if ( ::feof( _stderr ) ) 00623 { 00624 if ( _buffer.empty() ) 00625 return false; 00626 break; 00627 } 00628 else if ( errno != EINTR ) 00629 return false; 00630 } while ( true ); 00631 // HERE: we left after readig at least one char (\n) 00632 retval_r.swap( _buffer ); 00633 _buffer.clear(); 00634 return true; 00635 } 00636 00637 00638 } // namespace zypp