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