xref: /netbsd/external/bsd/atf/dist/tools/process.hpp (revision 6660df14)
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