57b1dbedf43a68e42550fd5178cbcce2e07959ae
[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)
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);
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); }
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 { return capture_exec<const char *const *>(command, res, out, err, path); }
139
140 /** @brief      Instantiation of \c capture_exec for argument lists. The
141  *              pipestream used to run the command will not shell out.
142  *              One of \c out or \c err must be set.
143  *
144  *  @param command    String vector specifying the \c argv array of the
145  *                    command to run. Note that the binary to executed is
146  *                    assumed to be present at index 0.
147  *  @param res        (Out parameter) Store information about the termination
148  *                    state in this struct.
149  *  @param out        Whether to collect \c stdout.
150  *  @param err        Whether to collect \c stderr; combines with \c out.
151  *  @param path       Wether to look up the executable in \c $PATH.
152  *
153  *  @returns          Captured output, combined into one string.
154  */
155 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
156                           const bool out, const bool err, const bool path)
157 {
158     return capture_exec<const std::vector<std::string> &>
159         (command, res, out, err, path);
160 }
161
162 #define PIPE_CTOR_FAIL(where) \
163     do { \
164         throw EXCEPTION (pipestream_error, \
165                          std::string (where) + ": error " \
166                          + I2n::to_string (errno) \
167                          + " (" + std::string (strerror (errno)) + ")"); \
168     } while (0)
169
170 /** @brief      Convert a string vector to a refcounted \c char**
171  *              that is \c NULL terminated for use with e. g. \c execve(2).
172  *
173  *  @param command    List of arguments including the binary at index 0.
174  *
175  *  @returns          A \c boost::shared_array of pointers to the
176  *                    arguments plus a trailing \c NULL. Note that
177  *                    while the array itself is refcounted, the
178  *                    pointees are assumed owned by the caller and
179  *                    *not copyied*. I. e. they lose validity if the
180  *                    original strings are freed.
181  */
182 static boost::shared_array <char *>
183 mk_argv (const std::vector<std::string> &command)
184 {
185     char **ret = NULL;
186
187     try {
188         ret = new char *[command.size () * sizeof (ret[0]) + 1];
189     } catch (std::bad_alloc &) {
190         return boost::shared_array<char *> ();
191     }
192
193     size_t cur = 0;
194     BOOST_FOREACH(const std::string &arg, command) {
195         /*
196          * Casting away constness is safe since the data is always
197          * kept alive until after exec().
198          */
199         ret [cur++] = const_cast<char *> (arg.c_str ());
200     }
201
202     ret [cur] = NULL;
203
204     return boost::shared_array<char *> (ret);
205 }
206
207 /** @brief      Helper for redirecting a file descriptor to \c /dev/null.
208  *              This will only acquire an fd the first time it is called
209  *              or if it is called after unsuccessfully attempting to
210  *              acquire one.
211  *
212  *  @param fd         The open file descriptor to operate on.
213  *  @param save_errno Out parameter: stores errno here after a syscall failure.
214  *
215  *  @returns          \c true on success, \c false otherwise (the call to
216  *                    either \c open(2) or \c dup2(2) failed), with errno
217  *                    communicated through saved_errno.
218  */
219 static bool
220 redirect_devnull (const int fd, int &save_errno)
221 {
222     static int nullfd = -1;
223     
224     errno = 0;
225     if (nullfd == -1 && (nullfd = open ("/dev/null", O_RDWR)) == -1) {
226         save_errno = errno;
227         return false;
228     }
229
230     errno = 0;
231     if (dup2 (nullfd, fd) == -1) {
232         save_errno = errno;
233         return false;
234     }
235
236     return true;
237 }
238
239 /** @brief      Helper aggregating common code for the shell-free ctors.
240  *
241  *  @param argv       Argument list prepared for \c execve(2).
242  *  @param out        Whether to capture \c stdout.
243  *  @param err        Whether to capture \c stderr.
244  *
245  *  @returns          A \c FILE* handle for streaming if successful, \c NULL
246  *                    otherwise.
247  */
248 std::pair <pid_t, FILE *>
249 inpipebuf::init_without_shell (const char *const *argv,
250                                const bool out,
251                                const bool err,
252                                const bool path) const
253 {
254     FILE *pipeobj = NULL;
255     int pipefd [2]; /* for reading output from the child */
256     int errfd  [2]; /* for determining a successful exec() */
257     sigset_t oldmask, newmask;
258
259     if (!out && !err) {
260         errno = EINVAL;
261         PIPE_CTOR_FAIL("ctor");
262     }
263
264     errno = 0;
265     if (   ::pipe2 (pipefd, O_CLOEXEC) == -1
266         || ::pipe2 (errfd , O_CLOEXEC) == -1) {
267         PIPE_CTOR_FAIL("pipe2");
268     }
269
270     sigfillset (&newmask);
271     sigprocmask (SIG_SETMASK, &newmask, &oldmask);
272
273     errno = 0;
274     pid_t childpid = fork ();
275     switch (childpid) {
276         case -1: {
277             sigprocmask (SIG_SETMASK, &oldmask, NULL);
278             PIPE_CTOR_FAIL("fork");
279             break;
280         }
281         case 0: {
282             close (pipefd [0]);
283             close (errfd  [0]);
284
285             fcntl (pipefd [1], F_SETFD, 0);
286
287             int save_errno = 0;
288             if (!out) {
289                 if (!redirect_devnull (STDOUT_FILENO, save_errno)) {
290                     (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
291                     exit (EXIT_FAILURE);
292                 }
293             } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
294                 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
295                 exit (EXIT_FAILURE);
296             }
297
298             if (!err) {
299                 if (!redirect_devnull (STDERR_FILENO, save_errno)) {
300                     (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
301                     exit (EXIT_FAILURE);
302                 }
303             } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
304                 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
305                 exit (EXIT_FAILURE);
306             }
307
308             close (pipefd [1]);
309
310             sigprocmask (SIG_SETMASK, &oldmask, NULL);
311
312             errno = 0;
313             if (path) {
314                 execvpe (argv [0], const_cast <char *const *>(argv), environ);
315             } else {
316                 execve (argv [0], const_cast <char *const *>(argv), NULL);
317             }
318
319             (void)write (errfd [1], (char *)&errno, sizeof(errno));
320             exit (EXIT_FAILURE);
321             break;
322         }
323         default: {
324             break;
325         }
326     }
327
328     close (pipefd [1]);
329     close (errfd  [1]);
330
331     /*
332      * Check whether the child exec()’ed by reading from the error pipe.
333      * The call to read(2) will block, uninterruptible due to signals being
334      * blocked. If all went well, the read(2) will return zero bytes and we can
335      * ditch the error channel.
336      *
337      * Otherwise either the read(2) failed or we actually received something
338      * through the error pipe. Both cases are treated as errors and cause an
339      * exit from the ctor.
340      */
341     char buf [sizeof (errno)];
342     int ret;
343     memset (buf, 0, sizeof (buf));
344     errno = 0;
345     if ((ret = read (errfd [0], buf, sizeof (buf))) != 0) {
346         close (pipefd [0]);
347         close (errfd  [0]);
348         sigprocmask (SIG_SETMASK, &oldmask, NULL);
349         if (ret == - 1) {
350             /* read(2) failed */
351             PIPE_CTOR_FAIL("read");
352         } else {
353             /*
354              * We received data on the error channel indicating the child
355              * process never successfully exec()’ed. We grab the error code
356              * from the buffer and bail.
357              */
358             errno = *((int *)&buf[0]);
359             PIPE_CTOR_FAIL("child failed to exec()");
360         }
361     }
362
363     /*
364      * read(2) yielded zero bytes; it’s safe to use the pipe so close our end
365      * and continue.
366      */
367     close (errfd [0]);
368
369     sigprocmask (SIG_SETMASK, &oldmask, NULL);
370
371     errno = 0;
372     if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
373         close (pipefd [0]);
374         PIPE_CTOR_FAIL("fdopen");
375     }
376
377     return std::make_pair (childpid, pipeobj);
378 }
379
380 inpipebuf::inpipebuf(const char *const *command,
381                      const bool out,
382                      const bool err,
383                      const bool path)
384     : pipe (NULL) /* brr: shadowing global ident */
385     , pid (-1)
386     , status_set (NULL)
387     , exit_status (NULL)
388 {
389     if (command == NULL || command [0] == NULL) {
390         PIPE_CTOR_FAIL("command");
391     }
392
393     std::pair <pid_t, FILE *> tmp =
394         this->init_without_shell (command, out, err, path);
395     this->pid  = tmp.first; /* no std::tie :/ */
396     this->pipe = tmp.second;
397
398     setg (&buffer, &buffer, &buffer);
399 }
400
401 inpipebuf::inpipebuf(const std::vector<std::string> &command,
402                      const bool out,
403                      const bool err,
404                      const bool path)
405     : pipe (NULL) /* brr: shadowing global ident */
406     , pid (-1)
407     , status_set (NULL)
408     , exit_status (NULL)
409 {
410     if (command.empty ()) {
411         PIPE_CTOR_FAIL("command");
412     }
413
414     const boost::shared_array <char *> argv = mk_argv (command);
415     if (!argv) {
416         PIPE_CTOR_FAIL("malloc");
417     }
418
419     std::pair <pid_t, FILE *> tmp =
420         this->init_without_shell (argv.get (), out, err, path);
421     this->pid  = tmp.first;
422     this->pipe = tmp.second;
423
424     setg (&buffer, &buffer, &buffer);
425 }
426
427 inpipebuf::inpipebuf(const std::string& command,
428                      const bool _ignored_out,
429                      const bool _ignored_err,
430                      const bool _ignored_path)
431     : pid (-1)
432     , status_set (NULL)
433     , exit_status (NULL)
434 {
435     pipe = popen (command.c_str(), "r");
436     if (pipe == NULL)
437         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
438
439     // force underflow
440     setg (&buffer, &buffer, &buffer);
441 }
442
443 inpipebuf::~inpipebuf()
444 {
445     if (pipe != NULL) {
446         int status;
447
448         if (this->pid == -1)
449         {
450             errno = 0;
451             status = pclose (pipe);
452             if (status != -1) {
453                 if (exit_status != NULL) {
454                     *exit_status = status;
455                     if (status_set != NULL) {
456                         *status_set = true;
457                     }
458                 }
459             }
460         }
461         else
462         {
463             errno = 0;
464             status = fclose (pipe);
465             if (status != EOF) {
466                 if (exit_status != NULL) {
467                     *exit_status = status; /* might be overwritten below */
468                     if (status_set != NULL) {
469                         *status_set = true;
470                     }
471                 }
472             }
473
474             errno = 0;
475             while (waitpid (this->pid, &status, 0) == -1) {
476                 if (errno != EINTR) {
477                     status = -1;
478                     break;
479                 }
480             }
481             if (status != 0 && exit_status != NULL) {
482                 *exit_status = status; /* might overwrite pipe status above */
483                 if (status_set != NULL) {
484                     *status_set = true;
485                 }
486             }
487         }
488
489         pipe = NULL;
490     }
491 }
492
493 /** note: exit status only available after destruction */
494 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
495
496     status_set = _status_set; 
497     exit_status = _exit_status; 
498 }
499
500 inpipebuf::int_type inpipebuf::underflow()
501 {
502     if (gptr() < egptr())
503         return traits_type::to_int_type(*gptr());
504
505     buffer = fgetc (pipe);
506     if (feof (pipe))
507     {
508         // ERROR or EOF
509         return EOF;
510     }
511
512     setg (&buffer, &buffer, &buffer+sizeof(char));
513
514     return traits_type::to_int_type(*gptr());
515 }
516
517 outpipebuf::outpipebuf(const std::string& command)
518 {
519     status_set = NULL;
520     exit_status = NULL;
521
522     pipe = popen (command.c_str(), "w");
523     if (pipe == NULL)
524         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
525 }
526
527 outpipebuf::~outpipebuf()
528 {
529     if (pipe != NULL) {
530         int pclose_exit = pclose (pipe);
531
532         if (exit_status && pclose_exit != -1)
533         {
534             if (status_set)
535                 *status_set = true;
536             *exit_status = pclose_exit;
537         }
538
539         pipe = NULL;
540     }
541 }
542
543 /** note: exit status only available after destruction */
544 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
545
546     status_set = _status_set; 
547     exit_status = _exit_status; 
548 }
549
550 outpipebuf::int_type outpipebuf::overflow(int_type c)
551 {
552     if (c != EOF)
553     {
554         if (fputc(c,pipe)==EOF)
555             return EOF;
556     }
557     return c;
558 }
559
560 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
561 {
562     return fwrite(s,num,1,pipe);
563 }