xref: /freebsd/contrib/atf/atf-c++/detail/process.cpp (revision 87c1627502a5dde91e5284118eec8682b60f27a2)
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 extern "C" {
31 #include <signal.h>
32 
33 #include "../../atf-c/error.h"
34 
35 #include "../../atf-c/detail/process.h"
36 }
37 
38 #include <iostream>
39 
40 #include "exceptions.hpp"
41 #include "process.hpp"
42 #include "sanity.hpp"
43 
44 namespace detail = atf::process::detail;
45 namespace impl = atf::process;
46 #define IMPL_NAME "atf::process"
47 
48 // ------------------------------------------------------------------------
49 // Auxiliary functions.
50 // ------------------------------------------------------------------------
51 
52 template< class C >
53 atf::utils::auto_array< const char* >
54 collection_to_argv(const C& c)
55 {
56     atf::utils::auto_array< const char* > argv(new const char*[c.size() + 1]);
57 
58     std::size_t pos = 0;
59     for (typename C::const_iterator iter = c.begin(); iter != c.end();
60          iter++) {
61         argv[pos] = (*iter).c_str();
62         pos++;
63     }
64     INV(pos == c.size());
65     argv[pos] = NULL;
66 
67     return argv;
68 }
69 
70 template< class C >
71 C
72 argv_to_collection(const char* const* argv)
73 {
74     C c;
75 
76     for (const char* const* iter = argv; *iter != NULL; iter++)
77         c.push_back(std::string(*iter));
78 
79     return c;
80 }
81 
82 // ------------------------------------------------------------------------
83 // The "argv_array" type.
84 // ------------------------------------------------------------------------
85 
86 impl::argv_array::argv_array(void) :
87     m_exec_argv(collection_to_argv(m_args))
88 {
89 }
90 
91 impl::argv_array::argv_array(const char* arg1, ...)
92 {
93     m_args.push_back(arg1);
94 
95     {
96         va_list ap;
97         const char* nextarg;
98 
99         va_start(ap, arg1);
100         while ((nextarg = va_arg(ap, const char*)) != NULL)
101             m_args.push_back(nextarg);
102         va_end(ap);
103     }
104 
105     ctor_init_exec_argv();
106 }
107 
108 impl::argv_array::argv_array(const char* const* ca) :
109     m_args(argv_to_collection< args_vector >(ca)),
110     m_exec_argv(collection_to_argv(m_args))
111 {
112 }
113 
114 impl::argv_array::argv_array(const argv_array& a) :
115     m_args(a.m_args),
116     m_exec_argv(collection_to_argv(m_args))
117 {
118 }
119 
120 void
121 impl::argv_array::ctor_init_exec_argv(void)
122 {
123     m_exec_argv = collection_to_argv(m_args);
124 }
125 
126 const char* const*
127 impl::argv_array::exec_argv(void)
128     const
129 {
130     return m_exec_argv.get();
131 }
132 
133 impl::argv_array::size_type
134 impl::argv_array::size(void)
135     const
136 {
137     return m_args.size();
138 }
139 
140 const char*
141 impl::argv_array::operator[](int idx)
142     const
143 {
144     return m_args[idx].c_str();
145 }
146 
147 impl::argv_array::const_iterator
148 impl::argv_array::begin(void)
149     const
150 {
151     return m_args.begin();
152 }
153 
154 impl::argv_array::const_iterator
155 impl::argv_array::end(void)
156     const
157 {
158     return m_args.end();
159 }
160 
161 impl::argv_array&
162 impl::argv_array::operator=(const argv_array& a)
163 {
164     if (this != &a) {
165         m_args = a.m_args;
166         m_exec_argv = collection_to_argv(m_args);
167     }
168     return *this;
169 }
170 
171 // ------------------------------------------------------------------------
172 // The "stream" types.
173 // ------------------------------------------------------------------------
174 
175 impl::basic_stream::basic_stream(void) :
176     m_inited(false)
177 {
178 }
179 
180 impl::basic_stream::~basic_stream(void)
181 {
182     if (m_inited)
183         atf_process_stream_fini(&m_sb);
184 }
185 
186 const atf_process_stream_t*
187 impl::basic_stream::get_sb(void)
188     const
189 {
190     INV(m_inited);
191     return &m_sb;
192 }
193 
194 impl::stream_capture::stream_capture(void)
195 {
196     atf_error_t err = atf_process_stream_init_capture(&m_sb);
197     if (atf_is_error(err))
198         throw_atf_error(err);
199     m_inited = true;
200 }
201 
202 impl::stream_connect::stream_connect(const int src_fd, const int tgt_fd)
203 {
204     atf_error_t err = atf_process_stream_init_connect(&m_sb, src_fd, tgt_fd);
205     if (atf_is_error(err))
206         throw_atf_error(err);
207     m_inited = true;
208 }
209 
210 impl::stream_inherit::stream_inherit(void)
211 {
212     atf_error_t err = atf_process_stream_init_inherit(&m_sb);
213     if (atf_is_error(err))
214         throw_atf_error(err);
215     m_inited = true;
216 }
217 
218 impl::stream_redirect_fd::stream_redirect_fd(const int fd)
219 {
220     atf_error_t err = atf_process_stream_init_redirect_fd(&m_sb, fd);
221     if (atf_is_error(err))
222         throw_atf_error(err);
223     m_inited = true;
224 }
225 
226 impl::stream_redirect_path::stream_redirect_path(const fs::path& p)
227 {
228     atf_error_t err = atf_process_stream_init_redirect_path(&m_sb, p.c_path());
229     if (atf_is_error(err))
230         throw_atf_error(err);
231     m_inited = true;
232 }
233 
234 // ------------------------------------------------------------------------
235 // The "status" type.
236 // ------------------------------------------------------------------------
237 
238 impl::status::status(atf_process_status_t& s) :
239     m_status(s)
240 {
241 }
242 
243 impl::status::~status(void)
244 {
245     atf_process_status_fini(&m_status);
246 }
247 
248 bool
249 impl::status::exited(void)
250     const
251 {
252     return atf_process_status_exited(&m_status);
253 }
254 
255 int
256 impl::status::exitstatus(void)
257     const
258 {
259     return atf_process_status_exitstatus(&m_status);
260 }
261 
262 bool
263 impl::status::signaled(void)
264     const
265 {
266     return atf_process_status_signaled(&m_status);
267 }
268 
269 int
270 impl::status::termsig(void)
271     const
272 {
273     return atf_process_status_termsig(&m_status);
274 }
275 
276 bool
277 impl::status::coredump(void)
278     const
279 {
280     return atf_process_status_coredump(&m_status);
281 }
282 
283 // ------------------------------------------------------------------------
284 // The "child" type.
285 // ------------------------------------------------------------------------
286 
287 impl::child::child(atf_process_child_t& c) :
288     m_child(c),
289     m_waited(false)
290 {
291 }
292 
293 impl::child::~child(void)
294 {
295     if (!m_waited) {
296         ::kill(atf_process_child_pid(&m_child), SIGTERM);
297 
298         atf_process_status_t s;
299         atf_error_t err = atf_process_child_wait(&m_child, &s);
300         INV(!atf_is_error(err));
301         atf_process_status_fini(&s);
302     }
303 }
304 
305 impl::status
306 impl::child::wait(void)
307 {
308     atf_process_status_t s;
309 
310     atf_error_t err = atf_process_child_wait(&m_child, &s);
311     if (atf_is_error(err))
312         throw_atf_error(err);
313 
314     m_waited = true;
315     return status(s);
316 }
317 
318 pid_t
319 impl::child::pid(void)
320     const
321 {
322     return atf_process_child_pid(&m_child);
323 }
324 
325 int
326 impl::child::stdout_fd(void)
327 {
328     return atf_process_child_stdout(&m_child);
329 }
330 
331 int
332 impl::child::stderr_fd(void)
333 {
334     return atf_process_child_stderr(&m_child);
335 }
336 
337 // ------------------------------------------------------------------------
338 // Free functions.
339 // ------------------------------------------------------------------------
340 
341 void
342 detail::flush_streams(void)
343 {
344     // This is a weird hack to ensure that the output of the parent process
345     // is flushed before executing a child which prevents, for example, the
346     // output of the atf-run hooks to appear before the output of atf-run
347     // itself.
348     //
349     // TODO: This should only be executed when inheriting the stdout or
350     // stderr file descriptors.  However, the flushing is specific to the
351     // iostreams, so we cannot do it from the C library where all the process
352     // logic is performed.  Come up with a better design.
353     std::cout.flush();
354     std::cerr.flush();
355 }
356