Imported Upstream version 17.28.8
[platform/upstream/libzypp.git] / zypp-core / zyppng / io / forkspawnengine.cc
1 #include "private/forkspawnengine_p.h"
2
3 #include <sstream>
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>
13
14 #include <cstdint>
15 #include <iostream>
16 #include <signal.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <sys/wait.h>
20 #include <fcntl.h>
21 #include <pty.h> // openpty
22 #include <stdlib.h> // setenv
23 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
24
25 zyppng::AbstractDirectSpawnEngine::~AbstractDirectSpawnEngine()
26 {
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 );
31   }
32 }
33
34 bool zyppng::AbstractDirectSpawnEngine::isRunning( bool wait )
35 {
36   if ( _pid < 0 ) return false;
37
38   int status = 0;
39   int p = zyppng::eintrSafeCall( ::waitpid, _pid, &status, wait ? 0 : WNOHANG );
40   switch ( p )
41     {
42     case -1:
43       ERR << "waitpid( " << _pid << ") returned error '" << strerror(errno) << "'" << std::endl;
44       return false;
45       break;
46     case 0:
47       return true; // still running
48       break;
49     }
50
51   // Here: completed...
52   _exitStatus = checkStatus( status );
53   _pid = -1;
54   return false;
55 }
56
57 void zyppng::AbstractDirectSpawnEngine::mapExtraFds ( int controlFd )
58 {
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 );
69     } else {
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
72       while (true) {
73
74         int backupTo = nextBackupFd;
75         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) ) {
79           dup2( fd, backupTo );
80           safeFds.push_back( backupTo );
81           break;
82         }
83       }
84     }
85   }
86
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 ) {
90     nextFd++;
91     dup2( fd, nextFd );
92   }
93
94   const auto &canCloseFd = [&]( int fd ){
95     // controlFD has O_CLOEXEC set so it will be cleaned up :)
96     if ( controlFd != -1 && controlFd == fd )
97       return false;
98     // make sure we don't close fd's still need
99     if ( fd <= lastFdToKeep )
100       return false;
101     return true;
102   };
103
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() ) {
108     
109     std::vector<int> fdsToClose;
110     fdsToClose.reserve (256);
111
112     zypp::filesystem::dirForEachExt( "/proc/self/fd", [&]( const zypp::Pathname &p, const zypp::filesystem::DirEntry &entry ){
113       if ( entry.type != zypp::filesystem::FT_LINK)
114         return true;
115
116       const auto &fdVal = zyppng::str::safe_strtonum<int>( entry.name );
117       if ( !fdVal || !canCloseFd(*fdVal) )
118         return true;
119
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
122       // after iterating
123       fdsToClose.push_back (*fdVal);
124       return true;
125     });
126     for ( int cFd : fdsToClose )
127       ::close( cFd );
128   } else {
129     // close all filedescriptors above the last we want to keep
130     for ( int i = maxFds; i > lastFdToKeep; --i ) {
131       if ( !canCloseFd(i) ) continue;
132       ::close( i );
133     }
134   }
135 }
136
137 bool zyppng::ForkSpawnEngine::start( const char * const *argv, int stdin_fd, int stdout_fd, int stderr_fd )
138 {
139   _pid = -1;
140   _exitStatus = 0;
141   _execError.clear();
142   _executedCommand.clear();
143   _args.clear();
144
145   const char * chdirTo = nullptr;
146
147   if ( !argv || !argv[0] ) {
148     _execError = _("Invalid spawn arguments given.");
149     _exitStatus = 128;
150     return false;
151   }
152
153   if ( !_chroot.empty() )
154   {
155     const auto chroot = _chroot.c_str();
156     if ( chroot[0] == '\0' )
157     {
158       _chroot = zypp::Pathname();       // ignore empty _chroot
159     }
160     else if ( chroot[0] == '/' && chroot[1] == '\0' )
161     {
162       // If _chroot is '/' do not chroot, but chdir to '/'
163       // unless arglist defines another dir.
164       chdirTo = "/";
165       _chroot = zypp::Pathname();
166     }
167   }
168
169   if ( !_workingDirectory.empty() )
170     chdirTo = _workingDirectory.c_str();
171
172   // do not remove the single quotes around every argument, copy&paste of
173   // command to shell will not work otherwise!
174   {
175     _args.clear();
176     std::stringstream cmdstr;
177     for (int i = 0; argv[i]; i++) {
178       if ( i != 0 ) cmdstr << ' ';
179       cmdstr << '\'';
180       cmdstr << argv[i];
181       cmdstr << '\'';
182       _args.push_back( argv[i] );
183     }
184     _executedCommand = cmdstr.str();
185   }
186   DBG << "Executing" << ( _useDefaultLocale?"[C] ":" ") << _executedCommand << std::endl;
187
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
196
197   enum class ChildErrType : int8_t {
198     NO_ERR,
199     CHROOT_FAILED,
200     CHDIR_FAILED,
201     EXEC_FAILED
202   };
203
204   struct ChildErr {
205     int childErrno = 0;
206     ChildErrType type = ChildErrType::NO_ERR;
207   };
208
209   auto controlPipe = Pipe::create( O_CLOEXEC );
210   if ( !controlPipe ) {
211     _execError = _("Unable to create control pipe.");
212     _exitStatus = 128;
213     return false;
214   }
215
216   pid_t ppid_before_fork = ::getpid();
217
218   // Create module process
219   if ( ( _pid = fork() ) == 0 )
220   {
221
222     // child process
223     controlPipe->unrefRead();
224
225     const auto &writeErrAndExit = [&]( int errCode, ChildErrType type ){
226       ChildErr buf {
227         errno,
228         type
229       };
230
231       zypp::io::writeAll( controlPipe->writeFd, &buf, sizeof(ChildErr) );
232       _exit ( errCode );
233     };
234
235     //////////////////////////////////////////////////////////////////////
236     // Don't write to the logfile after fork!
237     //////////////////////////////////////////////////////////////////////
238     if ( _use_pty )
239     {
240         setsid();
241         dup2 ( stdout_fd, 1);     // set new stdout
242         dup2 ( stdin_fd , 0);     // set new stdin
243
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 ;-) )
247
248         char name[512];
249         ttyname_r( stdout_fd , name, sizeof(name) );
250         ::close(open(name, O_RDONLY));
251     }
252     else
253     {
254         if ( _switchPgid )
255           setpgid( 0, 0);
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
260     }
261
262     // Handle stderr
263     if ( stderr_fd != -1 )
264       dup2 ( stderr_fd, 2); // set new stderr
265
266     for ( Environment::const_iterator it = _environment.begin(); it != _environment.end(); ++it ) {
267       setenv( it->first.c_str(), it->second.c_str(), 1 );
268     }
269
270     if( _useDefaultLocale )
271       setenv("LC_ALL","C",1);
272
273     if( !_chroot.empty() )
274     {
275         if( ::chroot(_chroot.c_str()) == -1)
276         {
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!!
280         }
281         if ( ! chdirTo )
282           chdirTo = "/";
283     }
284
285     if ( chdirTo && chdir( chdirTo ) == -1 )
286     {
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() );
289
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!!
292     }
293
294     // map the extra fds the user might have set
295     mapExtraFds( controlPipe->writeFd );
296
297     if ( _dieWithParent ) {
298       // process dies with us
299       int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
300       if (r == -1) {
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
303       }
304
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
311         _exit(128);
312       }
313     }
314
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     //////////////////////////////////////////////////////////////////////
321   }
322   else if ( _pid == -1 )         // Fork failed, close everything.
323   {
324     _execError = zypp::str::form( _("Can't fork (%s)."), strerror(errno).c_str() );
325     _exitStatus = 127;
326     ERR << _execError << std::endl;
327     return false;
328   }
329   else {
330
331     // parent process, fork worked lets wait for the exec to happen
332     controlPipe->unrefWrite();
333
334     ChildErr buf;
335     const auto res = zypp::io::readAll( controlPipe->readFd, &buf, sizeof(ChildErr) );
336     if ( res == zypp::io::ReadAllResult::Eof ) {
337       // success!!!!
338       DBG << "pid " << _pid << " launched" << std::endl;
339       return true;
340     } else if ( res == zypp::io::ReadAllResult::Ok ) {
341       switch( buf.type ) {
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() );
344           break;
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() );
347           break;
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() );
350           break;
351         // all other cases need to be some sort of error, because we only get data if the exec fails
352         default:
353           _execError = zypp::str::form( _("Can't exec '%s', unexpected error."), _args[0].c_str() );
354           break;
355       }
356
357       // reap child and collect exit code
358       isRunning( true );
359       return false;
360     } else {
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;
363       return isRunning();
364     }
365   }
366   return true;
367 }
368
369 bool zyppng::ForkSpawnEngine::usePty() const
370 {
371   return _use_pty;
372 }
373
374 void zyppng::ForkSpawnEngine::setUsePty( const bool set )
375 {
376   _use_pty = set;
377 }
378
379
380 #if ZYPP_HAS_GLIBSPAWNENGINE
381
382 struct GLibForkData {
383   zyppng::GlibSpawnEngine *that = nullptr;
384   pid_t pidParent = -1;
385 };
386
387 bool zyppng::GlibSpawnEngine::start( const char * const *argv, int stdin_fd, int stdout_fd, int stderr_fd )
388 {
389   _pid = -1;
390   _exitStatus = 0;
391   _execError.clear();
392   _executedCommand.clear();
393   _args.clear();
394
395   if ( !argv || !argv[0] ) {
396     _execError = _("Invalid spawn arguments given.");
397     _exitStatus = 128;
398     return false;
399   }
400
401   const char * chdirTo = nullptr;
402
403   if ( !_chroot.empty() ) {
404     const auto chroot = _chroot.c_str();
405     if ( chroot[0] == '\0' )
406     {
407       _chroot = zypp::Pathname();       // ignore empty _chroot
408     }
409     else if ( chroot[0] == '/' && chroot[1] == '\0' )
410     {
411       // If _chroot is '/' do not chroot, but chdir to '/'
412       // unless arglist defines another dir.
413       chdirTo = "/";
414       _chroot = zypp::Pathname();
415     }
416   }
417
418   if ( !_workingDirectory.empty() )
419     chdirTo = _workingDirectory.c_str();
420
421   // do not remove the single quotes around every argument, copy&paste of
422   // command to shell will not work otherwise!
423   {
424     _args.clear();
425     std::stringstream cmdstr;
426     for (int i = 0; argv[i]; i++) {
427       if ( i != 0 ) cmdstr << ' ';
428       cmdstr << '\'';
429       cmdstr << argv[i];
430       cmdstr << '\'';
431       _args.push_back( argv[i] );
432     }
433     _executedCommand = cmdstr.str();
434   }
435   DBG << "Executing" << ( _useDefaultLocale?"[C] ":" ") << _executedCommand << std::endl;
436
437   // build the env var ptrs
438   std::vector<std::string> envStrs;
439   std::vector<gchar *> envPtrs;
440
441   for ( char **envPtr = environ; *envPtr != nullptr; envPtr++ )
442     envPtrs.push_back( *envPtr );
443
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() );
449   }
450   if ( _useDefaultLocale ) {
451     envStrs.push_back( "LC_ALL=C" );
452     envPtrs.push_back( envStrs.back().data() );
453   }
454   envPtrs.push_back( nullptr );
455
456   GLibForkData data;
457   data.that = this;
458   data.pidParent = ::getpid();
459
460   bool needCallback = !_chroot.empty() || _dieWithParent || _switchPgid || _mapFds.size();
461
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 );
465
466   GPid childPid = -1;
467   g_autoptr(GError) error = NULL;
468   g_spawn_async_with_fds(
469         chdirTo,
470         (gchar **)argv,
471         envPtrs.data(),
472         spawnFlags,
473         needCallback ? &GlibSpawnEngine::glibSpawnCallback : nullptr,
474         needCallback ? &data : nullptr,
475         &childPid,
476         stdin_fd,  //in
477         stdout_fd,  //out
478         stderr_fd,  //err
479         &error
480   );
481
482   if ( !error ) {
483     _pid = childPid;
484   } else {
485     _execError = zypp::str::form( _("Can't fork (%s)."), strerror(errno).c_str() );
486     _exitStatus = 127;
487     ERR << _execError << std::endl;
488     return false;
489   }
490   return true;
491 }
492
493 void zyppng::GlibSpawnEngine::glibSpawnCallback(void *data)
494 {
495   GLibForkData *d = reinterpret_cast<GLibForkData *>(data);
496
497   bool doChroot = !d->that->_chroot.empty();
498
499   std::string execError;
500
501   if ( d->that->_switchPgid )
502     setpgid( 0, 0);
503
504   if ( doChroot ) {
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!!
509     }
510
511     std::string chdir; //if we are in chroot we need to chdir again
512     if ( d->that->_workingDirectory.empty() ) {
513       chdir = "/";
514     } else {
515       chdir = d->that->_workingDirectory.asString();
516     }
517
518     if ( !chdir.empty() && ::chdir( chdir.data() ) == -1 )
519     {
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!!
524     }
525
526   }
527
528   if ( d->that->_dieWithParent ) {
529     // process dies with us
530     int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
531     if (r == -1) {
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
534     }
535
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
541       _exit(128);
542     }
543   }
544
545   // map the extra fds the user might have set
546   d->that->mapExtraFds();
547 }
548 #endif