00001
00002
00003
00004
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>
00020 #include <stdlib.h>
00021
00022 #include <cstring>
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 {
00038 }
00039
00040
00041 ExternalProgram::ExternalProgram( std::string commandline,
00042 Stderr_Disposition stderr_disp,
00043 bool use_pty,
00044 int stderr_fd,
00045 bool default_locale,
00046 const Pathname & root )
00047 : use_pty (use_pty)
00048 {
00049 const char *argv[4];
00050 argv[0] = "/bin/sh";
00051 argv[1] = "-c";
00052 argv[2] = commandline.c_str();
00053 argv[3] = 0;
00054
00055 const char* rootdir = NULL;
00056 if(!root.empty() && root != "/")
00057 {
00058 rootdir = root.asString().c_str();
00059 }
00060 Environment environment;
00061 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00062 }
00063
00064
00065 ExternalProgram::ExternalProgram (const Arguments &argv,
00066 Stderr_Disposition stderr_disp,
00067 bool use_pty, int stderr_fd,
00068 bool default_locale,
00069 const Pathname& root)
00070 : use_pty (use_pty)
00071 {
00072 const char * argvp[argv.size() + 1];
00073 unsigned c = 0;
00074 for_( i, argv.begin(), argv.end() )
00075 {
00076 argvp[c] = i->c_str();
00077 ++c;
00078 }
00079 argvp[c] = 0;
00080
00081 Environment environment;
00082 const char* rootdir = NULL;
00083 if(!root.empty() && root != "/")
00084 {
00085 rootdir = root.asString().c_str();
00086 }
00087 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00088 }
00089
00090
00091 ExternalProgram::ExternalProgram (const Arguments &argv,
00092 const Environment & environment,
00093 Stderr_Disposition stderr_disp,
00094 bool use_pty, int stderr_fd,
00095 bool default_locale,
00096 const Pathname& root)
00097 : use_pty (use_pty)
00098 {
00099 const char * argvp[argv.size() + 1];
00100 unsigned c = 0;
00101 for_( i, argv.begin(), argv.end() )
00102 {
00103 argvp[c] = i->c_str();
00104 ++c;
00105 }
00106 argvp[c] = 0;
00107
00108 const char* rootdir = NULL;
00109 if(!root.empty() && root != "/")
00110 {
00111 rootdir = root.asString().c_str();
00112 }
00113 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00114
00115 }
00116
00117
00118
00119
00120 ExternalProgram::ExternalProgram( const char *const *argv,
00121 Stderr_Disposition stderr_disp,
00122 bool use_pty,
00123 int stderr_fd,
00124 bool default_locale,
00125 const Pathname & root )
00126 : use_pty (use_pty)
00127 {
00128 const char* rootdir = NULL;
00129 if(!root.empty() && root != "/")
00130 {
00131 rootdir = root.asString().c_str();
00132 }
00133 Environment environment;
00134 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00135 }
00136
00137
00138 ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
00139 Stderr_Disposition stderr_disp, bool use_pty,
00140 int stderr_fd, bool default_locale,
00141 const Pathname& root)
00142 : use_pty (use_pty)
00143 {
00144 const char* rootdir = NULL;
00145 if(!root.empty() && root != "/")
00146 {
00147 rootdir = root.asString().c_str();
00148 }
00149 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00150 }
00151
00152
00153 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
00154 bool use_pty)
00155 : use_pty (use_pty)
00156 {
00157 int i = 0;
00158 while (argv_1[i++])
00159 ;
00160 const char *argv[i + 1];
00161 argv[0] = binpath;
00162 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
00163 Environment environment;
00164 start_program (argv, environment);
00165 }
00166
00167
00168 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
00169 bool use_pty)
00170 : use_pty (use_pty)
00171 {
00172 int i = 0;
00173 while (argv_1[i++])
00174 ;
00175 const char *argv[i + 1];
00176 argv[0] = binpath;
00177 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
00178 start_program (argv, environment);
00179 }
00180
00181
00182 ExternalProgram::~ExternalProgram()
00183 {
00184 }
00185
00186
00187 void
00188 ExternalProgram::start_program (const char *const *argv, const Environment & environment,
00189 Stderr_Disposition stderr_disp,
00190 int stderr_fd, bool default_locale, const char* root)
00191 {
00192 pid = -1;
00193 _exitStatus = 0;
00194 int to_external[2], from_external[2];
00195 int master_tty, slave_tty;
00196
00197 const char * redirectStdin = 0;
00198 if ( argv[0] && *argv[0] == '<' )
00199 {
00200 redirectStdin = argv[0]+1;
00201 if ( *redirectStdin == '\0' )
00202 redirectStdin = "/dev/null";
00203 ++argv;
00204 }
00205
00206
00207
00208 {
00209 stringstream cmdstr;
00210 for (int i = 0; argv[i]; i++)
00211 {
00212 if (i>0) cmdstr << ' ';
00213 cmdstr << '\'';
00214 cmdstr << argv[i];
00215 cmdstr << '\'';
00216 }
00217 if ( redirectStdin )
00218 cmdstr << " < '" << redirectStdin << "'";
00219 _command = cmdstr.str();
00220 }
00221 DBG << "Executing " << _command << endl;
00222
00223
00224 if (use_pty)
00225 {
00226
00227 DBG << "Using ttys for communication with " << argv[0] << endl;
00228 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
00229 {
00230 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
00231 _exitStatus = 126;
00232 ERR << _execError << endl;
00233 return;
00234 }
00235 }
00236 else
00237 {
00238
00239 if (pipe (to_external) != 0 || pipe (from_external) != 0)
00240 {
00241 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
00242 _exitStatus = 126;
00243 ERR << _execError << endl;
00244 return;
00245 }
00246 }
00247
00248
00249 if ((pid = fork()) == 0)
00250 {
00252
00254 if (use_pty)
00255 {
00256 setsid();
00257 if(slave_tty != 1)
00258 dup2 (slave_tty, 1);
00259 renumber_fd (slave_tty, 0);
00260 ::close(master_tty);
00261
00262
00263
00264
00265
00266 char name[512];
00267 ttyname_r(slave_tty, name, sizeof(name));
00268 ::close(open(name, O_RDONLY));
00269 }
00270 else
00271 {
00272 renumber_fd (to_external[0], 0);
00273 ::close(from_external[0]);
00274
00275 renumber_fd (from_external[1], 1);
00276 ::close(to_external [1]);
00277 }
00278
00279 if ( redirectStdin )
00280 {
00281 ::close( 0 );
00282 int inp_fd = open( redirectStdin, O_RDONLY );
00283 dup2( inp_fd, 0 );
00284 }
00285
00286
00287 if (stderr_disp == Discard_Stderr)
00288 {
00289 int null_fd = open("/dev/null", O_WRONLY);
00290 dup2(null_fd, 2);
00291 ::close(null_fd);
00292 }
00293 else if (stderr_disp == Stderr_To_Stdout)
00294 {
00295 dup2(1, 2);
00296 }
00297 else if (stderr_disp == Stderr_To_FileDesc)
00298 {
00299
00300
00301 dup2 (stderr_fd, 2);
00302 }
00303
00304 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
00305 setenv( it->first.c_str(), it->second.c_str(), 1 );
00306 }
00307
00308 if(default_locale)
00309 setenv("LC_ALL","C",1);
00310
00311 if(root)
00312 {
00313 if(chroot(root) == -1)
00314 {
00315 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
00316 std::cerr << _execError << endl;
00317 _exit (128);
00318 }
00319 if(chdir("/") == -1)
00320 {
00321 _execError = str::form( _("Can't chdir to '/' inside chroot (%s)."), strerror(errno) );
00322 std::cerr << _execError << endl;
00323 _exit (128);
00324 }
00325 }
00326
00327
00328 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
00329 ::close( i );
00330 }
00331
00332 execvp(argv[0], const_cast<char *const *>(argv));
00333
00334 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
00335 std::cerr << _execError << endl;
00336 _exit (129);
00338 }
00339
00340 else if (pid == -1)
00341 {
00342 _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
00343 _exitStatus = 127;
00344 ERR << _execError << endl;
00345
00346 if (use_pty) {
00347 ::close(master_tty);
00348 ::close(slave_tty);
00349 }
00350 else {
00351 ::close(to_external[0]);
00352 ::close(to_external[1]);
00353 ::close(from_external[0]);
00354 ::close(from_external[1]);
00355 }
00356 }
00357
00358 else {
00359 if (use_pty)
00360 {
00361 ::close(slave_tty);
00362 inputfile = fdopen(master_tty, "r");
00363 outputfile = fdopen(master_tty, "w");
00364 }
00365 else
00366 {
00367 ::close(to_external[0]);
00368 ::close(from_external[1]);
00369 inputfile = fdopen(from_external[0], "r");
00370 outputfile = fdopen(to_external[1], "w");
00371 }
00372
00373 DBG << "pid " << pid << " launched" << endl;
00374
00375 if (!inputfile || !outputfile)
00376 {
00377 ERR << "Cannot create streams to external program " << argv[0] << endl;
00378 close();
00379 }
00380 }
00381 }
00382
00383
00384 int
00385 ExternalProgram::close()
00386 {
00387 if (pid > 0)
00388 {
00389 setBlocking( true );
00390 while ( receiveLine().length() )
00391 ;
00392
00393
00394
00395 int ret;
00396 int status = 0;
00397 do
00398 {
00399 ret = waitpid(pid, &status, 0);
00400 }
00401 while (ret == -1 && errno == EINTR);
00402
00403 if (ret != -1)
00404 {
00405 status = checkStatus( status );
00406 }
00407 pid = -1;
00408 return status;
00409 }
00410 else
00411 {
00412 return _exitStatus;
00413 }
00414 }
00415
00416
00417 int ExternalProgram::checkStatus( int status )
00418 {
00419 if (WIFEXITED (status))
00420 {
00421 status = WEXITSTATUS (status);
00422 if(status)
00423 {
00424 DBG << "Pid " << pid << " exited with status " << status << endl;
00425 _execError = str::form( _("Command exited with status %d."), status );
00426 }
00427 else
00428 {
00429
00430
00431 DBG << "Pid " << pid << " successfully completed" << endl;
00432
00433 }
00434 }
00435 else if (WIFSIGNALED (status))
00436 {
00437 status = WTERMSIG (status);
00438 WAR << "Pid " << pid << " was killed by signal " << status
00439 << " (" << strsignal(status);
00440 if (WCOREDUMP (status))
00441 {
00442 WAR << ", core dumped";
00443 }
00444 WAR << ")" << endl;
00445 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
00446 status+=128;
00447 }
00448 else {
00449 ERR << "Pid " << pid << " exited with unknown error" << endl;
00450 _execError = _("Command exited with unknown error.");
00451 }
00452
00453 return status;
00454 }
00455
00456 bool
00457 ExternalProgram::kill()
00458 {
00459 if (pid > 0)
00460 {
00461 ::kill(pid, SIGKILL);
00462 close();
00463 }
00464 return true;
00465 }
00466
00467
00468 bool
00469 ExternalProgram::running()
00470 {
00471 if ( pid < 0 ) return false;
00472
00473 int status = 0;
00474 int p = waitpid( pid, &status, WNOHANG );
00475 switch ( p )
00476 {
00477 case -1:
00478 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
00479 return false;
00480 break;
00481 case 0:
00482 return true;
00483 break;
00484 }
00485
00486
00487 _exitStatus = checkStatus( status );
00488 pid = -1;
00489 return false;
00490 }
00491
00492
00493 void ExternalProgram::renumber_fd (int origfd, int newfd)
00494 {
00495
00496
00497
00498 if (origfd != newfd)
00499 {
00500 dup2 (origfd, newfd);
00501 ::close (origfd);
00502 }
00503 }
00504
00505 std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
00506 {
00507 setBlocking( true );
00508 for ( std::string line = receiveLine(); line.length(); line = receiveLine() )
00509 out_r << line;
00510 return out_r;
00511 }
00512
00513
00514 }