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;
 
 
   69  if ( 
_pid < 0 ) 
return true;
 
   72  if ( !timeout.has_value () )
 
   76  const auto &fallbackPoll = [&]( uint64_t timeout ){
 
   82      std::this_thread::sleep_for( std::chrono::milliseconds(1) );
 
   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;
 
  106  uint64_t tRemaining = *timeout;
 
  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 );
 
  132  return fallbackPoll( *timeout );
 
 
  141  int lastFdToKeep = STDERR_FILENO + 
_mapFds.size();
 
  142  int nextBackupFd = lastFdToKeep + 1; 
 
  143  std::vector<int> safeFds;
 
  146    if ( fd > lastFdToKeep ) {
 
  147      safeFds.push_back( fd );
 
  153        int backupTo = nextBackupFd;
 
  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 );
 
  186  if ( maxFds > 1024 && 
zypp::PathInfo( 
"/proc/self/fd" ).isExist() ) {
 
  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 );
 
 
  242  if ( !argv || !argv[0] ) {
 
  248  const char * chdirTo = 
nullptr;
 
  264    std::stringstream cmdstr;
 
  265    for (
int i = 0; argv[i]; i++) {
 
  266      if ( i != 0 ) cmdstr << 
' ';
 
  270      _args.push_back( argv[i] );
 
  285  enum class ChildErrType : int8_t {
 
  294    ChildErrType type = ChildErrType::NO_ERR;
 
  298  if ( !controlPipe ) {
 
  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); 
 
  356      setenv( it->first.c_str(), it->second.c_str(), 1 );
 
  360      setenv(
"LC_ALL",
"C",1);
 
  368            writeErrAndExit( 128, ChildErrType::CHROOT_FAILED ); 
 
  374    if ( chdirTo && chdir( chdirTo ) == -1 )
 
  380      writeErrAndExit( 128, ChildErrType::CHDIR_FAILED ); 
 
  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 ) );
 
  408    writeErrAndExit( 129, ChildErrType::EXEC_FAILED ); 
 
  411  else if ( 
_pid == -1 )     
 
  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:
 
  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(
 
  552        const_cast<gchar**
>(argv),
 
  555        needCallback ? &GlibSpawnEngine::glibSpawnCallback : 
nullptr,
 
  556        needCallback ? &data : 
nullptr,
 
  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  if ( d->that->_switchPgid )
 
  586    std::string execError;
 
  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.
~AbstractDirectSpawnEngine() override
bool isRunning(bool wait=false) override
bool waitForExit(const std::optional< uint64_t > &timeout={}) override
void mapExtraFds(int controlFd=-1)
zypp::Pathname _chroot
Path to chroot into.
zypp::Pathname _workingDirectory
Working directory.
std::string _execError
Remember execution errors like failed fork/exec.
std::string _executedCommand
Store the command we're executing.
virtual bool start(const char *const *argv, int stdin_fd, int stdout_fd, int stderr_fd)=0
bool _dieWithParent
Should the process die with the parent process.
std::vector< int > _mapFds
Additional file descriptors we want to map to the new process.
Environment _environment
Environment variables to set in the new process.
int checkStatus(int status)
zypp::Pathname chroot() const
std::vector< std::string > _args
The arguments we want to pass to the program.
bool _use_pty
Set to true, if a pair of ttys is used for communication instead of a pair of pipes.
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)
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.
std::optional< T > safe_strtonum(const std::string_view &val)
std::string strerror(int errno_r) ZYPP_API
Return string describing the error_r code.
auto eintrSafeCall(Fun &&function, Args &&... args)
AutoDispose<int> calling close
Listentry returned by readdir.
static std::optional< Pipe > create(int flags=0)