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