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
30extern "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
44namespace detail = atf::process::detail;
45namespace impl = atf::process;
46#define IMPL_NAME "atf::process"
47
48// ------------------------------------------------------------------------
49// Auxiliary functions.
50// ------------------------------------------------------------------------
51
52template< class C >
53atf::auto_array< const char* >
54collection_to_argv(const C& c)
55{
56    atf::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
70template< class C >
71C
72argv_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
86impl::argv_array::argv_array(void) :
87    m_exec_argv(collection_to_argv(m_args))
88{
89}
90
91impl::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
108impl::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
114impl::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
120void
121impl::argv_array::ctor_init_exec_argv(void)
122{
123    m_exec_argv = collection_to_argv(m_args);
124}
125
126const char* const*
127impl::argv_array::exec_argv(void)
128    const
129{
130    return m_exec_argv.get();
131}
132
133impl::argv_array::size_type
134impl::argv_array::size(void)
135    const
136{
137    return m_args.size();
138}
139
140const char*
141impl::argv_array::operator[](int idx)
142    const
143{
144    return m_args[idx].c_str();
145}
146
147impl::argv_array::const_iterator
148impl::argv_array::begin(void)
149    const
150{
151    return m_args.begin();
152}
153
154impl::argv_array::const_iterator
155impl::argv_array::end(void)
156    const
157{
158    return m_args.end();
159}
160
161impl::argv_array&
162impl::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
175impl::basic_stream::basic_stream(void) :
176    m_inited(false)
177{
178}
179
180impl::basic_stream::~basic_stream(void)
181{
182    if (m_inited)
183        atf_process_stream_fini(&m_sb);
184}
185
186const atf_process_stream_t*
187impl::basic_stream::get_sb(void)
188    const
189{
190    INV(m_inited);
191    return &m_sb;
192}
193
194impl::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
202impl::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
210impl::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
218impl::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
226impl::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
238impl::status::status(atf_process_status_t& s) :
239    m_status(s)
240{
241}
242
243impl::status::~status(void)
244{
245    atf_process_status_fini(&m_status);
246}
247
248bool
249impl::status::exited(void)
250    const
251{
252    return atf_process_status_exited(&m_status);
253}
254
255int
256impl::status::exitstatus(void)
257    const
258{
259    return atf_process_status_exitstatus(&m_status);
260}
261
262bool
263impl::status::signaled(void)
264    const
265{
266    return atf_process_status_signaled(&m_status);
267}
268
269int
270impl::status::termsig(void)
271    const
272{
273    return atf_process_status_termsig(&m_status);
274}
275
276bool
277impl::status::coredump(void)
278    const
279{
280    return atf_process_status_coredump(&m_status);
281}
282
283// ------------------------------------------------------------------------
284// The "child" type.
285// ------------------------------------------------------------------------
286
287impl::child::child(atf_process_child_t& c) :
288    m_child(c),
289    m_waited(false)
290{
291}
292
293impl::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
305impl::status
306impl::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
318pid_t
319impl::child::pid(void)
320    const
321{
322    return atf_process_child_pid(&m_child);
323}
324
325int
326impl::child::stdout_fd(void)
327{
328    return atf_process_child_stdout(&m_child);
329}
330
331int
332impl::child::stderr_fd(void)
333{
334    return atf_process_child_stderr(&m_child);
335}
336
337// ------------------------------------------------------------------------
338// Free functions.
339// ------------------------------------------------------------------------
340
341void
342detail::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