1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2008 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30 #if !defined(TOOLS_PROCESS_HPP)
31 #define TOOLS_PROCESS_HPP
32
33 extern "C" {
34 #include <sys/types.h>
35
36 #include <unistd.h>
37 }
38
39 #include <cerrno>
40 #include <cstdlib>
41 #include <iostream>
42 #include <string>
43 #include <vector>
44
45 #include "auto_array.hpp"
46 #include "exceptions.hpp"
47 #include "fs.hpp"
48
49 namespace tools {
50 namespace process {
51
52 class child;
53 class status;
54
55 // ------------------------------------------------------------------------
56 // The "argv_array" type.
57 // ------------------------------------------------------------------------
58
59 class argv_array {
60 typedef std::vector< std::string > args_vector;
61 args_vector m_args;
62
63 // TODO: This is immutable, so we should be able to use
64 // std::tr1::shared_array instead when it becomes widely available.
65 // The reason would be to remove all copy constructors and assignment
66 // operators from this class.
67 auto_array< const char* > m_exec_argv;
68 void ctor_init_exec_argv(void);
69
70 public:
71 typedef args_vector::const_iterator const_iterator;
72 typedef args_vector::size_type size_type;
73
74 argv_array(void);
75 argv_array(const char*, ...);
76 explicit argv_array(const char* const*);
77 template< class C > explicit argv_array(const C&);
78 argv_array(const argv_array&);
79
80 const char* const* exec_argv(void) const;
81 size_type size(void) const;
82 const char* operator[](int) const;
83
84 const_iterator begin(void) const;
85 const_iterator end(void) const;
86
87 argv_array& operator=(const argv_array&);
88 };
89
90 template< class C >
argv_array(const C & c)91 argv_array::argv_array(const C& c)
92 {
93 for (typename C::const_iterator iter = c.begin(); iter != c.end();
94 iter++)
95 m_args.push_back(*iter);
96 ctor_init_exec_argv();
97 }
98
99 // ------------------------------------------------------------------------
100 // The "stream" types.
101 // ------------------------------------------------------------------------
102
103 class stream_capture {
104 int m_pipefds[2];
105
106 // Allow access to the getters.
107 template< class OutStream, class ErrStream > friend
108 child fork(void (*)(void*), OutStream, ErrStream, void*);
109 template< class OutStream, class ErrStream > friend
110 status exec(const tools::fs::path&, const argv_array&,
111 const OutStream&, const ErrStream&, void (*)(void));
112
113 void prepare(void);
114 int connect_parent(void);
115 void connect_child(const int);
116
117 public:
118 stream_capture(void);
119 ~stream_capture(void);
120 };
121
122 class stream_connect {
123 int m_src_fd;
124 int m_tgt_fd;
125
126 // Allow access to the getters.
127 template< class OutStream, class ErrStream > friend
128 child fork(void (*)(void*), OutStream, ErrStream, void*);
129 template< class OutStream, class ErrStream > friend
130 status exec(const tools::fs::path&, const argv_array&,
131 const OutStream&, const ErrStream&, void (*)(void));
132
133 void prepare(void);
134 int connect_parent(void);
135 void connect_child(const int);
136
137 public:
138 stream_connect(const int, const int);
139 };
140
141 class stream_inherit {
142 // Allow access to the getters.
143 template< class OutStream, class ErrStream > friend
144 child fork(void (*)(void*), OutStream, ErrStream, void*);
145 template< class OutStream, class ErrStream > friend
146 status exec(const tools::fs::path&, const argv_array&,
147 const OutStream&, const ErrStream&, void (*)(void));
148
149 void prepare(void);
150 int connect_parent(void);
151 void connect_child(const int);
152
153 public:
154 stream_inherit(void);
155 };
156
157 class stream_redirect_fd {
158 int m_fd;
159
160 // Allow access to the getters.
161 template< class OutStream, class ErrStream > friend
162 child fork(void (*)(void*), OutStream, ErrStream, void*);
163 template< class OutStream, class ErrStream > friend
164 status exec(const tools::fs::path&, const argv_array&,
165 const OutStream&, const ErrStream&, void (*)(void));
166
167 void prepare(void);
168 int connect_parent(void);
169 void connect_child(const int);
170
171 public:
172 stream_redirect_fd(const int);
173 };
174
175 class stream_redirect_path {
176 const tools::fs::path m_path;
177
178 // Allow access to the getters.
179 template< class OutStream, class ErrStream > friend
180 child fork(void (*)(void*), OutStream, ErrStream, void*);
181 template< class OutStream, class ErrStream > friend
182 status exec(const tools::fs::path&, const argv_array&,
183 const OutStream&, const ErrStream&, void (*)(void));
184
185 void prepare(void);
186 int connect_parent(void);
187 void connect_child(const int);
188
189 public:
190 stream_redirect_path(const tools::fs::path&);
191 };
192
193 // ------------------------------------------------------------------------
194 // The "status" type.
195 // ------------------------------------------------------------------------
196
197 class status {
198 int m_status;
199
200 friend class child;
201 template< class OutStream, class ErrStream > friend
202 status exec(const tools::fs::path&, const argv_array&,
203 const OutStream&, const ErrStream&, void (*)(void));
204
205 status(int);
206
207 public:
208 ~status(void);
209
210 bool exited(void) const;
211 int exitstatus(void) const;
212
213 bool signaled(void) const;
214 int termsig(void) const;
215 bool coredump(void) const;
216 };
217
218 // ------------------------------------------------------------------------
219 // The "child" type.
220 // ------------------------------------------------------------------------
221
222 class child {
223 pid_t m_pid;
224
225 int m_stdout;
226 int m_stderr;
227
228 bool m_waited;
229
230 template< class OutStream, class ErrStream > friend
231 child fork(void (*)(void*), OutStream, ErrStream, void*);
232
233 child(const pid_t, const int, const int);
234
235 public:
236 ~child(void);
237
238 status wait(void);
239
240 pid_t pid(void) const;
241 int stdout_fd(void);
242 int stderr_fd(void);
243 };
244
245 // ------------------------------------------------------------------------
246 // Free functions.
247 // ------------------------------------------------------------------------
248
249 namespace detail {
250 void flush_streams(void);
251
252 struct exec_args {
253 const tools::fs::path m_prog;
254 const argv_array& m_argv;
255 void (*m_prehook)(void);
256 };
257
258 void do_exec(void *);
259 } // namespace detail
260
261 // TODO: The void* cookie can probably be templatized, thus also allowing
262 // const data structures.
263 template< class OutStream, class ErrStream >
264 child
fork(void (* start)(void *),OutStream outsb,ErrStream errsb,void * v)265 fork(void (*start)(void*), OutStream outsb, ErrStream errsb, void* v)
266 {
267 detail::flush_streams();
268
269 outsb.prepare();
270 errsb.prepare();
271
272 pid_t pid = ::fork();
273 if (pid == -1) {
274 throw system_error("tools::process::child::fork",
275 "Failed to fork", errno);
276 } else if (pid == 0) {
277 try {
278 outsb.connect_child(STDOUT_FILENO);
279 errsb.connect_child(STDERR_FILENO);
280 start(v);
281 std::abort();
282 } catch (...) {
283 std::cerr << "Unhandled error while running subprocess\n";
284 std::exit(EXIT_FAILURE);
285 }
286 } else {
287 const int stdout_fd = outsb.connect_parent();
288 const int stderr_fd = errsb.connect_parent();
289 return child(pid, stdout_fd, stderr_fd);
290 }
291 }
292
293 template< class OutStream, class ErrStream >
294 status
exec(const tools::fs::path & prog,const argv_array & argv,const OutStream & outsb,const ErrStream & errsb,void (* prehook)(void))295 exec(const tools::fs::path& prog, const argv_array& argv,
296 const OutStream& outsb, const ErrStream& errsb,
297 void (*prehook)(void))
298 {
299 struct detail::exec_args ea = { prog, argv, prehook };
300 child c = fork(detail::do_exec, outsb, errsb, &ea);
301
302 again:
303 try {
304 return c.wait();
305 } catch (const system_error& e) {
306 if (e.code() == EINTR)
307 goto again;
308 else
309 throw e;
310 }
311 }
312
313 template< class OutStream, class ErrStream >
314 status
exec(const tools::fs::path & prog,const argv_array & argv,const OutStream & outsb,const ErrStream & errsb)315 exec(const tools::fs::path& prog, const argv_array& argv,
316 const OutStream& outsb, const ErrStream& errsb)
317 {
318 return exec(prog, argv, outsb, errsb, NULL);
319 }
320
321 } // namespace process
322 } // namespace tools
323
324 #endif // !defined(TOOLS_PROCESS_HPP)
325