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 * @param out Whether to collect \c stdout.
48 * @param err Whether to collect \c stderr; combines with \c out.
49 * @param path Wether to look up the executable in \c $PATH.
50 * @returns the output (stdout) of the called program
52 template <typename CmdT>
53 std::string capture_exec(CmdT command, ExecResult &rescode,
54 const bool out, const bool err,
59 bool exit_set = false;
60 int exit_status_waitpid;
62 // set the results to false until we are sure we have proper values
63 rescode.normal_exit = false;
64 rescode.terminated_by_signal = false;
69 inpipestream ips(command, out, err, path);
71 ips.store_exit_status(&exit_set, &exit_status_waitpid);
76 ips.read(buffer, sizeof(buffer));
77 output.append(buffer, ips.gcount());
81 // exit_status_waitpid only valid after destruction of the inpipestream
85 rescode.normal_exit = WIFEXITED(exit_status_waitpid);
86 if (rescode.normal_exit)
87 rescode.return_code = WEXITSTATUS(exit_status_waitpid);
89 rescode.terminated_by_signal = WIFSIGNALED(exit_status_waitpid);
90 if (rescode.terminated_by_signal)
91 rescode.signal = WTERMSIG(exit_status_waitpid);
94 catch (pipestream_error &e)
96 rescode.error_message = e.what();
102 /** @brief Instantiation of \c capture_exec for STL string arguments.
103 * Caveat emptor: this will cause the backing stream to use \c
104 * popen(3). To avoid shelling out, please refer to one of the
105 * variants that allow passing an argument list.
107 * @param command String specifying the shell expression to be executed.
108 * @param res (Out parameter) Store information about the termination
109 * state in this struct.
111 * @returns Result of \c stdout. Note that due to the use of \c
112 * popen, the correct way to collect stderr output as
113 * well is to use shell redirection inside the expression
116 std::string capture_exec (const std::string &command, ExecResult &res)
117 { return capture_exec<const std::string &>(command, res, true, false, false); }
119 /** @brief Instantiation of \c capture_exec for argument lists. The
120 * pipestream used to run the command will not shell out.
121 * One of \c out or \c err must be set.
123 * @param command List of \c char* specifying the \c argv array of the
124 * command to run. Note that the binary to executed is
125 * assumed to be present at index 0 and that the input
126 * is properly \c NULL terminated.
127 * @param res (Out parameter) Store information about the termination
128 * state in this struct.
129 * @param out Whether to collect \c stdout.
130 * @param err Whether to collect \c stderr; combines with \c out.
131 * @param path Wether to look up the executable in \c $PATH.
133 * @returns Captured output, combined into one string.
135 std::string capture_exec (const char *const *command, ExecResult &res,
136 const bool out, const bool err, const bool path)
137 { return capture_exec<const char *const *>(command, res, out, err, path); }
139 /** @brief Instantiation of \c capture_exec for argument lists. The
140 * pipestream used to run the command will not shell out.
141 * One of \c out or \c err must be set.
143 * @param command String vector specifying the \c argv array of the
144 * command to run. Note that the binary to executed is
145 * assumed to be present at index 0.
146 * @param res (Out parameter) Store information about the termination
147 * state in this struct.
148 * @param out Whether to collect \c stdout.
149 * @param err Whether to collect \c stderr; combines with \c out.
150 * @param path Wether to look up the executable in \c $PATH.
152 * @returns Captured output, combined into one string.
154 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res,
155 const bool out, const bool err, const bool path)
157 return capture_exec<const std::vector<std::string> &>
158 (command, res, out, err, path);
161 #define PIPE_CTOR_FAIL(where) \
163 throw EXCEPTION (pipestream_error, \
164 std::string (where) + ": error " \
165 + I2n::to_string (errno) \
166 + " (" + std::string (strerror (errno)) + ")"); \
169 /** @brief Convert a string vector to a refcounted \c char**
170 * that is \c NULL terminated for use with e. g. \c execve(2).
172 * @param command List of arguments including the binary at index 0.
174 * @returns A \c boost::shared_array of pointers to the
175 * arguments plus a trailing \c NULL. Note that
176 * while the array itself is refcounted, the
177 * pointees are assumed owned by the caller and
178 * *not copyied*. I. e. they lose validity if the
179 * original strings are freed.
181 static boost::shared_array <char *>
182 mk_argv (const std::vector<std::string> &command)
187 ret = new char *[command.size () * sizeof (ret[0]) + 1];
188 } catch (std::bad_alloc &) {
189 return boost::shared_array<char *> ();
193 BOOST_FOREACH(const std::string &arg, command) {
195 * Casting away constness is safe since the data is always
196 * kept alive until after exec().
198 ret [cur++] = const_cast<char *> (arg.c_str ());
203 return boost::shared_array<char *> (ret);
206 /** @brief Helper aggregating common code for the shell-free ctors.
208 * @param argv Argument list prepared for \c execve(2).
209 * @param out Whether to capture \c stdout.
210 * @param err Whether to capture \c stderr.
212 * @returns A \c FILE* handle for streaming if successful, \c NULL
215 std::pair <pid_t, FILE *>
216 inpipebuf::init_without_shell (const char *const *argv,
219 const bool path) const
221 FILE *pipeobj = NULL;
226 PIPE_CTOR_FAIL("ctor");
230 if (::pipe (pipefd) == -1) {
231 PIPE_CTOR_FAIL("pipe");
235 pid_t childpid = fork ();
238 PIPE_CTOR_FAIL("fork");
245 close (STDOUT_FILENO);
246 } else if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
247 fprintf(stderr, "dup2/stdout: %m\n");
252 close (STDERR_FILENO);
253 } else if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
254 fprintf(stderr, "dup2/stderr: %m\n");
262 execvpe (argv [0], const_cast <char *const *>(argv), environ);
264 execve (argv [0], const_cast <char *const *>(argv), NULL);
273 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
274 PIPE_CTOR_FAIL("fdopen");
280 return std::make_pair (childpid, pipeobj);
283 inpipebuf::inpipebuf(const char *const *command,
287 : pipe (NULL) /* brr: shadowing global ident */
292 if (command == NULL || command [0] == NULL) {
293 PIPE_CTOR_FAIL("command");
296 std::pair <pid_t, FILE *> tmp =
297 this->init_without_shell (command, out, err, path);
298 this->pid = tmp.first; /* no std::tie :/ */
299 this->pipe = tmp.second;
301 setg (&buffer, &buffer, &buffer);
304 inpipebuf::inpipebuf(const std::vector<std::string> &command,
308 : pipe (NULL) /* brr: shadowing global ident */
313 if (command.empty ()) {
314 PIPE_CTOR_FAIL("command");
317 const boost::shared_array <char *> argv = mk_argv (command);
319 PIPE_CTOR_FAIL("malloc");
322 std::pair <pid_t, FILE *> tmp =
323 this->init_without_shell (argv.get (), out, err, path);
324 this->pid = tmp.first;
325 this->pipe = tmp.second;
327 setg (&buffer, &buffer, &buffer);
330 inpipebuf::inpipebuf(const std::string& command,
331 const bool _ignored_out,
332 const bool _ignored_err,
333 const bool _ignored_path)
338 pipe = popen (command.c_str(), "r");
340 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
343 setg (&buffer, &buffer, &buffer);
346 inpipebuf::~inpipebuf()
354 status = pclose (pipe);
356 if (exit_status != NULL) {
357 *exit_status = status;
358 if (status_set != NULL) {
367 status = fclose (pipe);
369 if (exit_status != NULL) {
370 *exit_status = status; /* might be overwritten below */
371 if (status_set != NULL) {
378 while (waitpid (this->pid, &status, 0) == -1) {
379 if (errno != EINTR) {
384 if (status != 0 && exit_status != NULL) {
385 *exit_status = status; /* might overwrite pipe status above */
386 if (status_set != NULL) {
396 /** note: exit status only available after destruction */
397 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
399 status_set = _status_set;
400 exit_status = _exit_status;
403 inpipebuf::int_type inpipebuf::underflow()
405 if (gptr() < egptr())
406 return traits_type::to_int_type(*gptr());
408 buffer = fgetc (pipe);
415 setg (&buffer, &buffer, &buffer+sizeof(char));
417 return traits_type::to_int_type(*gptr());
420 outpipebuf::outpipebuf(const std::string& command)
425 pipe = popen (command.c_str(), "w");
427 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
430 outpipebuf::~outpipebuf()
433 int pclose_exit = pclose (pipe);
435 if (exit_status && pclose_exit != -1)
439 *exit_status = pclose_exit;
446 /** note: exit status only available after destruction */
447 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
449 status_set = _status_set;
450 exit_status = _exit_status;
453 outpipebuf::int_type outpipebuf::overflow(int_type c)
457 if (fputc(c,pipe)==EOF)
463 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
465 return fwrite(s,num,1,pipe);