Home | History | Annotate | Line # | Download | only in detail
      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::auto_array< const char* >
     54 collection_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 
     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