Imported Upstream version 17.25.3
[platform/upstream/libzypp.git] / zypp / ExternalProgram.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/ExternalProgram.cc
10 */
11
12 #define _GNU_SOURCE 1 // for ::getline
13
14 #include <signal.h>
15 #include <errno.h>
16 #include <unistd.h>
17 #include <sys/wait.h>
18 #include <fcntl.h>
19 #include <pty.h> // openpty
20 #include <stdlib.h> // setenv
21 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
22
23 #include <cstring> // strsignal
24 #include <iostream>
25 #include <sstream>
26
27 #include <zypp/base/Logger.h>
28 #include <zypp/base/String.h>
29 #include <zypp/base/Gettext.h>
30 #include <zypp/ExternalProgram.h>
31 #include <zypp/base/CleanerThread_p.h>
32
33 using std::endl;
34
35 #undef  ZYPP_BASE_LOGGER_LOGGROUP
36 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::exec"
37
38 namespace zypp {
39
40     ExternalProgram::ExternalProgram()
41       : use_pty (false)
42       , pid( -1 )
43     {}
44
45
46     ExternalProgram::ExternalProgram( std::string commandline,
47                                       Stderr_Disposition stderr_disp,
48                                       bool use_pty,
49                                       int stderr_fd,
50                                       bool default_locale,
51                                       const Pathname & root )
52       : use_pty (use_pty)
53       , pid( -1 )
54     {
55       const char *argv[4];
56       argv[0] = "/bin/sh";
57       argv[1] = "-c";
58       argv[2] = commandline.c_str();
59       argv[3] = 0;
60
61       start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
62     }
63
64
65     ExternalProgram::ExternalProgram( const Arguments & argv,
66                                       Stderr_Disposition stderr_disp,
67                                       bool use_pty,
68                                       int stderr_fd,
69                                       bool default_locale,
70                                       const Pathname & root )
71       : use_pty (use_pty)
72       , pid( -1 )
73     {
74       const char * argvp[argv.size() + 1];
75       unsigned c = 0;
76       for_( i, argv.begin(), argv.end() )
77       {
78         argvp[c] = i->c_str();
79         ++c;
80       }
81       argvp[c] = 0;
82
83       start_program( argvp, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
84     }
85
86
87     ExternalProgram::ExternalProgram( const Arguments & argv,
88                                       const Environment & environment,
89                                       Stderr_Disposition stderr_disp,
90                                       bool use_pty,
91                                       int stderr_fd,
92                                       bool default_locale,
93                                       const Pathname & root )
94       : use_pty (use_pty)
95       , pid( -1 )
96     {
97       const char * argvp[argv.size() + 1];
98       unsigned c = 0;
99       for_( i, argv.begin(), argv.end() )
100       {
101         argvp[c] = i->c_str();
102         ++c;
103       }
104       argvp[c] = 0;
105
106       start_program( argvp, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
107     }
108
109
110
111     ExternalProgram::ExternalProgram( const char *const *argv,
112                                       Stderr_Disposition stderr_disp,
113                                       bool use_pty,
114                                       int stderr_fd,
115                                       bool default_locale,
116                                       const Pathname & root )
117       : use_pty (use_pty)
118       , pid( -1 )
119     {
120       start_program( argv, Environment(), stderr_disp, stderr_fd, default_locale, root.c_str() );
121     }
122
123
124     ExternalProgram::ExternalProgram( const char *const * argv,
125                                       const Environment & environment,
126                                       Stderr_Disposition stderr_disp,
127                                       bool use_pty,
128                                       int stderr_fd,
129                                       bool default_locale,
130                                       const Pathname & root )
131       : use_pty (use_pty)
132       , pid( -1 )
133     {
134       start_program( argv, environment, stderr_disp, stderr_fd, default_locale, root.c_str() );
135     }
136
137
138     ExternalProgram::ExternalProgram( const char *binpath,
139                                       const char *const *argv_1,
140                                       bool use_pty )
141       : use_pty (use_pty)
142       , pid( -1 )
143     {
144       int i = 0;
145       while (argv_1[i++])
146         ;
147       const char *argv[i + 1];
148       argv[0] = binpath;
149       memcpy( &argv[1], argv_1, (i - 1) * sizeof (char *) );
150       start_program( argv, Environment() );
151     }
152
153
154     ExternalProgram::ExternalProgram( const char *binpath,
155                                       const char *const *argv_1,
156                                       const Environment & environment,
157                                       bool use_pty )
158       : use_pty (use_pty)
159       , pid( -1 )
160     {
161       int i = 0;
162       while (argv_1[i++])
163         ;
164       const char *argv[i + 1];
165       argv[0] = binpath;
166       memcpy( &argv[1], argv_1, (i - 1) * sizeof (char *) );
167       start_program( argv, environment );
168     }
169
170
171     ExternalProgram::~ExternalProgram()
172     {
173       if ( running() ) {
174         // we got destructed while the external process is still alive
175         // make sure the zombie is cleaned up once it exits
176         CleanerThread::watchPID( pid );
177       }
178     }
179
180
181
182     void ExternalProgram::start_program(const char *const *argv,
183                                          const Environment & environment,
184                                          Stderr_Disposition stderr_disp,
185                                          int stderr_fd,
186                                          bool default_locale,
187                                          const char * root , bool switch_pgid, bool die_with_parent )
188     {
189       pid = -1;
190       _exitStatus = 0;
191       int to_external[2], from_external[2];     // fds for pair of pipes
192       int master_tty,   slave_tty;              // fds for pair of ttys
193
194       // retrieve options at beginning of arglist
195       const char * redirectStdin = nullptr;     // <[file]
196       const char * redirectStdout = nullptr;    // >[file]
197       const char * chdirTo = nullptr;           // #/[path]
198
199       if ( root )
200       {
201         if ( root[0] == '\0' )
202         {
203           root = nullptr;       // ignore empty root
204         }
205         else if ( root[0] == '/' && root[1] == '\0' )
206         {
207           // If root is '/' do not chroot, but chdir to '/'
208           // unless arglist defines another dir.
209           chdirTo = "/";
210           root = nullptr;
211         }
212       }
213
214       for ( bool strip = false; argv[0]; ++argv )
215       {
216         strip = false;
217         switch ( argv[0][0] )
218         {
219           case '<':
220             strip = true;
221             redirectStdin = argv[0]+1;
222             if ( *redirectStdin == '\0' )
223               redirectStdin = "/dev/null";
224             break;
225
226           case '>':
227             strip = true;
228             redirectStdout = argv[0]+1;
229             if ( *redirectStdout == '\0' )
230               redirectStdout = "/dev/null";
231             break;
232
233           case '#':
234             strip = true;
235             if ( argv[0][1] == '/' )    // #/[path]
236               chdirTo = argv[0]+1;
237             break;
238         }
239         if ( ! strip )
240           break;
241       }
242
243       // do not remove the single quotes around every argument, copy&paste of
244       // command to shell will not work otherwise!
245       {
246         std::stringstream cmdstr;
247         for (int i = 0; argv[i]; i++)
248         {
249           if (i>0) cmdstr << ' ';
250           cmdstr << '\'';
251           cmdstr << argv[i];
252           cmdstr << '\'';
253         }
254         if ( redirectStdin )
255           cmdstr << " < '" << redirectStdin << "'";
256         if ( redirectStdout )
257           cmdstr << " > '" << redirectStdout << "'";
258         _command = cmdstr.str();
259       }
260       DBG << "Executing" << (default_locale?"[C] ":" ") << _command << endl;
261
262
263       if (use_pty)
264       {
265         // Create pair of ttys
266         DBG << "Using ttys for communication with " << argv[0] << endl;
267         if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
268         {
269           _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
270           _exitStatus = 126;
271           ERR << _execError << endl;
272           return;
273         }
274       }
275       else
276       {
277         // Create pair of pipes
278         if (pipe (to_external) != 0 || pipe (from_external) != 0)
279         {
280           _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
281           _exitStatus = 126;
282           ERR << _execError << endl;
283           return;
284         }
285       }
286
287       pid_t ppid_before_fork = ::getpid();
288
289       // Create module process
290       if ((pid = fork()) == 0)
291       {
292         //////////////////////////////////////////////////////////////////////
293         // Don't write to the logfile after fork!
294         //////////////////////////////////////////////////////////////////////
295         if (use_pty)
296         {
297             setsid();
298             if(slave_tty != 1)
299                 dup2 (slave_tty, 1);      // set new stdout
300             renumber_fd (slave_tty, 0);   // set new stdin
301             ::close(master_tty);          // Belongs to father process
302
303             // We currently have no controlling terminal (due to setsid).
304             // The first open call will also set the new ctty (due to historical
305             // unix guru knowledge ;-) )
306
307             char name[512];
308             ttyname_r(slave_tty, name, sizeof(name));
309             ::close(open(name, O_RDONLY));
310         }
311         else
312         {
313             if ( switch_pgid )
314               setpgid( 0, 0);
315             renumber_fd (to_external[0], 0); // set new stdin
316             ::close(from_external[0]);    // Belongs to father process
317
318             renumber_fd (from_external[1], 1); // set new stdout
319             ::close(to_external  [1]);    // Belongs to father process
320         }
321
322         if ( redirectStdin )
323         {
324           ::close( 0 );
325           int inp_fd = open( redirectStdin, O_RDONLY );
326           dup2( inp_fd, 0 );
327         }
328
329         if ( redirectStdout )
330         {
331           ::close( 1 );
332           int inp_fd = open( redirectStdout, O_WRONLY|O_CREAT|O_APPEND, 0600 );
333           dup2( inp_fd, 1 );
334         }
335
336         // Handle stderr
337         if (stderr_disp == Discard_Stderr)
338         {
339             int null_fd = open("/dev/null", O_WRONLY);
340             dup2(null_fd, 2);
341             ::close(null_fd);
342         }
343         else if (stderr_disp == Stderr_To_Stdout)
344         {
345             dup2(1, 2);
346         }
347         else if (stderr_disp == Stderr_To_FileDesc)
348         {
349             // Note: We don't have to close anything regarding stderr_fd.
350             // Our caller is responsible for that.
351             dup2 (stderr_fd, 2);
352         }
353
354         for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
355           setenv( it->first.c_str(), it->second.c_str(), 1 );
356         }
357
358         if(default_locale)
359                 setenv("LC_ALL","C",1);
360
361         if(root)
362         {
363             if(chroot(root) == -1)
364             {
365                 _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
366                 std::cerr << _execError << endl;// After fork log on stderr too
367                 _exit (128);                    // No sense in returning! I am forked away!!
368             }
369             if ( ! chdirTo )
370               chdirTo = "/";
371         }
372
373         if ( chdirTo && chdir( chdirTo ) == -1 )
374         {
375           _execError = root ? str::form( _("Can't chdir to '%s' inside chroot '%s' (%s)."), chdirTo, root, strerror(errno) )
376                             : str::form( _("Can't chdir to '%s' (%s)."), chdirTo, strerror(errno) );
377           std::cerr << _execError << endl;// After fork log on stderr too
378           _exit (128);                  // No sense in returning! I am forked away!!
379         }
380
381         // close all filedesctiptors above stderr
382         for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
383           ::close( i );
384         }
385
386         if ( die_with_parent ) {
387           // process dies with us
388           int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
389           if (r == -1) {
390             //ignore if it did not work, worst case the process lives on after the parent dies
391             std::cerr << "Failed to set PR_SET_PDEATHSIG" << endl;// After fork log on stderr too
392           }
393
394           // test in case the original parent exited just
395           // before the prctl() call
396           pid_t ppidNow = getppid();
397           if (ppidNow != ppid_before_fork) {
398             std::cerr << "PPID changed from "<<ppid_before_fork<<" to "<< ppidNow << endl;// After fork log on stderr too
399             _exit(128);
400           }
401         }
402
403         execvp(argv[0], const_cast<char *const *>(argv));
404         // don't want to get here
405         _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
406         std::cerr << _execError << endl;// After fork log on stderr too
407         _exit (129);                    // No sense in returning! I am forked away!!
408         //////////////////////////////////////////////////////////////////////
409       }
410
411       else if (pid == -1)        // Fork failed, close everything.
412       {
413         _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
414         _exitStatus = 127;
415         ERR << _execError << endl;
416
417         if (use_pty) {
418             ::close(master_tty);
419             ::close(slave_tty);
420         }
421         else {
422             ::close(to_external[0]);
423             ::close(to_external[1]);
424             ::close(from_external[0]);
425             ::close(from_external[1]);
426         }
427       }
428
429       else {
430         if (use_pty)
431         {
432             ::close(slave_tty);        // belongs to child process
433             inputfile  = fdopen(master_tty, "r");
434             outputfile = fdopen(master_tty, "w");
435         }
436         else
437         {
438             ::close(to_external[0]);   // belongs to child process
439             ::close(from_external[1]); // belongs to child process
440             inputfile = fdopen(from_external[0], "r");
441             outputfile = fdopen(to_external[1], "w");
442         }
443
444         DBG << "pid " << pid << " launched" << endl;
445
446         if (!inputfile || !outputfile)
447         {
448             ERR << "Cannot create streams to external program " << argv[0] << endl;
449             close();
450         }
451       }
452     }
453
454
455     int
456     ExternalProgram::close()
457     {
458       if (pid > 0)
459       {
460         if ( inputFile() )
461         {
462           // Discard any output instead of closing the pipe,
463           // but watch out for the command exiting while some
464           // subprocess keeps the filedescriptor open.
465           setBlocking( false );
466           FILE * inputfile = inputFile();
467           int    inputfileFd = ::fileno( inputfile );
468           long   delay = 0;
469           do
470           {
471             /* Watch inputFile to see when it has input. */
472             fd_set rfds;
473             FD_ZERO( &rfds );
474             FD_SET( inputfileFd, &rfds );
475
476             /* Wait up to 1 seconds. */
477             struct timeval tv;
478             tv.tv_sec  = (delay < 0 ? 1 : 0);
479             tv.tv_usec = (delay < 0 ? 0 : delay*100000);
480             if ( delay >= 0 && ++delay > 9 )
481               delay = -1;
482             int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
483
484             if ( retval == -1 )
485             {
486               if ( errno != EINTR ) {
487                 ERR << "select error: " << strerror(errno) << endl;
488                 break;
489               }
490             }
491             else if ( retval )
492             {
493               // Data is available now.
494               static size_t linebuffer_size = 0;      // static because getline allocs
495               static char * linebuffer = 0;           // and reallocs if buffer is too small
496               getline( &linebuffer, &linebuffer_size, inputfile );
497               // ::feof check is important as select returns
498               // positive if the file was closed.
499               if ( ::feof( inputfile ) )
500                 break;
501               clearerr( inputfile );
502             }
503             else
504             {
505               // No data within time.
506               if ( ! running() )
507                 break;
508             }
509           } while ( true );
510         }
511
512         if ( pid > 0 )  // bsc#1109877: must re-check! running() in the loop above may have already waited.
513         {
514           // Wait for child to exit
515           int ret;
516           int status = 0;
517           do
518           {
519             ret = waitpid(pid, &status, 0);
520           }
521           while (ret == -1 && errno == EINTR);
522
523           if (ret != -1)
524           {
525             _exitStatus = checkStatus( status );
526           }
527           pid = -1;
528         }
529       }
530
531       return _exitStatus;
532     }
533
534
535     int ExternalProgram::checkStatus( int status )
536     {
537       if (WIFEXITED (status))
538       {
539         status = WEXITSTATUS (status);
540         if(status)
541         {
542             DBG << "Pid " << pid << " exited with status " << status << endl;
543             _execError = str::form( _("Command exited with status %d."), status );
544         }
545         else
546         {
547             // if 'launch' is logged, completion should be logged,
548             // even if successfull.
549             DBG << "Pid " << pid << " successfully completed" << endl;
550             _execError.clear(); // empty if running or successfully completed
551         }
552       }
553       else if (WIFSIGNALED (status))
554       {
555         status = WTERMSIG (status);
556         WAR << "Pid " << pid << " was killed by signal " << status
557                 << " (" << strsignal(status);
558         if (WCOREDUMP (status))
559         {
560             WAR << ", core dumped";
561         }
562         WAR << ")" << endl;
563         _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
564         status+=128;
565       }
566       else {
567         ERR << "Pid " << pid << " exited with unknown error" << endl;
568         _execError = _("Command exited with unknown error.");
569       }
570
571       return status;
572     }
573
574     bool
575     ExternalProgram::kill()
576     {
577       if (pid > 0)
578       {
579         ::kill(pid, SIGKILL);
580         close();
581       }
582       return true;
583     }
584
585     bool ExternalProgram::kill(int sig)
586     {
587       if (pid > 0)
588       {
589         ::kill(pid, sig);
590       }
591       return true;
592     }
593
594     bool
595     ExternalProgram::running()
596     {
597       if ( pid < 0 ) return false;
598
599       int status = 0;
600       int p = waitpid( pid, &status, WNOHANG );
601       switch ( p )
602         {
603         case -1:
604           ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
605           return false;
606           break;
607         case 0:
608           return true; // still running
609           break;
610         }
611
612       // Here: completed...
613       _exitStatus = checkStatus( status );
614       pid = -1;
615       return false;
616     }
617
618     // origfd will be accessible as newfd and closed (unless they were equal)
619     void ExternalProgram::renumber_fd (int origfd, int newfd)
620     {
621       // It may happen that origfd is already the one we want
622       // (Although in our circumstances, that would mean somebody has closed
623       // our stdin or stdout... weird but has appened to Cray, #49797)
624       if (origfd != newfd)
625       {
626         dup2 (origfd, newfd);
627         ::close (origfd);
628       }
629     }
630
631     std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
632     {
633       setBlocking( true );
634       for ( std::string line = receiveLine(); line.length(); line = receiveLine() )
635         out_r << line;
636       return out_r;
637     }
638
639     //////////////////////////////////////////////////////////////////////
640     //
641     // class ExternalProgramWithStderr
642     //
643     //////////////////////////////////////////////////////////////////////
644
645     namespace externalprogram
646     {
647       EarlyPipe::EarlyPipe()
648       {
649         _fds[R] = _fds[W] = -1;
650 #ifdef HAVE_PIPE2
651         ::pipe2( _fds, O_NONBLOCK );
652 #else
653         ::pipe( _fds );
654         ::fcntl(_fds[R], F_SETFD, O_NONBLOCK );
655         ::fcntl(_fds[W], F_SETFD, O_NONBLOCK );
656 #endif
657         _stderr = ::fdopen( _fds[R], "r" );
658       }
659
660       EarlyPipe::~EarlyPipe()
661       {
662         closeW();
663         if ( _stderr )
664           ::fclose( _stderr );
665       }
666     } // namespace externalprogram
667
668     bool ExternalProgramWithStderr::stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r )
669     {
670       if ( ! _stderr )
671         return false;
672       if ( delim_r && ! _buffer.empty() )
673       {
674         // check for delim already in buffer
675         std::string::size_type pos( _buffer.find( delim_r ) );
676         if ( pos != std::string::npos )
677         {
678           retval_r = _buffer.substr( 0, returnDelim_r ? pos+1 : pos );
679           _buffer.erase( 0, pos+1 );
680           return true;
681         }
682       }
683       ::clearerr( _stderr );
684       do {
685         int ch = fgetc( _stderr );
686         if ( ch != EOF )
687         {
688           if ( ch != delim_r || ! delim_r )
689             _buffer.push_back( ch );
690           else
691           {
692             if ( returnDelim_r )
693               _buffer.push_back( delim_r );
694             break;
695           }
696         }
697         else if ( ::feof( _stderr ) )
698         {
699           if ( _buffer.empty() )
700             return false;
701           break;
702         }
703         else if ( errno != EINTR )
704           return false;
705       } while ( true );
706       // HERE: we left after readig at least one char (\n)
707       retval_r.swap( _buffer );
708       _buffer.clear();
709       return true;
710     }
711
712
713 } // namespace zypp