2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
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.
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.
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.
20 /***************************************************************************
21 inpipestream.cpp - C++ streambuffer wrapper
23 begin : Thu Dec 27 2001
24 copyright : (C) 2001 by Intra2net AG
25 ***************************************************************************/
38 #include <boost/foreach.hpp>
39 #include <boost/shared_array.hpp>
41 #include "exception.hxx"
42 #include "stringfunc.hxx"
43 #include "pipestream.hxx"
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
53 template <typename CmdT>
54 std::string capture_exec(CmdT command, ExecResult &rescode,
55 const bool out, const bool err,
60 bool exit_set = false;
61 int exit_status_waitpid;
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;
70 inpipestream ips(command, out, err, path);
72 ips.store_exit_status(&exit_set, &exit_status_waitpid);
77 ips.read(buffer, sizeof(buffer));
78 output.append(buffer, ips.gcount());
82 // exit_status_waitpid only valid after destruction of the inpipestream
86 rescode.normal_exit = WIFEXITED(exit_status_waitpid);
87 if (rescode.normal_exit)
88 rescode.return_code = WEXITSTATUS(exit_status_waitpid);
90 rescode.terminated_by_signal = WIFSIGNALED(exit_status_waitpid);
91 if (rescode.terminated_by_signal)
92 rescode.signal = WTERMSIG(exit_status_waitpid);
95 catch (pipestream_error &e)
97 rescode.error_message = e.what();
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.
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.
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
117 std::string capture_exec (const std::string &command, ExecResult &res)
118 { return capture_exec<const std::string &>(command, res, true, false, false); }
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.
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.
134 * @returns Captured output, combined into one string.
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); }
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.
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.
153 * @returns Captured output, combined into one string.
155 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
156 const bool out, const bool err, const bool path)
158 return capture_exec<const std::vector<std::string> &>
159 (command, res, out, err, path);
162 #define PIPE_CTOR_FAIL(where) \
164 throw EXCEPTION (pipestream_error, \
165 std::string (where) + ": error " \
166 + I2n::to_string (errno) \
167 + " (" + std::string (strerror (errno)) + ")"); \
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).
173 * @param command List of arguments including the binary at index 0.
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.
182 static boost::shared_array <char *>
183 mk_argv (const std::vector<std::string> &command)
188 ret = new char *[command.size () * sizeof (ret[0]) + 1];
189 } catch (std::bad_alloc &) {
190 return boost::shared_array<char *> ();
194 BOOST_FOREACH(const std::string &arg, command) {
196 * Casting away constness is safe since the data is always
197 * kept alive until after exec().
199 ret [cur++] = const_cast<char *> (arg.c_str ());
204 return boost::shared_array<char *> (ret);
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
212 * @param fd The open file descriptor to operate on.
213 * @param save_errno Out parameter: stores errno here after a syscall failure.
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.
220 redirect_devnull (const int fd, int &save_errno)
222 static int nullfd = -1;
225 if (nullfd == -1 && (nullfd = open ("/dev/null", O_RDWR)) == -1) {
231 if (dup2 (nullfd, fd) == -1) {
239 /** @brief Helper aggregating common code for the shell-free ctors.
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.
245 * @returns A \c FILE* handle for streaming if successful, \c NULL
248 std::pair <pid_t, FILE *>
249 inpipebuf::init_without_shell (const char *const *argv,
252 const bool path) const
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;
261 PIPE_CTOR_FAIL("ctor");
265 if ( ::pipe2 (pipefd, O_CLOEXEC) == -1
266 || ::pipe2 (errfd , O_CLOEXEC) == -1) {
267 PIPE_CTOR_FAIL("pipe2");
270 sigfillset (&newmask);
271 sigprocmask (SIG_SETMASK, &newmask, &oldmask);
274 pid_t childpid = fork ();
277 sigprocmask (SIG_SETMASK, &oldmask, NULL);
278 PIPE_CTOR_FAIL("fork");
285 fcntl (pipefd [1], F_SETFD, 0);
289 if (!redirect_devnull (STDOUT_FILENO, save_errno)) {
290 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
293 } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
294 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
299 if (!redirect_devnull (STDERR_FILENO, save_errno)) {
300 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
303 } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
304 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
310 sigprocmask (SIG_SETMASK, &oldmask, NULL);
314 execvpe (argv [0], const_cast <char *const *>(argv), environ);
316 execve (argv [0], const_cast <char *const *>(argv), NULL);
319 (void)write (errfd [1], (char *)&errno, sizeof(errno));
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.
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.
341 char buf [sizeof (errno)];
343 memset (buf, 0, sizeof (buf));
345 if ((ret = read (errfd [0], buf, sizeof (buf))) != 0) {
348 sigprocmask (SIG_SETMASK, &oldmask, NULL);
351 PIPE_CTOR_FAIL("read");
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.
358 errno = *((int *)&buf[0]);
359 PIPE_CTOR_FAIL("child failed to exec()");
364 * read(2) yielded zero bytes; it’s safe to use the pipe so close our end
369 sigprocmask (SIG_SETMASK, &oldmask, NULL);
372 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
374 PIPE_CTOR_FAIL("fdopen");
377 return std::make_pair (childpid, pipeobj);
380 inpipebuf::inpipebuf(const char *const *command,
384 : pipe (NULL) /* brr: shadowing global ident */
389 if (command == NULL || command [0] == NULL) {
390 PIPE_CTOR_FAIL("command");
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;
398 setg (&buffer, &buffer, &buffer);
401 inpipebuf::inpipebuf(const std::vector<std::string> &command,
405 : pipe (NULL) /* brr: shadowing global ident */
410 if (command.empty ()) {
411 PIPE_CTOR_FAIL("command");
414 const boost::shared_array <char *> argv = mk_argv (command);
416 PIPE_CTOR_FAIL("malloc");
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;
424 setg (&buffer, &buffer, &buffer);
427 inpipebuf::inpipebuf(const std::string& command,
428 const bool _ignored_out,
429 const bool _ignored_err,
430 const bool _ignored_path)
435 pipe = popen (command.c_str(), "r");
437 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
440 setg (&buffer, &buffer, &buffer);
443 inpipebuf::~inpipebuf()
451 status = pclose (pipe);
453 if (exit_status != NULL) {
454 *exit_status = status;
455 if (status_set != NULL) {
464 status = fclose (pipe);
466 if (exit_status != NULL) {
467 *exit_status = status; /* might be overwritten below */
468 if (status_set != NULL) {
475 while (waitpid (this->pid, &status, 0) == -1) {
476 if (errno != EINTR) {
481 if (status != 0 && exit_status != NULL) {
482 *exit_status = status; /* might overwrite pipe status above */
483 if (status_set != NULL) {
493 /** note: exit status only available after destruction */
494 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
496 status_set = _status_set;
497 exit_status = _exit_status;
500 inpipebuf::int_type inpipebuf::underflow()
502 if (gptr() < egptr())
503 return traits_type::to_int_type(*gptr());
505 buffer = fgetc (pipe);
512 setg (&buffer, &buffer, &buffer+sizeof(char));
514 return traits_type::to_int_type(*gptr());
517 outpipebuf::outpipebuf(const std::string& command)
522 pipe = popen (command.c_str(), "w");
524 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
527 outpipebuf::~outpipebuf()
530 int pclose_exit = pclose (pipe);
532 if (exit_status && pclose_exit != -1)
536 *exit_status = pclose_exit;
543 /** note: exit status only available after destruction */
544 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
546 status_set = _status_set;
547 exit_status = _exit_status;
550 outpipebuf::int_type outpipebuf::overflow(int_type c)
554 if (fputc(c,pipe)==EOF)
560 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
562 return fwrite(s,num,1,pipe);