xref: /minix/external/bsd/atf/dist/tools/process.hpp (revision 0a6a1f1d)
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