process.cpp revision 275988
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
28extern "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
40namespace detail = atf::process::detail;
41namespace impl = atf::process;
42#define IMPL_NAME "atf::process"
43
44// ------------------------------------------------------------------------
45// Auxiliary functions.
46// ------------------------------------------------------------------------
47
48template< class C >
49atf::auto_array< const char* >
50collection_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
66template< class C >
67C
68argv_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
82impl::argv_array::argv_array(void) :
83    m_exec_argv(collection_to_argv(m_args))
84{
85}
86
87impl::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
104impl::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
110impl::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
116void
117impl::argv_array::ctor_init_exec_argv(void)
118{
119    m_exec_argv = collection_to_argv(m_args);
120}
121
122const char* const*
123impl::argv_array::exec_argv(void)
124    const
125{
126    return m_exec_argv.get();
127}
128
129impl::argv_array::size_type
130impl::argv_array::size(void)
131    const
132{
133    return m_args.size();
134}
135
136const char*
137impl::argv_array::operator[](int idx)
138    const
139{
140    return m_args[idx].c_str();
141}
142
143impl::argv_array::const_iterator
144impl::argv_array::begin(void)
145    const
146{
147    return m_args.begin();
148}
149
150impl::argv_array::const_iterator
151impl::argv_array::end(void)
152    const
153{
154    return m_args.end();
155}
156
157impl::argv_array&
158impl::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
171impl::basic_stream::basic_stream(void) :
172    m_inited(false)
173{
174}
175
176impl::basic_stream::~basic_stream(void)
177{
178    if (m_inited)
179        atf_process_stream_fini(&m_sb);
180}
181
182const atf_process_stream_t*
183impl::basic_stream::get_sb(void)
184    const
185{
186    INV(m_inited);
187    return &m_sb;
188}
189
190impl::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
198impl::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
206impl::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
214impl::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
222impl::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
234impl::status::status(atf_process_status_t& s) :
235    m_status(s)
236{
237}
238
239impl::status::~status(void)
240{
241    atf_process_status_fini(&m_status);
242}
243
244bool
245impl::status::exited(void)
246    const
247{
248    return atf_process_status_exited(&m_status);
249}
250
251int
252impl::status::exitstatus(void)
253    const
254{
255    return atf_process_status_exitstatus(&m_status);
256}
257
258bool
259impl::status::signaled(void)
260    const
261{
262    return atf_process_status_signaled(&m_status);
263}
264
265int
266impl::status::termsig(void)
267    const
268{
269    return atf_process_status_termsig(&m_status);
270}
271
272bool
273impl::status::coredump(void)
274    const
275{
276    return atf_process_status_coredump(&m_status);
277}
278
279// ------------------------------------------------------------------------
280// The "child" type.
281// ------------------------------------------------------------------------
282
283impl::child::child(atf_process_child_t& c) :
284    m_child(c),
285    m_waited(false)
286{
287}
288
289impl::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
301impl::status
302impl::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
314pid_t
315impl::child::pid(void)
316    const
317{
318    return atf_process_child_pid(&m_child);
319}
320
321int
322impl::child::stdout_fd(void)
323{
324    return atf_process_child_stdout(&m_child);
325}
326
327int
328impl::child::stderr_fd(void)
329{
330    return atf_process_child_stderr(&m_child);
331}
332
333// ------------------------------------------------------------------------
334// Free functions.
335// ------------------------------------------------------------------------
336
337void
338detail::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