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 #define PIPE_CTOR_FAIL(where) \
105 throw EXCEPTION (pipestream_error, \
106 std::string (where) + ": error " \
107 + I2n::to_string (errno) \
108 + " (" + std::string (strerror (errno)) + ")"); \
111 static boost::shared_array <char *>
112 mk_argv (const std::vector<std::string> &command)
117 ret = new char *[command.size () * sizeof (ret[0]) + 1];
118 } catch (std::bad_alloc &) {
119 return boost::shared_array<char *> ();
123 BOOST_FOREACH(const std::string &arg, command) {
125 * Casting away constness is safe since the data is always
126 * kept alive until after exec().
128 ret [cur++] = const_cast<char *> (arg.c_str ());
133 return boost::shared_array<char *> (ret);
138 inpipebuf::init_without_shell (const char *const *argv) const
140 FILE *pipeobj = NULL;
144 if (::pipe (pipefd) == -1) {
145 PIPE_CTOR_FAIL("pipe");
149 pid_t childpid = fork ();
152 PIPE_CTOR_FAIL("fork");
158 if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
159 PIPE_CTOR_FAIL("dup2");
162 if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
163 PIPE_CTOR_FAIL("dup2");
167 if (execve (argv [0], const_cast <char *const *>(argv), NULL) == -1) {
168 PIPE_CTOR_FAIL("exec");
176 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
177 PIPE_CTOR_FAIL("fdopen");
186 inpipebuf::inpipebuf(const char *const *command)
187 : pipe (NULL) /* brr: shadowing global ident */
191 if (command == NULL || command [0] == NULL) {
192 PIPE_CTOR_FAIL("command");
195 this->pipe = this->init_without_shell (command);
197 setg (&buffer, &buffer, &buffer);
200 inpipebuf::inpipebuf(const std::string& command)
205 pipe = popen (command.c_str(), "r");
207 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
210 setg (&buffer, &buffer, &buffer);
213 inpipebuf::~inpipebuf()
216 int pclose_exit = pclose (pipe);
218 if (exit_status && pclose_exit != -1)
222 *exit_status = pclose_exit;
229 /** note: exit status only available after destruction */
230 void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
232 status_set = _status_set;
233 exit_status = _exit_status;
236 inpipebuf::int_type inpipebuf::underflow()
238 if (gptr() < egptr())
239 return traits_type::to_int_type(*gptr());
241 buffer = fgetc (pipe);
248 setg (&buffer, &buffer, &buffer+sizeof(char));
250 return traits_type::to_int_type(*gptr());
253 outpipebuf::outpipebuf(const std::string& command)
258 pipe = popen (command.c_str(), "w");
260 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
263 outpipebuf::~outpipebuf()
266 int pclose_exit = pclose (pipe);
268 if (exit_status && pclose_exit != -1)
272 *exit_status = pclose_exit;
279 /** note: exit status only available after destruction */
280 void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
282 status_set = _status_set;
283 exit_status = _exit_status;
286 outpipebuf::int_type outpipebuf::overflow(int_type c)
290 if (fputc(c,pipe)==EOF)
296 std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
298 return fwrite(s,num,1,pipe);