1 #include "private/forkspawnengine_p.h"
4 #include <zypp/base/LogControl.h>
5 #include <zypp/base/Gettext.h>
6 #include <zypp/base/IOTools.h>
7 #include <zypp-core/fs/PathInfo.h>
8 #include <zypp-core/zyppng/core/String>
9 #include <zypp-core/zyppng/base/EventDispatcher>
10 #include <zypp-core/zyppng/base/private/linuxhelpers_p.h>
11 #include <zypp-core/base/CleanerThread_p.h>
12 #include <zypp-core/base/LogControl.h>
21 #include <pty.h> // openpty
22 #include <stdlib.h> // setenv
23 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
25 zyppng::AbstractDirectSpawnEngine::~AbstractDirectSpawnEngine()
27 if ( AbstractDirectSpawnEngine::isRunning() ) {
28 // we got destructed while the external process is still alive
29 // make sure the zombie is cleaned up once it exits
30 zypp::CleanerThread::watchPID( _pid );
34 bool zyppng::AbstractDirectSpawnEngine::isRunning( bool wait )
36 if ( _pid < 0 ) return false;
39 int p = zyppng::eintrSafeCall( ::waitpid, _pid, &status, wait ? 0 : WNOHANG );
43 ERR << "waitpid( " << _pid << ") returned error '" << strerror(errno) << "'" << std::endl;
47 return true; // still running
52 _exitStatus = checkStatus( status );
57 void zyppng::AbstractDirectSpawnEngine::mapExtraFds ( int controlFd )
59 // we might have gotten other FDs to reuse, lets map them to STDERR_FILENO++
60 // BUT we need to make sure the fds are not already in the range we need to map them to
61 // so we first go over a list and collect those that are safe or move those that are not
62 int lastFdToKeep = STDERR_FILENO + _mapFds.size();
63 int nextBackupFd = lastFdToKeep + 1; //this we will use to count the fds upwards
64 std::vector<int> safeFds;
65 for ( auto fd : _mapFds ) {
66 // if the fds are bigger than the last one we will map to its safe
67 if ( fd > lastFdToKeep ) {
68 safeFds.push_back( fd );
70 // we need to map the fd after the set of those we want to keep, but also make sure
71 // that we do not close one of those we have already moved or might move
74 int backupTo = nextBackupFd;
76 const bool isSafe1 = std::find( _mapFds.begin(), _mapFds.end(), backupTo ) == _mapFds.end();
77 const bool isSafe2 = std::find( safeFds.begin(), safeFds.end(), backupTo ) == safeFds.end();
78 if ( isSafe1 && isSafe2 && ( controlFd == -1 || backupTo != controlFd) ) {
80 safeFds.push_back( backupTo );
87 // now we have a list of safe fds we need to map to the fd we want them to end up
88 int nextFd = STDERR_FILENO;
89 for ( auto fd : safeFds ) {
94 const auto &canCloseFd = [&]( int fd ){
95 // controlFD has O_CLOEXEC set so it will be cleaned up :)
96 if ( controlFd != -1 && controlFd == fd )
98 // make sure we don't close fd's still need
99 if ( fd <= lastFdToKeep )
104 const auto maxFds = ( ::getdtablesize() - 1 );
105 //If the rlimits are too high we need to use a different approach
106 // in detecting how many fds we need to close, or otherwise we are too slow (bsc#1191324)
107 if ( maxFds > 1024 && zypp::PathInfo( "/proc/self/fd" ).isExist() ) {
109 std::vector<int> fdsToClose;
110 fdsToClose.reserve (256);
112 zypp::filesystem::dirForEachExt( "/proc/self/fd", [&]( const zypp::Pathname &p, const zypp::filesystem::DirEntry &entry ){
113 if ( entry.type != zypp::filesystem::FT_LINK)
116 const auto &fdVal = zyppng::str::safe_strtonum<int>( entry.name );
117 if ( !fdVal || !canCloseFd(*fdVal) )
120 // we can not call close() directly here because zypp::filesystem::dirForEachExt actually has a fd open on
121 // /proc/self/fd that we would close as well. So we just remember which fd's we WOULD close and then do it
123 fdsToClose.push_back (*fdVal);
126 for ( int cFd : fdsToClose )
129 // close all filedescriptors above the last we want to keep
130 for ( int i = maxFds; i > lastFdToKeep; --i ) {
131 if ( !canCloseFd(i) ) continue;
137 bool zyppng::ForkSpawnEngine::start( const char * const *argv, int stdin_fd, int stdout_fd, int stderr_fd )
142 _executedCommand.clear();
145 const char * chdirTo = nullptr;
147 if ( !argv || !argv[0] ) {
148 _execError = _("Invalid spawn arguments given.");
153 if ( !_chroot.empty() )
155 const auto chroot = _chroot.c_str();
156 if ( chroot[0] == '\0' )
158 _chroot = zypp::Pathname(); // ignore empty _chroot
160 else if ( chroot[0] == '/' && chroot[1] == '\0' )
162 // If _chroot is '/' do not chroot, but chdir to '/'
163 // unless arglist defines another dir.
165 _chroot = zypp::Pathname();
169 if ( !_workingDirectory.empty() )
170 chdirTo = _workingDirectory.c_str();
172 // do not remove the single quotes around every argument, copy&paste of
173 // command to shell will not work otherwise!
176 std::stringstream cmdstr;
177 for (int i = 0; argv[i]; i++) {
178 if ( i != 0 ) cmdstr << ' ';
182 _args.push_back( argv[i] );
184 _executedCommand = cmdstr.str();
186 DBG << "Executing" << ( _useDefaultLocale?"[C] ":" ") << _executedCommand << std::endl;
188 // we use a control pipe to figure out if the exec actually worked,
189 // this is the approach:
190 // - create a pipe before forking
191 // - block on the read end of the pipe in the parent process
192 // - in the child process we write a error tag + errno into the pipe if we encounter any error and exit
193 // - If child setup works out, the pipe is auto closed by exec() and the parent process knows from just receiving EOF
194 // that starting the child was successful, otherwise the blocking read in the parent will return with actual data read from the fd
195 // which will contain the error description
197 enum class ChildErrType : int8_t {
206 ChildErrType type = ChildErrType::NO_ERR;
209 auto controlPipe = Pipe::create( O_CLOEXEC );
210 if ( !controlPipe ) {
211 _execError = _("Unable to create control pipe.");
216 pid_t ppid_before_fork = ::getpid();
218 // Create module process
219 if ( ( _pid = fork() ) == 0 )
223 controlPipe->unrefRead();
225 const auto &writeErrAndExit = [&]( int errCode, ChildErrType type ){
231 zypp::io::writeAll( controlPipe->writeFd, &buf, sizeof(ChildErr) );
235 //////////////////////////////////////////////////////////////////////
236 // Don't write to the logfile after fork!
237 //////////////////////////////////////////////////////////////////////
241 dup2 ( stdout_fd, 1); // set new stdout
242 dup2 ( stdin_fd , 0); // set new stdin
244 // We currently have no controlling terminal (due to setsid).
245 // The first open call will also set the new ctty (due to historical
246 // unix guru knowledge ;-) )
249 ttyname_r( stdout_fd , name, sizeof(name) );
250 ::close(open(name, O_RDONLY));
256 if ( stdin_fd != -1 )
257 dup2 ( stdin_fd, 0); // set new stdin
258 if ( stdout_fd != -1 )
259 dup2 ( stdout_fd, 1); // set new stdout
263 if ( stderr_fd != -1 )
264 dup2 ( stderr_fd, 2); // set new stderr
266 for ( Environment::const_iterator it = _environment.begin(); it != _environment.end(); ++it ) {
267 setenv( it->first.c_str(), it->second.c_str(), 1 );
270 if( _useDefaultLocale )
271 setenv("LC_ALL","C",1);
273 if( !_chroot.empty() )
275 if( ::chroot(_chroot.c_str()) == -1)
277 _execError = zypp::str::form( _("Can't chroot to '%s' (%s)."), _chroot.c_str(), strerror(errno).c_str() );
278 std::cerr << _execError << std::endl; // After fork log on stderr too
279 writeErrAndExit( 128, ChildErrType::CHROOT_FAILED ); // No sense in returning! I am forked away!!
285 if ( chdirTo && chdir( chdirTo ) == -1 )
287 _execError = _chroot.empty() ? zypp::str::form( _("Can't chdir to '%s' (%s)."), chdirTo, strerror(errno).c_str() )
288 : zypp::str::form( _("Can't chdir to '%s' inside chroot '%s' (%s)."), chdirTo, _chroot.c_str(), strerror(errno).c_str() );
290 std::cerr << _execError << std::endl;// After fork log on stderr too
291 writeErrAndExit( 128, ChildErrType::CHDIR_FAILED ); // No sense in returning! I am forked away!!
294 // map the extra fds the user might have set
295 mapExtraFds( controlPipe->writeFd );
297 if ( _dieWithParent ) {
298 // process dies with us
299 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
301 //ignore if it did not work, worst case the process lives on after the parent dies
302 std::cerr << "Failed to set PR_SET_PDEATHSIG" << std::endl;// After fork log on stderr too
305 // test in case the original parent exited just
306 // before the prctl() call
307 pid_t ppidNow = getppid();
308 if (ppidNow != ppid_before_fork) {
309 // no sense to write to control pipe, parent is gone
310 std::cerr << "PPID changed from "<<ppid_before_fork<<" to "<< ppidNow << std::endl;// After fork log on stderr too
315 execvp( argv[0], const_cast<char *const *>( argv ) );
316 // don't want to get here
317 _execError = zypp::str::form( _("Can't exec '%s' (%s)."), _args[0].c_str(), strerror(errno).c_str() );
318 std::cerr << _execError << std::endl;// After fork log on stderr too
319 writeErrAndExit( 129, ChildErrType::EXEC_FAILED ); // No sense in returning! I am forked away!!
320 //////////////////////////////////////////////////////////////////////
322 else if ( _pid == -1 ) // Fork failed, close everything.
324 _execError = zypp::str::form( _("Can't fork (%s)."), strerror(errno).c_str() );
326 ERR << _execError << std::endl;
331 // parent process, fork worked lets wait for the exec to happen
332 controlPipe->unrefWrite();
335 const auto res = zypp::io::readAll( controlPipe->readFd, &buf, sizeof(ChildErr) );
336 if ( res == zypp::io::ReadAllResult::Eof ) {
338 DBG << "pid " << _pid << " launched" << std::endl;
340 } else if ( res == zypp::io::ReadAllResult::Ok ) {
342 case ChildErrType::CHDIR_FAILED:
343 _execError = zypp::str::form( _("Can't exec '%s', chdir failed (%s)."), _args[0].c_str(), zypp::str::strerror(buf.childErrno).c_str() );
345 case ChildErrType::CHROOT_FAILED:
346 _execError = zypp::str::form( _("Can't exec '%s', chroot failed (%s)."), _args[0].c_str(), zypp::str::strerror(buf.childErrno).c_str() );
348 case ChildErrType::EXEC_FAILED:
349 _execError = zypp::str::form( _("Can't exec '%s', exec failed (%s)."), _args[0].c_str(), zypp::str::strerror(buf.childErrno).c_str() );
351 // all other cases need to be some sort of error, because we only get data if the exec fails
353 _execError = zypp::str::form( _("Can't exec '%s', unexpected error."), _args[0].c_str() );
357 // reap child and collect exit code
361 //reading from the fd failed, this should actually never happen
362 ERR << "Reading from the control pipe failed. " << errno << ". This is not supposed to happen ever." << std::endl;
369 bool zyppng::ForkSpawnEngine::usePty() const
374 void zyppng::ForkSpawnEngine::setUsePty( const bool set )
380 #if ZYPP_HAS_GLIBSPAWNENGINE
382 struct GLibForkData {
383 zyppng::GlibSpawnEngine *that = nullptr;
384 pid_t pidParent = -1;
387 bool zyppng::GlibSpawnEngine::start( const char * const *argv, int stdin_fd, int stdout_fd, int stderr_fd )
392 _executedCommand.clear();
395 if ( !argv || !argv[0] ) {
396 _execError = _("Invalid spawn arguments given.");
401 const char * chdirTo = nullptr;
403 if ( !_chroot.empty() ) {
404 const auto chroot = _chroot.c_str();
405 if ( chroot[0] == '\0' )
407 _chroot = zypp::Pathname(); // ignore empty _chroot
409 else if ( chroot[0] == '/' && chroot[1] == '\0' )
411 // If _chroot is '/' do not chroot, but chdir to '/'
412 // unless arglist defines another dir.
414 _chroot = zypp::Pathname();
418 if ( !_workingDirectory.empty() )
419 chdirTo = _workingDirectory.c_str();
421 // do not remove the single quotes around every argument, copy&paste of
422 // command to shell will not work otherwise!
425 std::stringstream cmdstr;
426 for (int i = 0; argv[i]; i++) {
427 if ( i != 0 ) cmdstr << ' ';
431 _args.push_back( argv[i] );
433 _executedCommand = cmdstr.str();
435 DBG << "Executing" << ( _useDefaultLocale?"[C] ":" ") << _executedCommand << std::endl;
437 // build the env var ptrs
438 std::vector<std::string> envStrs;
439 std::vector<gchar *> envPtrs;
441 for ( char **envPtr = environ; *envPtr != nullptr; envPtr++ )
442 envPtrs.push_back( *envPtr );
444 envStrs.reserve( _environment.size() );
445 envPtrs.reserve( envPtrs.size() + _environment.size() + ( _useDefaultLocale ? 2 : 1 ) );
446 for ( const auto &env : _environment ) {
447 envStrs.push_back( env.first + "=" + env.second );
448 envPtrs.push_back( envStrs.back().data() );
450 if ( _useDefaultLocale ) {
451 envStrs.push_back( "LC_ALL=C" );
452 envPtrs.push_back( envStrs.back().data() );
454 envPtrs.push_back( nullptr );
458 data.pidParent = ::getpid();
460 bool needCallback = !_chroot.empty() || _dieWithParent || _switchPgid || _mapFds.size();
462 auto spawnFlags = GSpawnFlags( G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH_FROM_ENVP );
463 if ( _mapFds.size() )
464 spawnFlags = GSpawnFlags( spawnFlags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN );
467 g_autoptr(GError) error = NULL;
468 g_spawn_async_with_fds(
473 needCallback ? &GlibSpawnEngine::glibSpawnCallback : nullptr,
474 needCallback ? &data : nullptr,
485 _execError = zypp::str::form( _("Can't fork (%s)."), strerror(errno).c_str() );
487 ERR << _execError << std::endl;
493 void zyppng::GlibSpawnEngine::glibSpawnCallback(void *data)
495 GLibForkData *d = reinterpret_cast<GLibForkData *>(data);
497 bool doChroot = !d->that->_chroot.empty();
499 std::string execError;
501 if ( d->that->_switchPgid )
505 if ( ::chroot( d->that->_chroot.c_str() ) == -1 ) {
506 execError = zypp::str::form( "Can't chroot to '%s' (%s).", d->that->_chroot.c_str(), strerror(errno).c_str() );
507 std::cerr << execError << std::endl;// After fork log on stderr too
508 _exit (128); // No sense in returning! I am forked away!!
511 std::string chdir; //if we are in chroot we need to chdir again
512 if ( d->that->_workingDirectory.empty() ) {
515 chdir = d->that->_workingDirectory.asString();
518 if ( !chdir.empty() && ::chdir( chdir.data() ) == -1 )
520 execError = doChroot ? zypp::str::form( "Can't chdir to '%s' inside chroot '%s' (%s).", chdir.data(), d->that->_chroot.c_str(), strerror(errno).c_str() )
521 : zypp::str::form( "Can't chdir to '%s' (%s).", chdir.data(), strerror(errno).c_str() );
522 std::cerr << execError << std::endl; // After fork log on stderr too
523 _exit (128); // No sense in returning! I am forked away!!
528 if ( d->that->_dieWithParent ) {
529 // process dies with us
530 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
532 //ignore if it did not work, worst case the process lives on after the parent dies
533 std::cerr << "Failed to set PR_SET_PDEATHSIG" << std::endl;// After fork log on stderr too
536 // test in case the original parent exited just
537 // before the prctl() call
538 pid_t ppidNow = getppid();
539 if (ppidNow != d->pidParent ) {
540 std::cerr << "PPID changed from "<<d->pidParent<<" to "<< ppidNow << std::endl;// After fork log on stderr too
545 // map the extra fds the user might have set
546 d->that->mapExtraFds();