xref: /freebsd/contrib/atf/atf-c++/detail/process.cpp (revision 8a7df6c58d549bd2eaa5cad30b2ef93c3eca3c18)
1 // Copyright (c) 2008 The NetBSD Foundation, Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 // 1. Redistributions of source code must retain the above copyright
8 //    notice, this list of conditions and the following disclaimer.
9 // 2. Redistributions in binary form must reproduce the above copyright
10 //    notice, this list of conditions and the following disclaimer in the
11 //    documentation and/or other materials provided with the distribution.
12 //
13 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #include "atf-c++/detail/process.hpp"
27 
28 extern "C" {
29 #include <signal.h>
30 
31 #include "atf-c/detail/process.h"
32 #include "atf-c/error.h"
33 }
34 
35 #include <iostream>
36 
37 #include "atf-c++/detail/exceptions.hpp"
38 #include "atf-c++/detail/sanity.hpp"
39 
40 namespace detail = atf::process::detail;
41 namespace impl = atf::process;
42 #define IMPL_NAME "atf::process"
43 
44 // ------------------------------------------------------------------------
45 // Auxiliary functions.
46 // ------------------------------------------------------------------------
47 
48 template< class C >
49 std::vector<const char*>
50 collection_to_argv(const C& c)
51 {
52     std::vector<const char*> argv;
53     argv.reserve(c.size() + 1);
54 
55     for (const auto& item : c) {
56         argv.push_back(item.c_str());
57     }
58     argv.push_back(nullptr);
59 
60     return argv;
61 }
62 
63 template<class C>
64 C
65 argv_to_collection(const char* const* argv)
66 {
67     C c;
68 
69     for (const char* const* iter = argv; *iter != nullptr; iter++) {
70         c.push_back(std::string(*iter));
71     }
72 
73     return c;
74 }
75 
76 // ------------------------------------------------------------------------
77 // The "argv_array" type.
78 // ------------------------------------------------------------------------
79 
80 impl::argv_array::argv_array(void) :
81     m_exec_argv(collection_to_argv(m_args))
82 {
83 }
84 
85 impl::argv_array::argv_array(const char* arg1, ...)
86 {
87     m_args.push_back(arg1);
88 
89     {
90         va_list ap;
91         const char* nextarg;
92 
93         va_start(ap, arg1);
94         while ((nextarg = va_arg(ap, const char*)) != NULL)
95             m_args.push_back(nextarg);
96         va_end(ap);
97     }
98 
99     ctor_init_exec_argv();
100 }
101 
102 impl::argv_array::argv_array(const char* const* ca) :
103     m_args(argv_to_collection< args_vector >(ca)),
104     m_exec_argv(collection_to_argv(m_args))
105 {
106 }
107 
108 impl::argv_array::argv_array(const argv_array& a) :
109     m_args(a.m_args),
110     m_exec_argv(collection_to_argv(m_args))
111 {
112 }
113 
114 void
115 impl::argv_array::ctor_init_exec_argv(void)
116 {
117     m_exec_argv = collection_to_argv(m_args);
118 }
119 
120 const char* const*
121 impl::argv_array::exec_argv(void)
122     const
123 {
124     return m_exec_argv.data();
125 }
126 
127 impl::argv_array::size_type
128 impl::argv_array::size(void)
129     const
130 {
131     return m_args.size();
132 }
133 
134 const char*
135 impl::argv_array::operator[](int idx)
136     const
137 {
138     return m_args[idx].c_str();
139 }
140 
141 impl::argv_array::const_iterator
142 impl::argv_array::begin(void)
143     const
144 {
145     return m_args.begin();
146 }
147 
148 impl::argv_array::const_iterator
149 impl::argv_array::end(void)
150     const
151 {
152     return m_args.end();
153 }
154 
155 impl::argv_array&
156 impl::argv_array::operator=(const argv_array& a)
157 {
158     if (this != &a) {
159         m_args = a.m_args;
160         m_exec_argv = collection_to_argv(m_args);
161     }
162     return *this;
163 }
164 
165 // ------------------------------------------------------------------------
166 // The "stream" types.
167 // ------------------------------------------------------------------------
168 
169 impl::basic_stream::basic_stream(void) :
170     m_inited(false)
171 {
172 }
173 
174 impl::basic_stream::~basic_stream(void)
175 {
176     if (m_inited)
177         atf_process_stream_fini(&m_sb);
178 }
179 
180 const atf_process_stream_t*
181 impl::basic_stream::get_sb(void)
182     const
183 {
184     INV(m_inited);
185     return &m_sb;
186 }
187 
188 impl::stream_capture::stream_capture(void)
189 {
190     atf_error_t err = atf_process_stream_init_capture(&m_sb);
191     if (atf_is_error(err))
192         throw_atf_error(err);
193     m_inited = true;
194 }
195 
196 impl::stream_connect::stream_connect(const int src_fd, const int tgt_fd)
197 {
198     atf_error_t err = atf_process_stream_init_connect(&m_sb, src_fd, tgt_fd);
199     if (atf_is_error(err))
200         throw_atf_error(err);
201     m_inited = true;
202 }
203 
204 impl::stream_inherit::stream_inherit(void)
205 {
206     atf_error_t err = atf_process_stream_init_inherit(&m_sb);
207     if (atf_is_error(err))
208         throw_atf_error(err);
209     m_inited = true;
210 }
211 
212 impl::stream_redirect_fd::stream_redirect_fd(const int fd)
213 {
214     atf_error_t err = atf_process_stream_init_redirect_fd(&m_sb, fd);
215     if (atf_is_error(err))
216         throw_atf_error(err);
217     m_inited = true;
218 }
219 
220 impl::stream_redirect_path::stream_redirect_path(const fs::path& p)
221 {
222     atf_error_t err = atf_process_stream_init_redirect_path(&m_sb, p.c_path());
223     if (atf_is_error(err))
224         throw_atf_error(err);
225     m_inited = true;
226 }
227 
228 // ------------------------------------------------------------------------
229 // The "status" type.
230 // ------------------------------------------------------------------------
231 
232 impl::status::status(atf_process_status_t& s) :
233     m_status(s)
234 {
235 }
236 
237 impl::status::~status(void)
238 {
239     atf_process_status_fini(&m_status);
240 }
241 
242 bool
243 impl::status::exited(void)
244     const
245 {
246     return atf_process_status_exited(&m_status);
247 }
248 
249 int
250 impl::status::exitstatus(void)
251     const
252 {
253     return atf_process_status_exitstatus(&m_status);
254 }
255 
256 bool
257 impl::status::signaled(void)
258     const
259 {
260     return atf_process_status_signaled(&m_status);
261 }
262 
263 int
264 impl::status::termsig(void)
265     const
266 {
267     return atf_process_status_termsig(&m_status);
268 }
269 
270 bool
271 impl::status::coredump(void)
272     const
273 {
274     return atf_process_status_coredump(&m_status);
275 }
276 
277 // ------------------------------------------------------------------------
278 // The "child" type.
279 // ------------------------------------------------------------------------
280 
281 impl::child::child(atf_process_child_t& c) :
282     m_child(c),
283     m_waited(false)
284 {
285 }
286 
287 impl::child::~child(void)
288 {
289     if (!m_waited) {
290         ::kill(atf_process_child_pid(&m_child), SIGTERM);
291 
292         atf_process_status_t s;
293         atf_error_t err = atf_process_child_wait(&m_child, &s);
294         INV(!atf_is_error(err));
295         atf_process_status_fini(&s);
296     }
297 }
298 
299 impl::status
300 impl::child::wait(void)
301 {
302     atf_process_status_t s;
303 
304     atf_error_t err = atf_process_child_wait(&m_child, &s);
305     if (atf_is_error(err))
306         throw_atf_error(err);
307 
308     m_waited = true;
309     return status(s);
310 }
311 
312 pid_t
313 impl::child::pid(void)
314     const
315 {
316     return atf_process_child_pid(&m_child);
317 }
318 
319 int
320 impl::child::stdout_fd(void)
321 {
322     return atf_process_child_stdout(&m_child);
323 }
324 
325 int
326 impl::child::stderr_fd(void)
327 {
328     return atf_process_child_stderr(&m_child);
329 }
330 
331 // ------------------------------------------------------------------------
332 // Free functions.
333 // ------------------------------------------------------------------------
334 
335 void
336 detail::flush_streams(void)
337 {
338     // TODO: This should only be executed when inheriting the stdout or
339     // stderr file descriptors.  However, the flushing is specific to the
340     // iostreams, so we cannot do it from the C library where all the process
341     // logic is performed.  Come up with a better design.
342     std::cout.flush();
343     std::cerr.flush();
344 }
345