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