8#include <zypp-core/zyppng/core/String>
9#include <zypp-core/zyppng/base/EventDispatcher>
10#include <zypp-core/zyppng/base/Timer>
26#include <sys/syscall.h>
31#undef ZYPP_BASE_LOGGER_LOGGROUP
32#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::exec"
46 if ( _pid < 0 )
return false;
53 ERR <<
"waitpid( " << _pid <<
") returned error '" << strerror(errno) <<
"'" << std::endl;
62 _exitStatus = checkStatus( status );
69 if ( _pid < 0 )
return true;
72 if ( !timeout.has_value () )
73 return !isRunning(
true );
76 const auto &fallbackPoll = [&](
uint64_t timeout ){
79 if ( !isRunning(
false) )
82 std::this_thread::sleep_for( std::chrono::milliseconds(1) );
85 return !isRunning (
false );
90 const auto &zypp_pidfd_open = [](pid_t pid,
unsigned int flags) ->
int {
91 return syscall( SYS_pidfd_open, pid, flags );
97 ERR <<
"pidfd_open failed, falling back to polling waidpid" << std::endl;
98 return fallbackPoll( *timeout );
101 struct pollfd pollfd;
103 pollfd.events = POLLIN;
111 int posixTimeout = tRemaining > INT_MAX ? INT_MAX :
static_cast<int>(tRemaining);
113 int ready = poll(&pollfd, 1, posixTimeout );
116 if ( ready == -1 && errno != EINTR ) {
117 ERR <<
"Polling the pidfd failed with error: " <<
zypp::Errno() << std::endl;
118 if ( tRemaining > 0 ) {
119 ERR <<
"Falling back to manual polling for the remaining timeout." << std::endl;
120 return fallbackPoll( tRemaining );
123 }
else if ( pollfd.revents & POLLIN ) {
126 }
while( tRemaining > 0 );
129 return !isRunning (
false );
132 return fallbackPoll( *timeout );
141 int lastFdToKeep = STDERR_FILENO + _mapFds.size();
142 int nextBackupFd = lastFdToKeep + 1;
143 std::vector<int> safeFds;
144 for (
auto fd : _mapFds ) {
146 if ( fd > lastFdToKeep ) {
147 safeFds.push_back( fd );
153 int backupTo = nextBackupFd;
155 const bool isSafe1 = std::find( _mapFds.begin(), _mapFds.end(), backupTo ) == _mapFds.end();
156 const bool isSafe2 = std::find( safeFds.begin(), safeFds.end(), backupTo ) == safeFds.end();
157 if ( isSafe1 && isSafe2 && ( controlFd == -1 || backupTo != controlFd) ) {
158 dup2( fd, backupTo );
159 safeFds.push_back( backupTo );
167 int nextFd = STDERR_FILENO;
168 for (
auto fd : safeFds ) {
173 const auto &canCloseFd = [&](
int fd ){
175 if ( controlFd != -1 && controlFd == fd )
178 if ( fd <= lastFdToKeep )
183 const auto maxFds = ( ::getdtablesize() - 1 );
188 std::vector<int> fdsToClose;
189 fdsToClose.reserve (256);
196 if ( !fdVal || !canCloseFd(*fdVal) )
202 fdsToClose.push_back (*fdVal);
205 for (
int cFd : fdsToClose )
209 for (
int i = maxFds; i > lastFdToKeep; --i ) {
210 if ( !canCloseFd(i) )
continue;
219 struct sigaction act;
220 memset (&act, 0,
sizeof (
struct sigaction));
221 act.sa_handler = SIG_DFL;
222 for (
int i = 1; i < NSIG; i++ ) {
225 sigaction(i, &act, NULL);
230 sigemptyset ( &sigMask );
231 pthread_sigmask ( SIG_SETMASK, &sigMask,
nullptr );
239 _executedCommand.clear();
242 if ( !argv || !argv[0] ) {
243 _execError =
_(
"Invalid spawn arguments given.");
248 const char * chdirTo =
nullptr;
250 if ( _chroot ==
"/" ) {
257 if ( !_workingDirectory.empty() )
258 chdirTo = _workingDirectory.c_str();
264 std::stringstream cmdstr;
265 for (
int i = 0; argv[i]; i++) {
266 if ( i != 0 ) cmdstr <<
' ';
270 _args.push_back( argv[i] );
272 _executedCommand = cmdstr.str();
274 DBG <<
"Executing" << ( _useDefaultLocale?
"[C] ":
" ") << _executedCommand << std::endl;
285 enum class ChildErrType : int8_t {
294 ChildErrType type = ChildErrType::NO_ERR;
298 if ( !controlPipe ) {
299 _execError =
_(
"Unable to create control pipe.");
304 pid_t ppid_before_fork = ::getpid();
307 if ( ( _pid = fork() ) == 0 )
312 controlPipe->unrefRead();
314 const auto &writeErrAndExit = [&](
int errCode, ChildErrType type ){
330 dup2 ( stdout_fd, 1);
331 dup2 ( stdin_fd , 0);
338 ttyname_r( stdout_fd , name,
sizeof(name) );
339 ::close(open(name, O_RDONLY));
345 if ( stdin_fd != -1 )
347 if ( stdout_fd != -1 )
348 dup2 ( stdout_fd, 1);
352 if ( stderr_fd != -1 )
353 dup2 ( stderr_fd, 2);
355 for ( Environment::const_iterator it = _environment.begin(); it != _environment.end(); ++it ) {
356 setenv( it->first.c_str(), it->second.c_str(), 1 );
359 if( _useDefaultLocale )
360 setenv(
"LC_ALL",
"C",1);
362 if( !_chroot.empty() )
364 if( ::chroot(_chroot.c_str()) == -1)
366 _execError =
zypp::str::form(
_(
"Can't chroot to '%s' (%s)."), _chroot.c_str(), strerror(errno).c_str() );
367 std::cerr << _execError << std::endl;
368 writeErrAndExit( 128, ChildErrType::CHROOT_FAILED );
374 if ( chdirTo && chdir( chdirTo ) == -1 )
376 _execError = _chroot.empty() ?
zypp::str::form(
_(
"Can't chdir to '%s' (%s)."), chdirTo, strerror(errno).c_str() )
377 :
zypp::str::form(
_(
"Can't chdir to '%s' inside chroot '%s' (%s)."), chdirTo, _chroot.c_str(), strerror(errno).c_str() );
379 std::cerr << _execError << std::endl;
380 writeErrAndExit( 128, ChildErrType::CHDIR_FAILED );
384 mapExtraFds( controlPipe->writeFd );
386 if ( _dieWithParent ) {
388 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
391 std::cerr <<
"Failed to set PR_SET_PDEATHSIG" << std::endl;
396 pid_t ppidNow = getppid();
397 if (ppidNow != ppid_before_fork) {
399 std::cerr <<
"PPID changed from "<<ppid_before_fork<<
" to "<< ppidNow << std::endl;
404 execvp( argv[0],
const_cast<char *
const *
>( argv ) );
406 _execError =
zypp::str::form(
_(
"Can't exec '%s' (%s)."), _args[0].c_str(), strerror(errno).c_str() );
407 std::cerr << _execError << std::endl;
408 writeErrAndExit( 129, ChildErrType::EXEC_FAILED );
411 else if ( _pid == -1 )
413 _execError =
zypp::str::form(
_(
"Can't fork (%s)."), strerror(errno).c_str() );
415 ERR << _execError << std::endl;
421 controlPipe->unrefWrite();
424 const auto res =
zypp::io::readAll( controlPipe->readFd, &buf,
sizeof(ChildErr) );
427 DBG <<
"pid " << _pid <<
" launched" << std::endl;
431 case ChildErrType::CHDIR_FAILED:
434 case ChildErrType::CHROOT_FAILED:
437 case ChildErrType::EXEC_FAILED:
442 _execError =
zypp::str::form(
_(
"Can't exec '%s', unexpected error."), _args[0].c_str() );
445 ERR <<
"pid " << _pid <<
" launch failed: " << _execError << std::endl;
452 ERR <<
"Reading from the control pipe failed. " << errno <<
". This is not supposed to happen ever." << std::endl;
470#if ZYPP_HAS_GLIBSPAWNENGINE
473 zyppng::GlibSpawnEngine *that =
nullptr;
474 pid_t pidParent = -1;
477bool zyppng::GlibSpawnEngine::start(
const char *
const *argv,
int stdin_fd,
int stdout_fd,
int stderr_fd )
482 _executedCommand.clear();
485 if ( !argv || !argv[0] ) {
486 _execError =
_(
"Invalid spawn arguments given.");
491 const char * chdirTo =
nullptr;
493 if ( _chroot ==
"/" ) {
500 if ( !_workingDirectory.empty() )
501 chdirTo = _workingDirectory.c_str();
507 std::stringstream cmdstr;
508 for (
int i = 0; argv[i]; i++) {
509 if ( i != 0 ) cmdstr <<
' ';
513 _args.push_back( argv[i] );
515 _executedCommand = cmdstr.str();
517 DBG <<
"Executing" << ( _useDefaultLocale?
"[C] ":
" ") << _executedCommand << std::endl;
520 std::vector<std::string> envStrs;
521 std::vector<gchar *> envPtrs;
523 for (
char **envPtr = environ; *envPtr !=
nullptr; envPtr++ )
524 envPtrs.push_back( *envPtr );
526 envStrs.reserve( _environment.size() );
527 envPtrs.reserve( envPtrs.size() + _environment.size() + ( _useDefaultLocale ? 2 : 1 ) );
528 for (
const auto &
env : _environment ) {
529 envStrs.push_back(
env.first +
"=" +
env.second );
530 envPtrs.push_back( envStrs.back().data() );
532 if ( _useDefaultLocale ) {
533 envStrs.push_back(
"LC_ALL=C" );
534 envPtrs.push_back( envStrs.back().data() );
536 envPtrs.push_back(
nullptr );
540 data.pidParent = ::getpid();
542 bool needCallback = !_chroot.empty() || _dieWithParent || _switchPgid || _mapFds.size();
544 auto spawnFlags = GSpawnFlags( G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH_FROM_ENVP );
545 if ( _mapFds.size() )
546 spawnFlags = GSpawnFlags( spawnFlags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN );
549 g_autoptr(GError)
error = NULL;
550 g_spawn_async_with_fds(
555 needCallback ? &GlibSpawnEngine::glibSpawnCallback :
nullptr,
556 needCallback ? &data :
nullptr,
567 _execError =
zypp::str::form(
_(
"Can't fork (%s)."), strerror(errno).c_str() );
569 ERR << _execError << std::endl;
575void zyppng::GlibSpawnEngine::glibSpawnCallback(
void *data)
577 GLibForkData *d =
reinterpret_cast<GLibForkData *
>(data);
579 d->that->resetSignals();
580 bool doChroot = !d->that->_chroot.empty();
582 std::string execError;
584 if ( d->that->_switchPgid )
588 if ( ::chroot( d->that->_chroot.c_str() ) == -1 ) {
589 execError =
zypp::str::form(
"Can't chroot to '%s' (%s).", d->that->_chroot.c_str(), strerror(errno).c_str() );
590 std::cerr << execError << std::endl;
595 if ( d->that->_workingDirectory.empty() ) {
598 chdir = d->that->_workingDirectory.asString();
601 if ( !chdir.empty() && ::chdir( chdir.data() ) == -1 )
603 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() )
604 :
zypp::
str::form(
"Can't chdir to '%s' (%s).", chdir.data(), strerror(errno).c_str() );
605 std::cerr << execError << std::endl;
611 if ( d->that->_dieWithParent ) {
613 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
616 std::cerr <<
"Failed to set PR_SET_PDEATHSIG" << std::endl;
621 pid_t ppidNow = getppid();
622 if (ppidNow != d->pidParent ) {
623 std::cerr <<
"PPID changed from "<<d->pidParent<<
" to "<< ppidNow << std::endl;
629 d->that->mapExtraFds();
static void watchPID(pid_t pid_r)
Convenience errno wrapper.
Wrapper class for stat/lstat.
bool isExist() const
Return whether valid stat info exists.
~AbstractDirectSpawnEngine() override
bool isRunning(bool wait=false) override
bool waitForExit(const std::optional< uint64_t > &timeout={}) override
void mapExtraFds(int controlFd=-1)
bool start(const char *const *argv, int stdin_fd, int stdout_fd, int stderr_fd) override
void setUsePty(const bool set=true)
static uint64_t elapsedSince(const uint64_t start)
String related utilities and Regular expression matching.
Namespace intended to collect all environment variables we use.
int dirForEachExt(const Pathname &dir_r, const function< bool(const Pathname &, const DirEntry &)> &fnc_r)
Simiar to.
bool writeAll(int fd, void *buf, size_t size)
ReadAllResult readAll(int fd, void *buf, size_t size)
std::string strerror(int errno_r)
Return string describing the error_r code.
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Easy-to use interface to the ZYPP dependency resolver.
std::optional< T > safe_strtonum(const std::string_view &val)
auto eintrSafeCall(Fun &&function, Args &&... args)
AutoDispose<int> calling close
Listentry returned by readdir.
static std::optional< Pipe > create(int flags=0)