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