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 ***************************************************************************/
37 #include <boost/foreach.hpp>
38 #include <boost/shared_array.hpp>
40 #include "exception.hxx"
41 #include "stringfunc.hxx"
42 #include "pipestream.hxx"
44 /** @brief runs command and returns it's output as string
45 * @param command the full command with all parameters
46 * @param rescode struct containing the return code, if the program exited normally and so on
47 * @returns the output (stdout) of the called program
49 template <typename CmdT>
50 std::string capture_exec(CmdT command, ExecResult &rescode,
51 const bool out, const bool err)
55 bool exit_set = false;
56 int exit_status_waitpid;
58 // set the results to false until we are sure we have proper values
59 rescode.normal_exit = false;
60 rescode.terminated_by_signal = false;
65 inpipestream ips(command, out, err);
67 ips.store_exit_status(&exit_set, &exit_status_waitpid);
72 ips.read(buffer, sizeof(buffer));
73 output.append(buffer, ips.gcount());
77 // exit_status_waitpid only valid after destruction of the inpipestream
81 rescode.normal_exit = WIFEXITED(exit_status_waitpid);
82 if (rescode.normal_exit)
83 rescode.return_code = WEXITSTATUS(exit_status_waitpid);
85 rescode.terminated_by_signal = WIFSIGNALED(exit_status_waitpid);
86 if (rescode.terminated_by_signal)
87 rescode.signal = WTERMSIG(exit_status_waitpid);
90 catch (pipestream_error &e)
92 rescode.error_message = e.what();
98 /** @brief Instantiation of \c capture_exec for STL string arguments.
99 * Caveat emptor: this will cause the backing stream to use \c
100 * popen(3). To avoid shelling out, please refer to one of the
101 * variants that allow passing an argument list.
103 * @param command String specifying the shell expression to be executed.
104 * @param res (Out parameter) Store information about the termination
105 * state in this struct.
107 * @returns Result of \c stdout. Note that due to the use of \c
108 * popen, the correct way to collect stderr output as
109 * well is to use shell redirection inside the expression
112 std::string capture_exec (const std::string &command, ExecResult &res)
113 { return capture_exec<const std::string &>(command, res, true, false); }
115 /** @brief Instantiation of \c capture_exec for argument lists. The
116 * pipestream used to run the command will not shell out.
117 * One of \c out or \c err must be set.
119 * @param command List of \c char* specifying the \c argv array of the
120 * command to run. Note that the binary to executed is
121 * assumed to be present at index 0 and that the input
122 * is properly \c NULL terminated.
123 * @param res (Out parameter) Store information about the termination
124 * state in this struct.
125 * @param out Whether to collect \c stdout.
126 * @param err Whether to collect \c stderr; combines with \c out.
128 * @returns Captured output, combined into one string.
130 std::string capture_exec (const char *const *command, ExecResult &res,
131 const bool out, const bool err)
132 { return capture_exec<const char *const *>(command, res, out, err); }
134 /** @brief Instantiation of \c capture_exec for argument lists. The
135 * pipestream used to run the command will not shell out.
136 * One of \c out or \c err must be set.
138 * @param command String vector specifying the \c argv array of the
139 * command to run. Note that the binary to executed is
140 * assumed to be present at index 0.
141 * @param res (Out parameter) Store information about the termination
142 * state in this struct.
143 * @param out Whether to collect \c stdout.
144 * @param err Whether to collect \c stderr; combines with \c out.
146 * @returns Captured output, combined into one string.
148 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
149 const bool out, const bool err)
151 return capture_exec<const std::vector<std::string> &>
152 (command, res, out, err);
155 #define PIPE_CTOR_FAIL(where) \
157 throw EXCEPTION (pipestream_error, \
158 std::string (where) + ": error " \
159 + I2n::to_string (errno) \
160 + " (" + std::string (strerror (errno)) + ")"); \
163 /** @brief Convert a string vector to a refcounted \c char**
164 * that is \c NULL terminated for use with e. g. \c execve(2).
166 * @param command List of arguments including the binary at index 0.
168 * @returns A \c boost::shared_array of pointers to the
169 * arguments plus a trailing \c NULL. Note that
170 * while the array itself is refcounted, the
171 * pointees are assumed owned by the caller and
172 * *not copyied*. I. e. they lose validity if the
173 * original strings are freed.
175 static boost::shared_array <char *>
176 mk_argv (const std::vector<std::string> &command)
181 ret = new char *[command.size () * sizeof (ret[0]) + 1];
182 } catch (std::bad_alloc &) {
183 return boost::shared_array<char *> ();
187 BOOST_FOREACH(const std::string &arg, command) {
189 * Casting away constness is safe since the data is always
190 * kept alive until after exec().
192 ret [cur++] = const_cast<char *> (arg.c_str ());
197 return boost::shared_array<char *> (ret);
201 /** @brief Helper aggregating common code for the shell-free ctors.
203 * @param argv Argument list prepared for \c execve(2).
204 * @param out Whether to capture \c stdout.
205 * @param err Whether to capture \c stderr.
207 * @returns A \c FILE* handle for streaming if successful, \c NULL
210 std::pair <pid_t, FILE *>
211 inpipebuf::init_without_shell (const char *const *argv,
213 const bool err) const
215 FILE *pipeobj = NULL;
220 PIPE_CTOR_FAIL("ctor");
224 if (::pipe (pipefd) == -1) {
225 PIPE_CTOR_FAIL("pipe");
229 pid_t childpid = fork ();
232 PIPE_CTOR_FAIL("fork");
239 close (STDOUT_FILENO);
240 } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
241 PIPE_CTOR_FAIL("dup2/stdout");
245 close (STDERR_FILENO);
246 } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
247 PIPE_CTOR_FAIL("dup2/stderr");
253 if (execve (argv [0], const_cast <char *const *>(argv), NULL) == -1) {
254 PIPE_CTOR_FAIL("exec");
262 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
263 PIPE_CTOR_FAIL("fdopen");
269 return std::make_pair (childpid, pipeobj);
272 inpipebuf::inpipebuf(const char *const *command,
275 : pipe (NULL) /* brr: shadowing global ident */
280 if (command == NULL || command [0] == NULL) {
281 PIPE_CTOR_FAIL("command");
284 std::pair <pid_t, FILE *> tmp = this->init_without_shell (command, out, err);
285 this->pid = tmp.first; /* no std::tie :/ */
286 this->pipe = tmp.second;
288 setg (&buffer, &buffer, &buffer);
291 inpipebuf::inpipebuf(const std::vector<std::string> &command,
294 : pipe (NULL) /* brr: shadowing global ident */
299 if (command.empty ()) {
300 PIPE_CTOR_FAIL("command");
303 const boost::shared_array <char *> argv = mk_argv (command);
305 PIPE_CTOR_FAIL("malloc");
308 std::pair <pid_t, FILE *> tmp = this->init_without_shell (argv.get (), out, err);
309 this->pid = tmp.first;
310 this->pipe = tmp.second;
312 setg (&buffer, &buffer, &buffer);
315 inpipebuf::inpipebuf(const std::string& command,
316 const bool _ignored_out,
317 const bool _ignored_err)
322 pipe = popen (command.c_str(), "r");
324 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
327 setg (&buffer, &buffer, &buffer);
330 inpipebuf::~inpipebuf()
338 status = pclose (pipe);
340 if (exit_status != NULL) {
341 *exit_status = status;
342 if (status_set != NULL) {
351 status = fclose (pipe);
353 if (exit_status != NULL) {
354 *exit_status = status; /* might be overwritten below */
355 if (status_set != NULL) {
362 while (waitpid (this->pid, &status, 0) == -1) {
363 if (errno != EINTR) {
368 if (status != 0 && exit_status != NULL) {
369 *exit_status = status; /* might overwrite pipe status above */
370 if (status_set != NULL) {
380 /** note: exit status only available after destruction */
381 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
383 status_set = _status_set;
384 exit_status = _exit_status;
387 inpipebuf::int_type inpipebuf::underflow()
389 if (gptr() < egptr())
390 return traits_type::to_int_type(*gptr());
392 buffer = fgetc (pipe);
399 setg (&buffer, &buffer, &buffer+sizeof(char));
401 return traits_type::to_int_type(*gptr());
404 outpipebuf::outpipebuf(const std::string& command)
409 pipe = popen (command.c_str(), "w");
411 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
414 outpipebuf::~outpipebuf()
417 int pclose_exit = pclose (pipe);
419 if (exit_status && pclose_exit != -1)
423 *exit_status = pclose_exit;
430 /** note: exit status only available after destruction */
431 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
433 status_set = _status_set;
434 exit_status = _exit_status;
437 outpipebuf::int_type outpipebuf::overflow(int_type c)
441 if (fputc(c,pipe)==EOF)
447 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
449 return fwrite(s,num,1,pipe);