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,
56 const bool path, const bool env)
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, env);
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, 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,
139 { return capture_exec<const char *const *>(command, res, out, err, path, env); }
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.
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.
154 * @returns Captured output, combined into one string.
156 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
157 const bool out, const bool err, const bool path,
160 return capture_exec<const std::vector<std::string> &>
161 (command, res, out, err, path, env);
164 #define PIPE_CTOR_FAIL(where) \
166 throw EXCEPTION (pipestream_error, \
167 std::string (where) + ": error " \
168 + I2n::to_string (errno) \
169 + " (" + std::string (strerror (errno)) + ")"); \
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).
175 * @param command List of arguments including the binary at index 0.
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.
184 static boost::shared_array <char *>
185 mk_argv (const std::vector<std::string> &command)
190 ret = new char *[command.size () * sizeof (ret[0]) + 1];
191 } catch (std::bad_alloc &) {
192 return boost::shared_array<char *> ();
196 BOOST_FOREACH(const std::string &arg, command) {
198 * Casting away constness is safe since the data is always
199 * kept alive until after exec().
201 ret [cur++] = const_cast<char *> (arg.c_str ());
206 return boost::shared_array<char *> (ret);
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
214 * @param fd The open file descriptor to operate on.
215 * @param save_errno Out parameter: stores errno here after a syscall failure.
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.
222 redirect_devnull (const int fd, int &save_errno)
224 static int nullfd = -1;
227 if (nullfd == -1 && (nullfd = open ("/dev/null", O_RDWR)) == -1) {
233 if (dup2 (nullfd, fd) == -1) {
241 /** @brief Helper aggregating common code for the shell-free ctors.
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.
247 * @returns A \c FILE* handle for streaming if successful, \c NULL
250 std::pair <pid_t, FILE *>
251 inpipebuf::init_without_shell (const char *const *argv,
255 const bool env) const
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;
265 PIPE_CTOR_FAIL("ctor");
269 if ( ::pipe2 (pipefd, O_CLOEXEC) == -1
270 || ::pipe2 (errfd , O_CLOEXEC) == -1) {
271 PIPE_CTOR_FAIL("pipe2");
274 sigfillset (&newmask);
275 sigprocmask (SIG_SETMASK, &newmask, &oldmask);
278 pid_t childpid = fork ();
281 sigprocmask (SIG_SETMASK, &oldmask, NULL);
282 PIPE_CTOR_FAIL("fork");
289 fcntl (pipefd [1], F_SETFD, 0);
293 if (!redirect_devnull (STDOUT_FILENO, save_errno)) {
294 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
297 } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
298 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
303 if (!redirect_devnull (STDERR_FILENO, save_errno)) {
304 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
307 } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
308 (void)write (errfd [1], (char *)&save_errno, sizeof(save_errno));
314 sigprocmask (SIG_SETMASK, &oldmask, NULL);
318 execvpe (argv [0], const_cast <char *const *>(argv), envp);
320 execve (argv [0], const_cast <char *const *>(argv), envp);
323 (void)write (errfd [1], (char *)&errno, sizeof(errno));
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.
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.
345 char buf [sizeof (errno)];
347 memset (buf, 0, sizeof (buf));
349 if ((ret = read (errfd [0], buf, sizeof (buf))) != 0) {
352 sigprocmask (SIG_SETMASK, &oldmask, NULL);
355 PIPE_CTOR_FAIL("read");
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.
362 errno = *((int *)&buf[0]);
363 PIPE_CTOR_FAIL("child failed to exec()");
368 * read(2) yielded zero bytes; it’s safe to use the pipe so close our end
373 sigprocmask (SIG_SETMASK, &oldmask, NULL);
376 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
378 PIPE_CTOR_FAIL("fdopen");
381 return std::make_pair (childpid, pipeobj);
384 inpipebuf::inpipebuf(const char *const *command,
389 : pipe (NULL) /* brr: shadowing global ident */
394 if (command == NULL || command [0] == NULL) {
395 PIPE_CTOR_FAIL("command");
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;
403 setg (&buffer, &buffer, &buffer);
406 inpipebuf::inpipebuf(const std::vector<std::string> &command,
411 : pipe (NULL) /* brr: shadowing global ident */
416 if (command.empty ()) {
417 PIPE_CTOR_FAIL("command");
420 const boost::shared_array <char *> argv = mk_argv (command);
422 PIPE_CTOR_FAIL("malloc");
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;
430 setg (&buffer, &buffer, &buffer);
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)
442 pipe = popen (command.c_str(), "r");
444 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
447 setg (&buffer, &buffer, &buffer);
450 inpipebuf::~inpipebuf()
458 status = pclose (pipe);
460 if (exit_status != NULL) {
461 *exit_status = status;
462 if (status_set != NULL) {
471 status = fclose (pipe);
473 if (exit_status != NULL) {
474 *exit_status = status; /* might be overwritten below */
475 if (status_set != NULL) {
482 while (waitpid (this->pid, &status, 0) == -1) {
483 if (errno != EINTR) {
488 if (status != 0 && exit_status != NULL) {
489 *exit_status = status; /* might overwrite pipe status above */
490 if (status_set != NULL) {
500 /** note: exit status only available after destruction */
501 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
503 status_set = _status_set;
504 exit_status = _exit_status;
507 inpipebuf::int_type inpipebuf::underflow()
509 if (gptr() < egptr())
510 return traits_type::to_int_type(*gptr());
512 buffer = fgetc (pipe);
519 setg (&buffer, &buffer, &buffer+sizeof(char));
521 return traits_type::to_int_type(*gptr());
524 outpipebuf::outpipebuf(const std::string& command)
529 pipe = popen (command.c_str(), "w");
531 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
534 outpipebuf::~outpipebuf()
537 int pclose_exit = pclose (pipe);
539 if (exit_status && pclose_exit != -1)
543 *exit_status = pclose_exit;
550 /** note: exit status only available after destruction */
551 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
553 status_set = _status_set;
554 exit_status = _exit_status;
557 outpipebuf::int_type outpipebuf::overflow(int_type c)
561 if (fputc(c,pipe)==EOF)
567 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
569 return fwrite(s,num,1,pipe);