bump release version 2.9 -> 2.10
[libi2ncommon] / src / pipestream.cpp
CommitLineData
7e606af5
GE
1 /*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
20/***************************************************************************
21 inpipestream.cpp - C++ streambuffer wrapper
22 -------------------
23 begin : Thu Dec 27 2001
24 copyright : (C) 2001 by Intra2net AG
25 ***************************************************************************/
26
9e322fb7 27#include <errno.h>
7e606af5 28#include <stdio.h>
9e322fb7 29#include <string.h>
f8f119bf 30#include <sys/wait.h>
9e322fb7 31#include <unistd.h>
7e606af5 32
7e606af5
GE
33#include <streambuf>
34#include <istream>
35#include <ostream>
36#include <cstdio>
9e322fb7
PG
37#include <boost/foreach.hpp>
38#include <boost/shared_array.hpp>
7e606af5
GE
39
40#include "exception.hxx"
9e322fb7 41#include "stringfunc.hxx"
7e606af5
GE
42#include "pipestream.hxx"
43
f8f119bf
GE
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
48 */
9e322fb7
PG
49template <typename CmdT>
50std::string capture_exec(CmdT command, ExecResult &rescode)
f8f119bf
GE
51{
52 std::string output;
53
1d7539d5 54 bool exit_set = false;
f8f119bf
GE
55 int exit_status_waitpid;
56
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;
60
61 try
62 {
63 {
64 inpipestream ips(command);
65
66 ips.store_exit_status(&exit_set, &exit_status_waitpid);
67
68 char buffer[2048];
69 while (ips.good())
70 {
71 ips.read(buffer, sizeof(buffer));
72 output.append(buffer, ips.gcount());
73 }
74 }
75
76 // exit_status_waitpid only valid after destruction of the inpipestream
77
78 if (exit_set)
79 {
80 rescode.normal_exit = WIFEXITED(exit_status_waitpid);
81 if (rescode.normal_exit)
82 rescode.return_code = WEXITSTATUS(exit_status_waitpid);
83
84 rescode.terminated_by_signal = WIFSIGNALED(exit_status_waitpid);
85 if (rescode.terminated_by_signal)
86 rescode.signal = WTERMSIG(exit_status_waitpid);
87 }
88 }
89 catch (pipestream_error &e)
90 {
91 rescode.error_message = e.what();
92 }
93
94 return output;
95}
96
9e322fb7
PG
97std::string capture_exec (const std::string &command, ExecResult &res)
98{ return capture_exec<const std::string &>(command, res); }
99
100std::string capture_exec (const char *const *command, ExecResult &res)
101{ return capture_exec<const char *const *>(command, res); }
102
c2c29997
PG
103std::string capture_exec (const std::vector<std::string> &command, ExecResult &res)
104{
105 return capture_exec<const std::vector<std::string> &>(command, res);
106}
107
9e322fb7
PG
108#define PIPE_CTOR_FAIL(where) \
109 do { \
110 throw EXCEPTION (pipestream_error, \
111 std::string (where) + ": error " \
112 + I2n::to_string (errno) \
113 + " (" + std::string (strerror (errno)) + ")"); \
114 } while (0)
115
116static boost::shared_array <char *>
117mk_argv (const std::vector<std::string> &command)
118{
119 char **ret = NULL;
120
121 try {
122 ret = new char *[command.size () * sizeof (ret[0]) + 1];
123 } catch (std::bad_alloc &) {
124 return boost::shared_array<char *> ();
125 }
126
127 size_t cur = 0;
128 BOOST_FOREACH(const std::string &arg, command) {
129 /*
130 * Casting away constness is safe since the data is always
131 * kept alive until after exec().
132 */
133 ret [cur++] = const_cast<char *> (arg.c_str ());
134 }
135
136 ret [cur] = NULL;
137
138 return boost::shared_array<char *> (ret);
139}
140
141
142FILE *
143inpipebuf::init_without_shell (const char *const *argv) const
144{
145 FILE *pipeobj = NULL;
146 int pipefd [2];
147
148 errno = 0;
149 if (::pipe (pipefd) == -1) {
150 PIPE_CTOR_FAIL("pipe");
151 }
152
153 errno = 0;
154 pid_t childpid = fork ();
155 switch (childpid) {
156 case -1: {
157 PIPE_CTOR_FAIL("fork");
158 break;
159 }
160 case 0: {
161 close (pipefd [0]);
162
163 if (dup2 (pipefd[1], STDOUT_FILENO) == -1) {
164 PIPE_CTOR_FAIL("dup2");
165 }
166
167 if (dup2 (pipefd[1], STDERR_FILENO) == -1) {
168 PIPE_CTOR_FAIL("dup2");
169 }
170
171 errno = 0;
172 if (execve (argv [0], const_cast <char *const *>(argv), NULL) == -1) {
173 PIPE_CTOR_FAIL("exec");
174 }
175 break;
176 }
177 default: {
178 close (pipefd [1]);
179
180 errno = 0;
181 if ((pipeobj = fdopen (pipefd [0], "r")) == NULL) {
182 PIPE_CTOR_FAIL("fdopen");
183 }
184 break;
185 }
186 }
187
188 return pipeobj;
189}
190
191inpipebuf::inpipebuf(const char *const *command)
192 : pipe (NULL) /* brr: shadowing global ident */
193 , status_set (NULL)
194 , exit_status (NULL)
195{
196 if (command == NULL || command [0] == NULL) {
197 PIPE_CTOR_FAIL("command");
198 }
199
200 this->pipe = this->init_without_shell (command);
201
202 setg (&buffer, &buffer, &buffer);
203}
204
c2c29997
PG
205inpipebuf::inpipebuf(const std::vector<std::string> &command)
206 : pipe (NULL) /* brr: shadowing global ident */
207 , status_set (NULL)
208 , exit_status (NULL)
209{
210 if (command.empty ()) {
211 PIPE_CTOR_FAIL("command");
212 }
213
214 const boost::shared_array <char *> argv = mk_argv (command);
215 if (!argv) {
216 PIPE_CTOR_FAIL("malloc");
217 }
218
219 this->pipe = this->init_without_shell (argv.get ());
220
221 setg (&buffer, &buffer, &buffer);
222}
223
7e606af5
GE
224inpipebuf::inpipebuf(const std::string& command)
225{
226 status_set = NULL;
227 exit_status = NULL;
228
229 pipe = popen (command.c_str(), "r");
230 if (pipe == NULL)
231 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
232
233 // force underflow
234 setg (&buffer, &buffer, &buffer);
235}
236
237inpipebuf::~inpipebuf()
238{
239 if (pipe != NULL) {
240 int pclose_exit = pclose (pipe);
241
d00589a0
GE
242 if (exit_status && pclose_exit != -1)
243 {
244 if (status_set)
245 *status_set = true;
7e606af5
GE
246 *exit_status = pclose_exit;
247 }
248
249 pipe = NULL;
250 }
251}
252
253/** note: exit status only available after destruction */
254void inpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
255{
256 status_set = _status_set;
257 exit_status = _exit_status;
258}
259
260inpipebuf::int_type inpipebuf::underflow()
261{
262 if (gptr() < egptr())
263 return traits_type::to_int_type(*gptr());
264
265 buffer = fgetc (pipe);
266 if (feof (pipe))
267 {
268 // ERROR or EOF
269 return EOF;
270 }
271
272 setg (&buffer, &buffer, &buffer+sizeof(char));
273
274 return traits_type::to_int_type(*gptr());
275}
276
7e606af5
GE
277outpipebuf::outpipebuf(const std::string& command)
278{
279 status_set = NULL;
280 exit_status = NULL;
281
282 pipe = popen (command.c_str(), "w");
283 if (pipe == NULL)
284 throw EXCEPTION (pipestream_error, "can't open program or permission denied");
285}
286
287outpipebuf::~outpipebuf()
288{
289 if (pipe != NULL) {
290 int pclose_exit = pclose (pipe);
291
d00589a0
GE
292 if (exit_status && pclose_exit != -1)
293 {
294 if (status_set)
295 *status_set = true;
7e606af5
GE
296 *exit_status = pclose_exit;
297 }
298
299 pipe = NULL;
300 }
301}
302
303/** note: exit status only available after destruction */
304void outpipebuf::store_exit_status(bool *_status_set, int *_exit_status)
305{
306 status_set = _status_set;
307 exit_status = _exit_status;
308}
309
310outpipebuf::int_type outpipebuf::overflow(int_type c)
311{
312 if (c != EOF)
313 {
314 if (fputc(c,pipe)==EOF)
315 return EOF;
316 }
317 return c;
318}
319
320std::streamsize outpipebuf::xsputn(const char* s, std::streamsize num)
321{
322 return fwrite(s,num,1,pipe);
323}