add option to forward environment to pipestream
[libi2ncommon] / src / pipestream.cpp
1  /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /***************************************************************************
21               inpipestream.cpp  -  C++ streambuffer wrapper 
22                              -------------------
23     begin                : Thu Dec 27 2001
24     copyright            : (C) 2001 by Intra2net AG
25  ***************************************************************************/
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33
34 #include <streambuf>
35 #include <istream>
36 #include <ostream>
37 #include <cstdio>
38 #include <boost/foreach.hpp>
39 #include <boost/shared_array.hpp>
40
41 #include "exception.hxx"
42 #include "stringfunc.hxx"
43 #include "pipestream.hxx"
44
45 /** @brief runs command and returns it's output as string
46  *  @param command the full command with all parameters
47  *  @param rescode struct containing the return code, if the program exited normally and so on
48  *  @param out Whether to collect \c stdout.
49  *  @param err Whether to collect \c stderr; combines with \c out.
50  *  @param path Wether to look up the executable in \c $PATH.
51  *  @returns the output (stdout) of the called program
52  */
53 template <typename CmdT>
54 std::string capture_exec(CmdT command, ExecResult &rescode,
55                          const bool out, const bool err,
56                          const bool path, const bool env)
57 {
58     std::string output;
59
60     bool exit_set = false;
61     int exit_status_waitpid;
62
63     // set the results to false until we are sure we have proper values
64     rescode.normal_exit = false;
65     rescode.terminated_by_signal = false;
66
67     try
68     {
69         {
70             inpipestream ips(command, out, err, path, env);
71
72             ips.store_exit_status(&exit_set, &exit_status_waitpid);
73
74             char buffer[2048];
75             while (ips.good())
76             {
77                 ips.read(buffer, sizeof(buffer));
78                 output.append(buffer, ips.gcount());
79             }
80         }
81
82         // exit_status_waitpid only valid after destruction of the inpipestream
83
84         if (exit_set)
85         {
86             rescode.normal_exit = WIFEXITED(exit_status_waitpid);
87             if (rescode.normal_exit)
88                 rescode.return_code = WEXITSTATUS(exit_status_waitpid);
89
90             rescode.terminated_by_signal = WIFSIGNALED(exit_status_waitpid);
91             if (rescode.terminated_by_signal)
92                 rescode.signal = WTERMSIG(exit_status_waitpid);
93         }
94     }
95     catch (pipestream_error &e)
96     {
97         rescode.error_message = e.what();
98     }
99
100     return output;
101 }
102
103 /** @brief      Instantiation of \c capture_exec for STL string arguments.
104  *              Caveat emptor: this will cause the backing stream to use \c
105  *              popen(3). To avoid shelling out, please refer to one of the
106  *              variants that allow passing an argument list.
107  *
108  *  @param command    String specifying the shell expression to be executed.
109  *  @param res        (Out parameter) Store information about the termination
110  *                    state in this struct.
111  *
112  *  @returns          Result of \c stdout. Note that due to the use of \c
113  *                    popen, the correct way to collect stderr output as
114  *                    well is to use shell redirection inside the expression
115  *                    passed.
116  */
117 std::string capture_exec (const std::string &command, ExecResult &res)
118 { return capture_exec<const std::string &>(command, res, true, false, false, false); }
119
120 /** @brief      Instantiation of \c capture_exec for argument lists. The
121  *              pipestream used to run the command will not shell out.
122  *              One of \c out or \c err must be set.
123  *
124  *  @param command    List of \c char* specifying the \c argv array of the
125  *                    command to run. Note that the binary to executed is
126  *                    assumed to be present at index 0 and that the input
127  *                    is properly \c NULL terminated.
128  *  @param res        (Out parameter) Store information about the termination
129  *                    state in this struct.
130  *  @param out        Whether to collect \c stdout.
131  *  @param err        Whether to collect \c stderr; combines with \c out.
132  *  @param path       Wether to look up the executable in \c $PATH.
133  *
134  *  @returns          Captured output, combined into one string.
135  */
136 std::string capture_exec (const char *const *command, ExecResult &res,
137                           const bool out, const bool err, const bool path,
138                           const bool env)
139 { return capture_exec<const char *const *>(command, res, out, err, path, env); }
140
141 /** @brief      Instantiation of \c capture_exec for argument lists. The
142  *              pipestream used to run the command will not shell out.
143  *              One of \c out or \c err must be set.
144  *
145  *  @param command    String vector specifying the \c argv array of the
146  *                    command to run. Note that the binary to executed is
147  *                    assumed to be present at index 0.
148  *  @param res        (Out parameter) Store information about the termination
149  *                    state in this struct.
150  *  @param out        Whether to collect \c stdout.
151  *  @param err        Whether to collect \c stderr; combines with \c out.
152  *  @param path       Wether to look up the executable in \c $PATH.
153  *
154  *  @returns          Captured output, combined into one string.
155  */
156 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
157                           const bool out, const bool err, const bool path,
158                           const bool env)
159 {
160     return capture_exec<const std::vector<std::string> &>
161         (command, res, out, err, path, env);
162 }
163
164 #define PIPE_CTOR_FAIL(where) \
165     do { \
166         throw EXCEPTION (pipestream_error, \
167                          std::string (where) + ": error " \
168                          + I2n::to_string (errno) \
169                          + " (" + std::string (strerror (errno)) + ")"); \
170     } while (0)
171
172 /** @brief      Convert a string vector to a refcounted \c char**
173  *              that is \c NULL terminated for use with e. g. \c execve(2).
174  *
175  *  @param command    List of arguments including the binary at index 0.
176  *
177  *  @returns          A \c boost::shared_array of pointers to the
178  *                    arguments plus a trailing \c NULL. Note that
179  *                    while the array itself is refcounted, the
180  *                    pointees are assumed owned by the caller and
181  *                    *not copyied*. I. e. they lose validity if the
182  *                    original strings are freed.
183  */
184 static boost::shared_array <char *>
185 mk_argv (const std::vector<std::string> &command)
186 {
187     char **ret = NULL;
188
189     try {
190         ret = new char *[command.size () * sizeof (ret[0]) + 1];
191     } catch (std::bad_alloc &) {
192         return boost::shared_array<char *> ();
193     }
194
195     size_t cur = 0;
196     BOOST_FOREACH(const std::string &arg, command) {
197         /*
198          * Casting away constness is safe since the data is always
199          * kept alive until after exec().
200          */
201         ret [cur++] = const_cast<char *> (arg.c_str ());
202     }
203
204     ret [cur] = NULL;
205
206     return boost::shared_array<char *> (ret);
207 }
208
209 /** @brief      Helper for redirecting a file descriptor to \c /dev/null.
210  *              This will only acquire an fd the first time it is called
211  *              or if it is called after unsuccessfully attempting to
212  *              acquire one.
213  *
214  *  @param fd         The open file descriptor to operate on.
215  *  @param save_errno Out parameter: stores errno here after a syscall failure.
216  *
217  *  @returns          \c true on success, \c false otherwise (the call to
218  *                    either \c open(2) or \c dup2(2) failed), with errno
219  *                    communicated through saved_errno.
220  */
221 static bool
222 redirect_devnull (const int fd, int &save_errno)
223 {
224     static int nullfd = -1;
225     
226     errno = 0;
227     if (nullfd == -1 && (nullfd = open ("/dev/null", O_RDWR)) == -1) {
228         save_errno = errno;
229         return false;
230     }
231
232     errno = 0;
233     if (dup2 (nullfd, fd) == -1) {
234         save_errno = errno;
235         return false;
236     }
237
238     return true;
239 }
240
241 /** @brief      Helper aggregating common code for the shell-free ctors.
242  *
243  *  @param argv       Argument list prepared for \c execve(2).
244  *  @param out        Whether to capture \c stdout.
245  *  @param err        Whether to capture \c stderr.
246  *
247  *  @returns          A \c FILE* handle for streaming if successful, \c NULL
248  *                    otherwise.
249  */
250 std::pair <pid_t, FILE *>
251 inpipebuf::init_without_shell (const char *const *argv,
252                                const bool out,
253                                const bool err,
254                                const bool path,
255                                const bool env) const
256 {
257     FILE *pipeobj = NULL;
258     int pipefd [2]; /* for reading output from the child */
259     int errfd  [2]; /* for determining a successful exec() */
260     sigset_t oldmask, newmask;
261     char *const *envp = env ? environ : NULL;
262
263     if (!out && !err) {
264         errno = EINVAL;
265         PIPE_CTOR_FAIL("ctor");
266     }
267
268     errno = 0;
269     if (   ::pipe2 (pipefd, O_CLOEXEC) == -1
270         || ::pipe2 (errfd , O_CLOEXEC) == -1) {
271         PIPE_CTOR_FAIL("pipe2");
272     }
273
274     sigfillset (&newmask);
275     sigprocmask (SIG_SETMASK, &newmask, &oldmask);
276
277     errno = 0;
278     pid_t childpid = fork ();
279     switch (childpid) {
280         case -1: {
281             sigprocmask (SIG_SETMASK, &oldmask, NULL);
282             PIPE_CTOR_FAIL("fork");
283             break;
284         }
285         case 0: {
286             close (pipefd [0]);
287             close (errfd  [0]);
288
289             fcntl (pipefd [1], F_SETFD, 0);
290
291             int save_errno = 0;
292             if (!out) {
293                 if (!redirect_devnull (STDOUT_FILENO, save_errno)) {
294                     (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
295                     exit (EXIT_FAILURE);
296                 }
297             } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
298                 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
299                 exit (EXIT_FAILURE);
300             }
301
302             if (!err) {
303                 if (!redirect_devnull (STDERR_FILENO, save_errno)) {
304                     (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
305                     exit (EXIT_FAILURE);
306                 }
307             } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
308                 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
309                 exit (EXIT_FAILURE);
310             }
311
312             close (pipefd [1]);
313
314             sigprocmask (SIG_SETMASK, &oldmask, NULL);
315
316             errno = 0;
317             if (path) {
318                 execvpe (argv [0], const_cast <char *const *>(argv), envp);
319             } else {
320                 execve (argv [0], const_cast <char *const *>(argv), envp);
321             }
322
323             (void)write (errfd [1], (char *)&errno, sizeof(errno));
324             exit (EXIT_FAILURE);
325             break;
326         }
327         default: {
328             break;
329         }
330     }
331
332     close (pipefd [1]);
333     close (errfd  [1]);
334
335     /*
336      * Check whether the child exec()’ed by reading from the error pipe.
337      * The call to read(2) will block, uninterruptible due to signals being
338      * blocked. If all went well, the read(2) will return zero bytes and we can
339      * ditch the error channel.
340      *
341      * Otherwise either the read(2) failed or we actually received something
342      * through the error pipe. Both cases are treated as errors and cause an
343      * exit from the ctor.
344      */
345     char buf [sizeof (errno)];
346     int ret;
347     memset (buf, 0, sizeof (buf));
348     errno = 0;
349     if ((ret = read (errfd [0], buf, sizeof (buf))) != 0) {
350         close (pipefd [0]);
351         close (errfd  [0]);
352         sigprocmask (SIG_SETMASK, &oldmask, NULL);
353         if (ret == - 1) {
354             /* read(2) failed */
355             PIPE_CTOR_FAIL("read");
356         } else {
357             /*
358              * We received data on the error channel indicating the child
359              * process never successfully exec()’ed. We grab the error code
360              * from the buffer and bail.
361              */
362             errno = *((int *)&buf[0]);
363             PIPE_CTOR_FAIL("child failed to exec()");
364         }
365     }
366
367     /*
368      * read(2) yielded zero bytes; it’s safe to use the pipe so close our end
369      * and continue.
370      */
371     close (errfd [0]);
372
373     sigprocmask (SIG_SETMASK, &oldmask, NULL);
374
375     errno = 0;
376     if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
377         close (pipefd [0]);
378         PIPE_CTOR_FAIL("fdopen");
379     }
380
381     return std::make_pair (childpid, pipeobj);
382 }
383
384 inpipebuf::inpipebuf(const char *const *command,
385                      const bool out,
386                      const bool err,
387                      const bool path,
388                      const bool env)
389     : pipe (NULL) /* brr: shadowing global ident */
390     , pid (-1)
391     , status_set (NULL)
392     , exit_status (NULL)
393 {
394     if (command == NULL || command [0] == NULL) {
395         PIPE_CTOR_FAIL("command");
396     }
397
398     std::pair <pid_t, FILE *> tmp =
399         this->init_without_shell (command, out, err, path, env);
400     this->pid  = tmp.first; /* no std::tie :/ */
401     this->pipe = tmp.second;
402
403     setg (&buffer, &buffer, &buffer);
404 }
405
406 inpipebuf::inpipebuf(const std::vector<std::string> &command,
407                      const bool out,
408                      const bool err,
409                      const bool path,
410                      const bool env)
411     : pipe (NULL) /* brr: shadowing global ident */
412     , pid (-1)
413     , status_set (NULL)
414     , exit_status (NULL)
415 {
416     if (command.empty ()) {
417         PIPE_CTOR_FAIL("command");
418     }
419
420     const boost::shared_array <char *> argv = mk_argv (command);
421     if (!argv) {
422         PIPE_CTOR_FAIL("malloc");
423     }
424
425     std::pair <pid_t, FILE *> tmp =
426         this->init_without_shell (argv.get (), out, err, path, env);
427     this->pid  = tmp.first;
428     this->pipe = tmp.second;
429
430     setg (&buffer, &buffer, &buffer);
431 }
432
433 inpipebuf::inpipebuf(const std::string& command,
434                      const bool _ignored_out,
435                      const bool _ignored_err,
436                      const bool _ignored_path,
437                      const bool _ignored_env)
438     : pid (-1)
439     , status_set (NULL)
440     , exit_status (NULL)
441 {
442     pipe = popen (command.c_str(), "r");
443     if (pipe == NULL)
444         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
445
446     // force underflow
447     setg (&buffer, &buffer, &buffer);
448 }
449
450 inpipebuf::~inpipebuf()
451 {
452     if (pipe != NULL) {
453         int status;
454
455         if (this->pid == -1)
456         {
457             errno = 0;
458             status = pclose (pipe);
459             if (status != -1) {
460                 if (exit_status != NULL) {
461                     *exit_status = status;
462                     if (status_set != NULL) {
463                         *status_set = true;
464                     }
465                 }
466             }
467         }
468         else
469         {
470             errno = 0;
471             status = fclose (pipe);
472             if (status != EOF) {
473                 if (exit_status != NULL) {
474                     *exit_status = status; /* might be overwritten below */
475                     if (status_set != NULL) {
476                         *status_set = true;
477                     }
478                 }
479             }
480
481             errno = 0;
482             while (waitpid (this->pid, &status, 0) == -1) {
483                 if (errno != EINTR) {
484                     status = -1;
485                     break;
486                 }
487             }
488             if (status != 0 && exit_status != NULL) {
489                 *exit_status = status; /* might overwrite pipe status above */
490                 if (status_set != NULL) {
491                     *status_set = true;
492                 }
493             }
494         }
495
496         pipe = NULL;
497     }
498 }
499
500 /** note: exit status only available after destruction */
501 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
502
503     status_set = _status_set; 
504     exit_status = _exit_status; 
505 }
506
507 inpipebuf::int_type inpipebuf::underflow()
508 {
509     if (gptr() < egptr())
510         return traits_type::to_int_type(*gptr());
511
512     buffer = fgetc (pipe);
513     if (feof (pipe))
514     {
515         // ERROR or EOF
516         return EOF;
517     }
518
519     setg (&buffer, &buffer, &buffer+sizeof(char));
520
521     return traits_type::to_int_type(*gptr());
522 }
523
524 outpipebuf::outpipebuf(const std::string& command)
525 {
526     status_set = NULL;
527     exit_status = NULL;
528
529     pipe = popen (command.c_str(), "w");
530     if (pipe == NULL)
531         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
532 }
533
534 outpipebuf::~outpipebuf()
535 {
536     if (pipe != NULL) {
537         int pclose_exit = pclose (pipe);
538
539         if (exit_status && pclose_exit != -1)
540         {
541             if (status_set)
542                 *status_set = true;
543             *exit_status = pclose_exit;
544         }
545
546         pipe = NULL;
547     }
548 }
549
550 /** note: exit status only available after destruction */
551 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
552
553     status_set = _status_set; 
554     exit_status = _exit_status; 
555 }
556
557 outpipebuf::int_type outpipebuf::overflow(int_type c)
558 {
559     if (c != EOF)
560     {
561         if (fputc(c,pipe)==EOF)
562             return EOF;
563     }
564     return c;
565 }
566
567 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
568 {
569     return fwrite(s,num,1,pipe);
570 }