distinguish child errors from failing to exec
[libi2ncommon] / src / pipestream.cpp
CommitLineData
7e606af5
GE
1 /*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on 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
9e322fb7 27#include <errno.h>
7e606af5 28#include <stdio.h>
9e322fb7 29#include <string.h>
ff5191e6 30#include <fcntl.h>
f8f119bf 31#include <sys/wait.h>
9e322fb7 32#include <unistd.h>
7e606af5 33
7e606af5
GE
34#include <streambuf>
35#include <istream>
36#include <ostream>
37#include <cstdio>
9e322fb7
PG
38#include <boost/foreach.hpp>
39#include <boost/shared_array.hpp>
7e606af5
GE
40
41#include "exception.hxx"
9e322fb7 42#include "stringfunc.hxx"
7e606af5
GE
43#include "pipestream.hxx"
44
f8f119bf
GE
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
ad4490f1
PG
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.
f8f119bf
GE
51 * @returns the output (stdout) of the called program
52 */
9e322fb7 53template <typename CmdT>
cc917897 54std::string capture_exec(CmdT command, ExecResult &rescode,
ad4490f1
PG
55 const bool out, const bool err,
56 const bool path)
f8f119bf
GE
57{
58 std::string output;
59
1d7539d5 60 bool exit_set = false;
f8f119bf
GE
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 {
ad4490f1 70 inpipestream ips(command, out, err, path);
f8f119bf
GE
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
cc917897
PG
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 */
9e322fb7 117std::string capture_exec (const std::string &command, ExecResult &res)
ad4490f1 118{ return capture_exec<const std::string &>(command, res, true, false, false); }
cc917897
PG
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.
ad4490f1 132 * @param path Wether to look up the executable in \c $PATH.
cc917897
PG
133 *
134 * @returns Captured output, combined into one string.
135 */
136std::string capture_exec (const char *const *command, ExecResult &res,
ad4490f1
PG
137 const bool out, const bool err, const bool path)
138{ return capture_exec<const char *const *>(command, res, out, err, path); }
cc917897
PG
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.
ad4490f1 151 * @param path Wether to look up the executable in \c $PATH.
cc917897
PG
152 *
153 * @returns Captured output, combined into one string.
154 */
155std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
ad4490f1 156 const bool out, const bool err, const bool path)
c2c29997 157{
cc917897 158 return capture_exec<const std::vector<std::string> &>
ad4490f1 159 (command, res, out, err, path);
c2c29997
PG
160}
161
9e322fb7
PG
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
cc917897
PG
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 */
9e322fb7
PG
182static boost::shared_array <char *>
183mk_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
ff5191e6
PG
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.
08199c66 213 * @param save_errno Out parameter: stores errno here after a syscall failure.
ff5191e6
PG
214 *
215 * @returns \c true on success, \c false otherwise (the call to
08199c66
PG
216 * either \c open(2) or \c dup2(2) failed), with errno
217 * communicated through saved_errno.
ff5191e6
PG
218 */
219static bool
08199c66 220redirect_devnull (const int fd, int &save_errno)
ff5191e6
PG
221{
222 static int nullfd = -1;
223
224 errno = 0;
225 if (nullfd == -1 && (nullfd = open ("/dev/null", O_RDWR)) == -1) {
08199c66 226 save_errno = errno;
ff5191e6
PG
227 return false;
228 }
229
230 errno = 0;
231 if (dup2 (nullfd, fd) == -1) {
08199c66 232 save_errno = errno;
ff5191e6
PG
233 return false;
234 }
235
236 return true;
237}
238
cc917897
PG
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 */
17b459b3 248std::pair <pid_t, FILE *>
cc917897
PG
249inpipebuf::init_without_shell (const char *const *argv,
250 const bool out,
ad4490f1
PG
251 const bool err,
252 const bool path) const
9e322fb7
PG
253{
254 FILE *pipeobj = NULL;
08199c66
PG
255 int pipefd [2]; /* for reading output from the child */
256 int errfd [2]; /* for determining a successful exec() */
f2da42aa 257 sigset_t oldmask, newmask;
9e322fb7 258
cc917897
PG
259 if (!out && !err) {
260 errno = EINVAL;
261 PIPE_CTOR_FAIL("ctor");
262 }
263
9e322fb7 264 errno = 0;
08199c66
PG
265 if ( ::pipe2 (pipefd, O_CLOEXEC) == -1
266 || ::pipe2 (errfd , O_CLOEXEC) == -1) {
a30f9a22 267 PIPE_CTOR_FAIL("pipe2");
9e322fb7
PG
268 }
269
f2da42aa
PG
270 sigfillset (&newmask);
271 sigprocmask (SIG_SETMASK, &newmask, &oldmask);
272
9e322fb7
PG
273 errno = 0;
274 pid_t childpid = fork ();
275 switch (childpid) {
276 case -1: {
f2da42aa 277 sigprocmask (SIG_SETMASK, &oldmask, NULL);
9e322fb7
PG
278 PIPE_CTOR_FAIL("fork");
279 break;
280 }
281 case 0: {
282 close (pipefd [0]);
08199c66 283 close (errfd [0]);
9e322fb7 284
a30f9a22
PG
285 fcntl (pipefd [1], F_SETFD, 0);
286
08199c66 287 int save_errno = 0;
cc917897 288 if (!out) {
08199c66
PG
289 if (!redirect_devnull (STDOUT_FILENO, save_errno)) {
290 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
291 exit (EXIT_FAILURE);
ff5191e6 292 }
cc917897 293 } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
08199c66
PG
294 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
295 exit (EXIT_FAILURE);
9e322fb7
PG
296 }
297
cc917897 298 if (!err) {
08199c66
PG
299 if (!redirect_devnull (STDERR_FILENO, save_errno)) {
300 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
301 exit (EXIT_FAILURE);
ff5191e6 302 }
cc917897 303 } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
08199c66
PG
304 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
305 exit (EXIT_FAILURE);
9e322fb7
PG
306 }
307
17b459b3
PG
308 close (pipefd [1]);
309
f2da42aa
PG
310 sigprocmask (SIG_SETMASK, &oldmask, NULL);
311
9e322fb7 312 errno = 0;
ad4490f1
PG
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);
9e322fb7 317 }
08199c66
PG
318
319 (void)write (errfd [1], (char *)&errno, sizeof(errno));
320 exit (EXIT_FAILURE);
9e322fb7
PG
321 break;
322 }
323 default: {
9e322fb7
PG
324 break;
325 }
326 }
327
08199c66
PG
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
17b459b3 377 return std::make_pair (childpid, pipeobj);
9e322fb7
PG
378}
379
cc917897
PG
380inpipebuf::inpipebuf(const char *const *command,
381 const bool out,
ad4490f1
PG
382 const bool err,
383 const bool path)
9e322fb7 384 : pipe (NULL) /* brr: shadowing global ident */
17b459b3 385 , pid (-1)
9e322fb7
PG
386 , status_set (NULL)
387 , exit_status (NULL)
388{
389 if (command == NULL || command [0] == NULL) {
390 PIPE_CTOR_FAIL("command");
391 }
392
ad4490f1
PG
393 std::pair <pid_t, FILE *> tmp =
394 this->init_without_shell (command, out, err, path);
17b459b3
PG
395 this->pid = tmp.first; /* no std::tie :/ */
396 this->pipe = tmp.second;
9e322fb7
PG
397
398 setg (&buffer, &buffer, &buffer);
399}
400
cc917897
PG
401inpipebuf::inpipebuf(const std::vector<std::string> &command,
402 const bool out,
ad4490f1
PG
403 const bool err,
404 const bool path)
c2c29997 405 : pipe (NULL) /* brr: shadowing global ident */
17b459b3 406 , pid (-1)
c2c29997
PG
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
ad4490f1
PG
419 std::pair <pid_t, FILE *> tmp =
420 this->init_without_shell (argv.get (), out, err, path);
17b459b3
PG
421 this->pid = tmp.first;
422 this->pipe = tmp.second;
c2c29997
PG
423
424 setg (&buffer, &buffer, &buffer);
425}
426
cc917897
PG
427inpipebuf::inpipebuf(const std::string& command,
428 const bool _ignored_out,
ad4490f1
PG
429 const bool _ignored_err,
430 const bool _ignored_path)
17b459b3
PG
431 : pid (-1)
432 , status_set (NULL)
433 , exit_status (NULL)
7e606af5 434{
7e606af5
GE
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
443inpipebuf::~inpipebuf()
444{
445 if (pipe != NULL) {
17b459b3 446 int status;
7e606af5 447
17b459b3 448 if (this->pid == -1)
d00589a0 449 {
17b459b3
PG
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 }
7e606af5
GE
487 }
488
489 pipe = NULL;
490 }
491}
492
493/** note: exit status only available after destruction */
494void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
495{
496 status_set = _status_set;
497 exit_status = _exit_status;
498}
499
500inpipebuf::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
7e606af5
GE
517outpipebuf::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
527outpipebuf::~outpipebuf()
528{
529 if (pipe != NULL) {
530 int pclose_exit = pclose (pipe);
531
d00589a0
GE
532 if (exit_status && pclose_exit != -1)
533 {
534 if (status_set)
535 *status_set = true;
7e606af5
GE
536 *exit_status = pclose_exit;
537 }
538
539 pipe = NULL;
540 }
541}
542
543/** note: exit status only available after destruction */
544void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
545{
546 status_set = _status_set;
547 exit_status = _exit_status;
548}
549
550outpipebuf::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
560std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
561{
562 return fwrite(s,num,1,pipe);
563}