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/ExternalProgram.h"
00028
00029 using namespace std;
00030
00031 namespace zypp {
00032
00033 ExternalProgram::ExternalProgram()
00034 : use_pty (false)
00035 {
00036 }
00037
00038 ExternalProgram::ExternalProgram( std::string commandline,
00039 Stderr_Disposition stderr_disp,
00040 bool use_pty,
00041 int stderr_fd,
00042 bool default_locale,
00043 const Pathname & root )
00044 : use_pty (use_pty)
00045 {
00046 const char *argv[4];
00047 argv[0] = "/bin/sh";
00048 argv[1] = "-c";
00049 argv[2] = commandline.c_str();
00050 argv[3] = 0;
00051
00052 const char* rootdir = NULL;
00053 if(!root.empty() && root != "/")
00054 {
00055 rootdir = root.asString().c_str();
00056 }
00057 Environment environment;
00058 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00059 }
00060
00061
00062 ExternalProgram::ExternalProgram( const char *const *argv,
00063 Stderr_Disposition stderr_disp,
00064 bool use_pty,
00065 int stderr_fd,
00066 bool default_locale,
00067 const Pathname & root )
00068 : use_pty (use_pty)
00069 {
00070 const char* rootdir = NULL;
00071 if(!root.empty() && root != "/")
00072 {
00073 rootdir = root.asString().c_str();
00074 }
00075 Environment environment;
00076 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00077 }
00078
00079
00080 ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
00081 Stderr_Disposition stderr_disp, bool use_pty,
00082 int stderr_fd, bool default_locale,
00083 const Pathname& root)
00084 : use_pty (use_pty)
00085 {
00086 const char* rootdir = NULL;
00087 if(!root.empty() && root != "/")
00088 {
00089 rootdir = root.asString().c_str();
00090 }
00091 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
00092 }
00093
00094
00095 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
00096 bool use_pty)
00097 : use_pty (use_pty)
00098 {
00099 int i = 0;
00100 while (argv_1[i++])
00101 ;
00102 const char *argv[i + 1];
00103 argv[0] = binpath;
00104 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
00105 Environment environment;
00106 start_program (argv, environment);
00107 }
00108
00109
00110 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
00111 bool use_pty)
00112 : use_pty (use_pty)
00113 {
00114 int i = 0;
00115 while (argv_1[i++])
00116 ;
00117 const char *argv[i + 1];
00118 argv[0] = binpath;
00119 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
00120 start_program (argv, environment);
00121 }
00122
00123
00124 ExternalProgram::~ExternalProgram()
00125 {
00126 }
00127
00128
00129 void
00130 ExternalProgram::start_program (const char *const *argv, const Environment & environment,
00131 Stderr_Disposition stderr_disp,
00132 int stderr_fd, bool default_locale, const char* root)
00133 {
00134 pid = -1;
00135 _exitStatus = 0;
00136 int to_external[2], from_external[2];
00137 int master_tty, slave_tty;
00138
00139 if (use_pty)
00140 {
00141
00142 DBG << "Using ttys for communication with " << argv[0] << endl;
00143 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
00144 {
00145 ERR << "openpty failed" << endl;
00146 return;
00147 }
00148 }
00149 else
00150 {
00151
00152 if (pipe (to_external) != 0 || pipe (from_external) != 0)
00153 {
00154 ERR << "pipe failed" << endl;
00155 return;
00156 }
00157 }
00158
00159
00160
00161
00162 stringstream cmdstr;
00163
00164 cmdstr << "Executing ";
00165 for (int i = 0; argv[i]; i++)
00166 {
00167 if (i>0) cmdstr << ' ';
00168 cmdstr << '\'';
00169 cmdstr << argv[i];
00170 cmdstr << '\'';
00171 }
00172 DBG << cmdstr.str() << endl;
00173
00174
00175 if ((pid = fork()) == 0)
00176 {
00177 if (use_pty)
00178 {
00179 setsid();
00180 if(slave_tty != 1)
00181 dup2 (slave_tty, 1);
00182 renumber_fd (slave_tty, 0);
00183 ::close(master_tty);
00184
00185
00186
00187
00188
00189 char name[512];
00190 ttyname_r(slave_tty, name, sizeof(name));
00191 ::close(open(name, O_RDONLY));
00192 }
00193 else
00194 {
00195 renumber_fd (to_external[0], 0);
00196 ::close(from_external[0]);
00197
00198 renumber_fd (from_external[1], 1);
00199 ::close(to_external [1]);
00200 }
00201
00202
00203 if (stderr_disp == Discard_Stderr)
00204 {
00205 int null_fd = open("/dev/null", O_WRONLY);
00206 dup2(null_fd, 2);
00207 ::close(null_fd);
00208 }
00209 else if (stderr_disp == Stderr_To_Stdout)
00210 {
00211 dup2(1, 2);
00212 }
00213 else if (stderr_disp == Stderr_To_FileDesc)
00214 {
00215
00216
00217 dup2 (stderr_fd, 2);
00218 }
00219
00220 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
00221 setenv( it->first.c_str(), it->second.c_str(), 1 );
00222 }
00223
00224 if(default_locale)
00225 setenv("LC_ALL","C",1);
00226
00227 if(root)
00228 {
00229 if(chroot(root) == -1)
00230 {
00231 ERR << "chroot to " << root << " failed: " << strerror(errno) << endl;
00232 _exit (3);
00233 }
00234 if(chdir("/") == -1)
00235 {
00236 ERR << "chdir to / inside chroot failed: " << strerror(errno) << endl;
00237 _exit (4);
00238 }
00239 }
00240
00241
00242 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
00243 ::close( i );
00244 }
00245
00246 execvp(argv[0], const_cast<char *const *>(argv));
00247 ERR << "Cannot execute external program "
00248 << argv[0] << ":" << strerror(errno) << endl;
00249 _exit (5);
00250 }
00251
00252 else if (pid == -1)
00253 {
00254 if (use_pty) {
00255 ::close(master_tty);
00256 ::close(slave_tty);
00257 }
00258 else {
00259 ::close(to_external[0]);
00260 ::close(to_external[1]);
00261 ::close(from_external[0]);
00262 ::close(from_external[1]);
00263 }
00264 ERR << "Cannot fork " << strerror(errno) << endl;
00265 _exitStatus = -127;
00266 }
00267
00268 else {
00269 if (use_pty)
00270 {
00271 ::close(slave_tty);
00272 inputfile = fdopen(master_tty, "r");
00273 outputfile = fdopen(master_tty, "w");
00274 }
00275 else
00276 {
00277 ::close(to_external[0]);
00278 ::close(from_external[1]);
00279 inputfile = fdopen(from_external[0], "r");
00280 outputfile = fdopen(to_external[1], "w");
00281 }
00282
00283 DBG << "pid " << pid << " launched" << endl;
00284
00285 if (!inputfile || !outputfile)
00286 {
00287 ERR << "Cannot create streams to external program " << argv[0] << endl;
00288 close();
00289 }
00290 }
00291 }
00292
00293
00294 int
00295 ExternalProgram::close()
00296 {
00297 if (pid > 0)
00298 {
00299 ExternalDataSource::close();
00300
00301 int ret;
00302 int status = 0;
00303 do
00304 {
00305 ret = waitpid(pid, &status, 0);
00306 }
00307 while (ret == -1 && errno == EINTR);
00308
00309 if (ret != -1)
00310 {
00311 status = checkStatus( status );
00312 }
00313 pid = -1;
00314 return status;
00315 }
00316 else
00317 {
00318 return _exitStatus;
00319 }
00320 }
00321
00322
00323 int ExternalProgram::checkStatus( int status )
00324 {
00325 if (WIFEXITED (status))
00326 {
00327 status = WEXITSTATUS (status);
00328 if(status)
00329 {
00330 DBG << "pid " << pid << " exited with status " << status << endl;
00331 }
00332 else
00333 {
00334
00335
00336 DBG << "pid " << pid << " successfully completed" << endl;
00337 }
00338 }
00339 else if (WIFSIGNALED (status))
00340 {
00341 status = WTERMSIG (status);
00342 WAR << "pid " << pid << " was killed by signal " << status
00343 << " (" << strsignal(status);
00344 if (WCOREDUMP (status))
00345 {
00346 WAR << ", core dumped";
00347 }
00348 WAR << ")" << endl;
00349 status+=128;
00350 }
00351 else {
00352 ERR << "pid " << pid << " exited with unknown error" << endl;
00353 }
00354
00355 return status;
00356 }
00357
00358 bool
00359 ExternalProgram::kill()
00360 {
00361 if (pid > 0)
00362 {
00363 ::kill(pid, SIGKILL);
00364 close();
00365 }
00366 return true;
00367 }
00368
00369
00370 bool
00371 ExternalProgram::running()
00372 {
00373 if ( pid < 0 ) return false;
00374
00375 int status = 0;
00376 int p = waitpid( pid, &status, WNOHANG );
00377 switch ( p )
00378 {
00379 case -1:
00380 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
00381 return false;
00382 break;
00383 case 0:
00384 return true;
00385 break;
00386 }
00387
00388
00389 _exitStatus = checkStatus( status );
00390 pid = -1;
00391 return false;
00392 }
00393
00394
00395 void ExternalProgram::renumber_fd (int origfd, int newfd)
00396 {
00397
00398
00399
00400 if (origfd != newfd)
00401 {
00402 dup2 (origfd, newfd);
00403 ::close (origfd);
00404 }
00405 }
00406
00407 }