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)
54 bool exit_set = false;
55 int exit_status_waitpid;
57 // set the results to false until we are sure we have proper values
58 rescode.normal_exit = false;
59 rescode.terminated_by_signal = false;
64 inpipestream ips(command);
66 ips.store_exit_status(&exit_set, &exit_status_waitpid);
71 ips.read(buffer, sizeof(buffer));
72 output.append(buffer, ips.gcount());
76 // exit_status_waitpid only valid after destruction of the inpipestream
80 rescode.normal_exit = WIFEXITED(exit_status_waitpid);
81 if (rescode.normal_exit)
82 rescode.return_code = WEXITSTATUS(exit_status_waitpid);
84 rescode.terminated_by_signal = WIFSIGNALED(exit_status_waitpid);
85 if (rescode.terminated_by_signal)
86 rescode.signal = WTERMSIG(exit_status_waitpid);
89 catch (pipestream_error &e)
91 rescode.error_message = e.what();
97 std::string capture_exec (const std::string &command, ExecResult &res)
98 { return capture_exec<const std::string &>(command, res); }
100 std::string capture_exec (const char *const *command, ExecResult &res)
101 { return capture_exec<const char *const *>(command, res); }
103 std::string capture_exec (const std::vector<std::string> &command, ExecResult &res)
105 return capture_exec<const std::vector<std::string> &>(command, res);
108 #define PIPE_CTOR_FAIL(where) \
110 throw EXCEPTION (pipestream_error, \
111 std::string (where) + ": error " \
112 + I2n::to_string (errno) \
113 + " (" + std::string (strerror (errno)) + ")"); \
116 static boost::shared_array <char *>
117 mk_argv (const std::vector<std::string> &command)
122 ret = new char *[command.size () * sizeof (ret[0]) + 1];
123 } catch (std::bad_alloc &) {
124 return boost::shared_array<char *> ();
128 BOOST_FOREACH(const std::string &arg, command) {
130 * Casting away constness is safe since the data is always
131 * kept alive until after exec().
133 ret [cur++] = const_cast<char *> (arg.c_str ());
138 return boost::shared_array<char *> (ret);
143 inpipebuf::init_without_shell (const char *const *argv) const
145 FILE *pipeobj = NULL;
149 if (::pipe (pipefd) == -1) {
150 PIPE_CTOR_FAIL("pipe");
154 pid_t childpid = fork ();
157 PIPE_CTOR_FAIL("fork");
163 if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
164 PIPE_CTOR_FAIL("dup2");
167 if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
168 PIPE_CTOR_FAIL("dup2");
172 if (execve (argv [0], const_cast <char *const *>(argv), NULL) == -1) {
173 PIPE_CTOR_FAIL("exec");
181 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
182 PIPE_CTOR_FAIL("fdopen");
191 inpipebuf::inpipebuf(const char *const *command)
192 : pipe (NULL) /* brr: shadowing global ident */
196 if (command == NULL || command [0] == NULL) {
197 PIPE_CTOR_FAIL("command");
200 this->pipe = this->init_without_shell (command);
202 setg (&buffer, &buffer, &buffer);
205 inpipebuf::inpipebuf(const std::vector<std::string> &command)
206 : pipe (NULL) /* brr: shadowing global ident */
210 if (command.empty ()) {
211 PIPE_CTOR_FAIL("command");
214 const boost::shared_array <char *> argv = mk_argv (command);
216 PIPE_CTOR_FAIL("malloc");
219 this->pipe = this->init_without_shell (argv.get ());
221 setg (&buffer, &buffer, &buffer);
224 inpipebuf::inpipebuf(const std::string& command)
229 pipe = popen (command.c_str(), "r");
231 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
234 setg (&buffer, &buffer, &buffer);
237 inpipebuf::~inpipebuf()
240 int pclose_exit = pclose (pipe);
242 if (exit_status && pclose_exit != -1)
246 *exit_status = pclose_exit;
253 /** note: exit status only available after destruction */
254 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
256 status_set = _status_set;
257 exit_status = _exit_status;
260 inpipebuf::int_type inpipebuf::underflow()
262 if (gptr() < egptr())
263 return traits_type::to_int_type(*gptr());
265 buffer = fgetc (pipe);
272 setg (&buffer, &buffer, &buffer+sizeof(char));
274 return traits_type::to_int_type(*gptr());
277 outpipebuf::outpipebuf(const std::string& command)
282 pipe = popen (command.c_str(), "w");
284 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
287 outpipebuf::~outpipebuf()
290 int pclose_exit = pclose (pipe);
292 if (exit_status && pclose_exit != -1)
296 *exit_status = pclose_exit;
303 /** note: exit status only available after destruction */
304 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
306 status_set = _status_set;
307 exit_status = _exit_status;
310 outpipebuf::int_type outpipebuf::overflow(int_type c)
314 if (fputc(c,pipe)==EOF)
320 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
322 return fwrite(s,num,1,pipe);