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