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 std::string str(void) const;
211
212 bool exited(void) const;
213 int exitstatus(void) const;
214
215 bool signaled(void) const;
216 int termsig(void) const;
217 bool coredump(void) const;
218 };
219
220 // ------------------------------------------------------------------------
221 // The "child" type.
222 // ------------------------------------------------------------------------
223
224 class child {
225 pid_t m_pid;
226
227 int m_stdout;
228 int m_stderr;
229
230 bool m_waited;
231
232 template< class OutStream, class ErrStream > friend
233 child fork(void (*)(void*), OutStream, ErrStream, void*);
234
235 child(const pid_t, const int, const int);
236
237 public:
238 ~child(void);
239
240 status wait(void);
241
242 pid_t pid(void) const;
243 int stdout_fd(void);
244 int stderr_fd(void);
245 };
246
247 // ------------------------------------------------------------------------
248 // Free functions.
249 // ------------------------------------------------------------------------
250
251 namespace detail {
252 void flush_streams(void);
253
254 struct exec_args {
255 const tools::fs::path m_prog;
256 const argv_array& m_argv;
257 void (*m_prehook)(void);
258 };
259
260 void do_exec(void *);
261 } // namespace detail
262
263 // TODO: The void* cookie can probably be templatized, thus also allowing
264 // const data structures.
265 template< class OutStream, class ErrStream >
266 child
fork(void (* start)(void *),OutStream outsb,ErrStream errsb,void * v)267 fork(void (*start)(void*), OutStream outsb, ErrStream errsb, void* v)
268 {
269 detail::flush_streams();
270
271 outsb.prepare();
272 errsb.prepare();
273
274 pid_t pid = ::fork();
275 if (pid == -1) {
276 throw system_error("tools::process::child::fork",
277 "Failed to fork", errno);
278 } else if (pid == 0) {
279 try {
280 outsb.connect_child(STDOUT_FILENO);
281 errsb.connect_child(STDERR_FILENO);
282 start(v);
283 std::abort();
284 } catch (...) {
285 std::cerr << "Unhandled error while running subprocess\n";
286 std::exit(EXIT_FAILURE);
287 }
288 } else {
289 const int stdout_fd = outsb.connect_parent();
290 const int stderr_fd = errsb.connect_parent();
291 return child(pid, stdout_fd, stderr_fd);
292 }
293 }
294
295 template< class OutStream, class ErrStream >
296 status
exec(const tools::fs::path & prog,const argv_array & argv,const OutStream & outsb,const ErrStream & errsb,void (* prehook)(void))297 exec(const tools::fs::path& prog, const argv_array& argv,
298 const OutStream& outsb, const ErrStream& errsb,
299 void (*prehook)(void))
300 {
301 struct detail::exec_args ea = { prog, argv, prehook };
302 child c = fork(detail::do_exec, outsb, errsb, &ea);
303
304 again:
305 try {
306 return c.wait();
307 } catch (const system_error& e) {
308 if (e.code() == EINTR)
309 goto again;
310 else
311 throw e;
312 }
313 }
314
315 template< class OutStream, class ErrStream >
316 status
exec(const tools::fs::path & prog,const argv_array & argv,const OutStream & outsb,const ErrStream & errsb)317 exec(const tools::fs::path& prog, const argv_array& argv,
318 const OutStream& outsb, const ErrStream& errsb)
319 {
320 return exec(prog, argv, outsb, errsb, NULL);
321 }
322
323 } // namespace process
324 } // namespace tools
325
326 #endif // !defined(TOOLS_PROCESS_HPP)
327