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()
40 ExternalProgram::ExternalProgram( std::string commandline,
41 Stderr_Disposition stderr_disp,
45 const Pathname & root )
51 argv[2] = commandline.c_str();
54 const char* rootdir = NULL;
55 if(!root.empty() && root != "/")
57 rootdir = root.asString().c_str();
59 Environment environment;
60 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
64 ExternalProgram::ExternalProgram( const char *const *argv,
65 Stderr_Disposition stderr_disp,
69 const Pathname & root )
72 const char* rootdir = NULL;
73 if(!root.empty() && root != "/")
75 rootdir = root.asString().c_str();
77 Environment environment;
78 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
82 ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
83 Stderr_Disposition stderr_disp, bool use_pty,
84 int stderr_fd, bool default_locale,
88 const char* rootdir = NULL;
89 if(!root.empty() && root != "/")
91 rootdir = root.asString().c_str();
93 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
97 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
104 const char *argv[i + 1];
106 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
107 Environment environment;
108 start_program (argv, environment);
112 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
119 const char *argv[i + 1];
121 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
122 start_program (argv, environment);
126 ExternalProgram::~ExternalProgram()
132 ExternalProgram::start_program (const char *const *argv, const Environment & environment,
133 Stderr_Disposition stderr_disp,
134 int stderr_fd, bool default_locale, const char* root)
138 int to_external[2], from_external[2]; // fds for pair of pipes
139 int master_tty, slave_tty; // fds for pair of ttys
141 // do not remove the single quotes around every argument, copy&paste of
142 // command to shell will not work otherwise!
145 for (int i = 0; argv[i]; i++)
147 if (i>0) cmdstr << ' ';
152 _command = cmdstr.str();
154 DBG << "Executing " << _command << endl;
159 // Create pair of ttys
160 DBG << "Using ttys for communication with " << argv[0] << endl;
161 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
163 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
165 ERR << _execError << endl;
171 // Create pair of pipes
172 if (pipe (to_external) != 0 || pipe (from_external) != 0)
174 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
176 ERR << _execError << endl;
181 // Create module process
182 if ((pid = fork()) == 0)
188 dup2 (slave_tty, 1); // set new stdout
189 renumber_fd (slave_tty, 0); // set new stdin
190 ::close(master_tty); // Belongs to father process
192 // We currently have no controlling terminal (due to setsid).
193 // The first open call will also set the new ctty (due to historical
194 // unix guru knowledge ;-) )
197 ttyname_r(slave_tty, name, sizeof(name));
198 ::close(open(name, O_RDONLY));
202 renumber_fd (to_external[0], 0); // set new stdin
203 ::close(from_external[0]); // Belongs to father process
205 renumber_fd (from_external[1], 1); // set new stdout
206 ::close(to_external [1]); // Belongs to father process
210 if (stderr_disp == Discard_Stderr)
212 int null_fd = open("/dev/null", O_WRONLY);
216 else if (stderr_disp == Stderr_To_Stdout)
220 else if (stderr_disp == Stderr_To_FileDesc)
222 // Note: We don't have to close anything regarding stderr_fd.
223 // Our caller is responsible for that.
227 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
228 setenv( it->first.c_str(), it->second.c_str(), 1 );
232 setenv("LC_ALL","C",1);
236 if(chroot(root) == -1)
238 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
239 ERR << _execError << endl;
240 std::cerr << _execError << endl;// After fork log on stderr too
241 _exit (128); // No sense in returning! I am forked away!!
245 _execError = str::form( _("Can't chdir to '/' inside chroot (%s)."), strerror(errno) );
246 ERR << _execError << endl;
247 std::cerr << _execError << endl;// After fork log on stderr too
248 _exit (128); // No sense in returning! I am forked away!!
252 // close all filedesctiptors above stderr
253 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
257 execvp(argv[0], const_cast<char *const *>(argv));
258 // don't want to get here
259 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
260 ERR << _execError << endl;
261 std::cerr << _execError << endl;// After fork log on stderr too
262 _exit (129); // No sense in returning! I am forked away!!
265 else if (pid == -1) // Fork failed, close everything.
267 _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
269 ERR << _execError << endl;
276 ::close(to_external[0]);
277 ::close(to_external[1]);
278 ::close(from_external[0]);
279 ::close(from_external[1]);
286 ::close(slave_tty); // belongs to child process
287 inputfile = fdopen(master_tty, "r");
288 outputfile = fdopen(master_tty, "w");
292 ::close(to_external[0]); // belongs to child process
293 ::close(from_external[1]); // belongs to child process
294 inputfile = fdopen(from_external[0], "r");
295 outputfile = fdopen(to_external[1], "w");
298 DBG << "pid " << pid << " launched" << endl;
300 if (!inputfile || !outputfile)
302 ERR << "Cannot create streams to external program " << argv[0] << endl;
310 ExternalProgram::close()
314 ExternalDataSource::close();
315 // Wait for child to exit
320 ret = waitpid(pid, &status, 0);
322 while (ret == -1 && errno == EINTR);
326 status = checkStatus( status );
338 int ExternalProgram::checkStatus( int status )
340 if (WIFEXITED (status))
342 status = WEXITSTATUS (status);
345 DBG << "Pid " << pid << " exited with status " << status << endl;
346 _execError = str::form( _("Command exited with status %d."), status );
350 // if 'launch' is logged, completion should be logged,
351 // even if successfull.
352 DBG << "Pid " << pid << " successfully completed" << endl;
353 //_execError = _("Command successfully completed.");
356 else if (WIFSIGNALED (status))
358 status = WTERMSIG (status);
359 WAR << "Pid " << pid << " was killed by signal " << status
360 << " (" << strsignal(status);
361 if (WCOREDUMP (status))
363 WAR << ", core dumped";
366 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
370 ERR << "Pid " << pid << " exited with unknown error" << endl;
371 _execError = _("Command exited with unknown error.");
378 ExternalProgram::kill()
382 ::kill(pid, SIGKILL);
390 ExternalProgram::running()
392 if ( pid < 0 ) return false;
395 int p = waitpid( pid, &status, WNOHANG );
399 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
403 return true; // still running
407 // Here: completed...
408 _exitStatus = checkStatus( status );
413 // origfd will be accessible as newfd and closed (unless they were equal)
414 void ExternalProgram::renumber_fd (int origfd, int newfd)
416 // It may happen that origfd is already the one we want
417 // (Although in our circumstances, that would mean somebody has closed
418 // our stdin or stdout... weird but has appened to Cray, #49797)
421 dup2 (origfd, newfd);