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()
41 ExternalProgram::ExternalProgram( std::string commandline,
42 Stderr_Disposition stderr_disp,
46 const Pathname & root )
52 argv[2] = commandline.c_str();
55 const char* rootdir = NULL;
56 if(!root.empty() && root != "/")
58 rootdir = root.asString().c_str();
60 Environment environment;
61 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
65 ExternalProgram::ExternalProgram (const Arguments &argv,
66 Stderr_Disposition stderr_disp,
67 bool use_pty, int stderr_fd,
72 const char * argvp[argv.size() + 1];
74 for_( i, argv.begin(), argv.end() )
76 argvp[c] = i->c_str();
81 Environment environment;
82 const char* rootdir = NULL;
83 if(!root.empty() && root != "/")
85 rootdir = root.asString().c_str();
87 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
91 ExternalProgram::ExternalProgram (const Arguments &argv,
92 const Environment & environment,
93 Stderr_Disposition stderr_disp,
94 bool use_pty, int stderr_fd,
99 const char * argvp[argv.size() + 1];
101 for_( i, argv.begin(), argv.end() )
103 argvp[c] = i->c_str();
108 const char* rootdir = NULL;
109 if(!root.empty() && root != "/")
111 rootdir = root.asString().c_str();
113 start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
120 ExternalProgram::ExternalProgram( const char *const *argv,
121 Stderr_Disposition stderr_disp,
125 const Pathname & root )
128 const char* rootdir = NULL;
129 if(!root.empty() && root != "/")
131 rootdir = root.asString().c_str();
133 Environment environment;
134 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
138 ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
139 Stderr_Disposition stderr_disp, bool use_pty,
140 int stderr_fd, bool default_locale,
141 const Pathname& root)
144 const char* rootdir = NULL;
145 if(!root.empty() && root != "/")
147 rootdir = root.asString().c_str();
149 start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
153 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
160 const char *argv[i + 1];
162 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
163 Environment environment;
164 start_program (argv, environment);
168 ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
175 const char *argv[i + 1];
177 memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
178 start_program (argv, environment);
182 ExternalProgram::~ExternalProgram()
188 ExternalProgram::start_program (const char *const *argv, const Environment & environment,
189 Stderr_Disposition stderr_disp,
190 int stderr_fd, bool default_locale, const char* root)
194 int to_external[2], from_external[2]; // fds for pair of pipes
195 int master_tty, slave_tty; // fds for pair of ttys
197 // do not remove the single quotes around every argument, copy&paste of
198 // command to shell will not work otherwise!
201 for (int i = 0; argv[i]; i++)
203 if (i>0) cmdstr << ' ';
208 _command = cmdstr.str();
210 DBG << "Executing " << _command << endl;
215 // Create pair of ttys
216 DBG << "Using ttys for communication with " << argv[0] << endl;
217 if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
219 _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
221 ERR << _execError << endl;
227 // Create pair of pipes
228 if (pipe (to_external) != 0 || pipe (from_external) != 0)
230 _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
232 ERR << _execError << endl;
237 // Create module process
238 if ((pid = fork()) == 0)
240 //////////////////////////////////////////////////////////////////////
241 // Don't write to the logfile after fork!
242 //////////////////////////////////////////////////////////////////////
247 dup2 (slave_tty, 1); // set new stdout
248 renumber_fd (slave_tty, 0); // set new stdin
249 ::close(master_tty); // Belongs to father process
251 // We currently have no controlling terminal (due to setsid).
252 // The first open call will also set the new ctty (due to historical
253 // unix guru knowledge ;-) )
256 ttyname_r(slave_tty, name, sizeof(name));
257 ::close(open(name, O_RDONLY));
261 renumber_fd (to_external[0], 0); // set new stdin
262 ::close(from_external[0]); // Belongs to father process
264 renumber_fd (from_external[1], 1); // set new stdout
265 ::close(to_external [1]); // Belongs to father process
269 if (stderr_disp == Discard_Stderr)
271 int null_fd = open("/dev/null", O_WRONLY);
275 else if (stderr_disp == Stderr_To_Stdout)
279 else if (stderr_disp == Stderr_To_FileDesc)
281 // Note: We don't have to close anything regarding stderr_fd.
282 // Our caller is responsible for that.
286 for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
287 setenv( it->first.c_str(), it->second.c_str(), 1 );
291 setenv("LC_ALL","C",1);
295 if(chroot(root) == -1)
297 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
298 std::cerr << _execError << endl;// After fork log on stderr too
299 _exit (128); // No sense in returning! I am forked away!!
303 _execError = str::form( _("Can't chdir to '/' inside chroot (%s)."), strerror(errno) );
304 std::cerr << _execError << endl;// After fork log on stderr too
305 _exit (128); // No sense in returning! I am forked away!!
309 // close all filedesctiptors above stderr
310 for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
314 execvp(argv[0], const_cast<char *const *>(argv));
315 // don't want to get here
316 _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
317 std::cerr << _execError << endl;// After fork log on stderr too
318 _exit (129); // No sense in returning! I am forked away!!
319 //////////////////////////////////////////////////////////////////////
322 else if (pid == -1) // Fork failed, close everything.
324 _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
326 ERR << _execError << endl;
333 ::close(to_external[0]);
334 ::close(to_external[1]);
335 ::close(from_external[0]);
336 ::close(from_external[1]);
343 ::close(slave_tty); // belongs to child process
344 inputfile = fdopen(master_tty, "r");
345 outputfile = fdopen(master_tty, "w");
349 ::close(to_external[0]); // belongs to child process
350 ::close(from_external[1]); // belongs to child process
351 inputfile = fdopen(from_external[0], "r");
352 outputfile = fdopen(to_external[1], "w");
355 DBG << "pid " << pid << " launched" << endl;
357 if (!inputfile || !outputfile)
359 ERR << "Cannot create streams to external program " << argv[0] << endl;
367 ExternalProgram::close()
372 while ( receiveLine().length() )
373 ; // discard any output instead of closing the pipe
374 //ExternalDataSource::close();
376 // Wait for child to exit
381 ret = waitpid(pid, &status, 0);
383 while (ret == -1 && errno == EINTR);
387 status = checkStatus( status );
399 int ExternalProgram::checkStatus( int status )
401 if (WIFEXITED (status))
403 status = WEXITSTATUS (status);
406 DBG << "Pid " << pid << " exited with status " << status << endl;
407 _execError = str::form( _("Command exited with status %d."), status );
411 // if 'launch' is logged, completion should be logged,
412 // even if successfull.
413 DBG << "Pid " << pid << " successfully completed" << endl;
414 //_execError = _("Command successfully completed.");
417 else if (WIFSIGNALED (status))
419 status = WTERMSIG (status);
420 WAR << "Pid " << pid << " was killed by signal " << status
421 << " (" << strsignal(status);
422 if (WCOREDUMP (status))
424 WAR << ", core dumped";
427 _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
431 ERR << "Pid " << pid << " exited with unknown error" << endl;
432 _execError = _("Command exited with unknown error.");
439 ExternalProgram::kill()
443 ::kill(pid, SIGKILL);
451 ExternalProgram::running()
453 if ( pid < 0 ) return false;
456 int p = waitpid( pid, &status, WNOHANG );
460 ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
464 return true; // still running
468 // Here: completed...
469 _exitStatus = checkStatus( status );
474 // origfd will be accessible as newfd and closed (unless they were equal)
475 void ExternalProgram::renumber_fd (int origfd, int newfd)
477 // It may happen that origfd is already the one we want
478 // (Although in our circumstances, that would mean somebody has closed
479 // our stdin or stdout... weird but has appened to Cray, #49797)
482 dup2 (origfd, newfd);
487 std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
490 for ( std::string line = receiveLine(); line.length(); line = receiveLine() )