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