diff options
Diffstat (limited to 'unit/atf-src/tools/process.hpp')
-rw-r--r-- | unit/atf-src/tools/process.hpp | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/unit/atf-src/tools/process.hpp b/unit/atf-src/tools/process.hpp new file mode 100644 index 0000000..2ebd865 --- /dev/null +++ b/unit/atf-src/tools/process.hpp @@ -0,0 +1,324 @@ +// +// Automated Testing Framework (atf) +// +// Copyright (c) 2008 The NetBSD Foundation, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#if !defined(TOOLS_PROCESS_HPP) +#define TOOLS_PROCESS_HPP + +extern "C" { +#include <sys/types.h> + +#include <unistd.h> +} + +#include <cerrno> +#include <cstdlib> +#include <iostream> +#include <string> +#include <vector> + +#include "auto_array.hpp" +#include "exceptions.hpp" +#include "fs.hpp" + +namespace tools { +namespace process { + +class child; +class status; + +// ------------------------------------------------------------------------ +// The "argv_array" type. +// ------------------------------------------------------------------------ + +class argv_array { + typedef std::vector< std::string > args_vector; + args_vector m_args; + + // TODO: This is immutable, so we should be able to use + // std::tr1::shared_array instead when it becomes widely available. + // The reason would be to remove all copy constructors and assignment + // operators from this class. + auto_array< const char* > m_exec_argv; + void ctor_init_exec_argv(void); + +public: + typedef args_vector::const_iterator const_iterator; + typedef args_vector::size_type size_type; + + argv_array(void); + argv_array(const char*, ...); + explicit argv_array(const char* const*); + template< class C > explicit argv_array(const C&); + argv_array(const argv_array&); + + const char* const* exec_argv(void) const; + size_type size(void) const; + const char* operator[](int) const; + + const_iterator begin(void) const; + const_iterator end(void) const; + + argv_array& operator=(const argv_array&); +}; + +template< class C > +argv_array::argv_array(const C& c) +{ + for (typename C::const_iterator iter = c.begin(); iter != c.end(); + iter++) + m_args.push_back(*iter); + ctor_init_exec_argv(); +} + +// ------------------------------------------------------------------------ +// The "stream" types. +// ------------------------------------------------------------------------ + +class stream_capture { + int m_pipefds[2]; + + // Allow access to the getters. + template< class OutStream, class ErrStream > friend + child fork(void (*)(void*), OutStream, ErrStream, void*); + template< class OutStream, class ErrStream > friend + status exec(const tools::fs::path&, const argv_array&, + const OutStream&, const ErrStream&, void (*)(void)); + + void prepare(void); + int connect_parent(void); + void connect_child(const int); + +public: + stream_capture(void); + ~stream_capture(void); +}; + +class stream_connect { + int m_src_fd; + int m_tgt_fd; + + // Allow access to the getters. + template< class OutStream, class ErrStream > friend + child fork(void (*)(void*), OutStream, ErrStream, void*); + template< class OutStream, class ErrStream > friend + status exec(const tools::fs::path&, const argv_array&, + const OutStream&, const ErrStream&, void (*)(void)); + + void prepare(void); + int connect_parent(void); + void connect_child(const int); + +public: + stream_connect(const int, const int); +}; + +class stream_inherit { + // Allow access to the getters. + template< class OutStream, class ErrStream > friend + child fork(void (*)(void*), OutStream, ErrStream, void*); + template< class OutStream, class ErrStream > friend + status exec(const tools::fs::path&, const argv_array&, + const OutStream&, const ErrStream&, void (*)(void)); + + void prepare(void); + int connect_parent(void); + void connect_child(const int); + +public: + stream_inherit(void); +}; + +class stream_redirect_fd { + int m_fd; + + // Allow access to the getters. + template< class OutStream, class ErrStream > friend + child fork(void (*)(void*), OutStream, ErrStream, void*); + template< class OutStream, class ErrStream > friend + status exec(const tools::fs::path&, const argv_array&, + const OutStream&, const ErrStream&, void (*)(void)); + + void prepare(void); + int connect_parent(void); + void connect_child(const int); + +public: + stream_redirect_fd(const int); +}; + +class stream_redirect_path { + const tools::fs::path m_path; + + // Allow access to the getters. + template< class OutStream, class ErrStream > friend + child fork(void (*)(void*), OutStream, ErrStream, void*); + template< class OutStream, class ErrStream > friend + status exec(const tools::fs::path&, const argv_array&, + const OutStream&, const ErrStream&, void (*)(void)); + + void prepare(void); + int connect_parent(void); + void connect_child(const int); + +public: + stream_redirect_path(const tools::fs::path&); +}; + +// ------------------------------------------------------------------------ +// The "status" type. +// ------------------------------------------------------------------------ + +class status { + int m_status; + + friend class child; + template< class OutStream, class ErrStream > friend + status exec(const tools::fs::path&, const argv_array&, + const OutStream&, const ErrStream&, void (*)(void)); + + status(int); + +public: + ~status(void); + + bool exited(void) const; + int exitstatus(void) const; + + bool signaled(void) const; + int termsig(void) const; + bool coredump(void) const; +}; + +// ------------------------------------------------------------------------ +// The "child" type. +// ------------------------------------------------------------------------ + +class child { + pid_t m_pid; + + int m_stdout; + int m_stderr; + + bool m_waited; + + template< class OutStream, class ErrStream > friend + child fork(void (*)(void*), OutStream, ErrStream, void*); + + child(const pid_t, const int, const int); + +public: + ~child(void); + + status wait(void); + + pid_t pid(void) const; + int stdout_fd(void); + int stderr_fd(void); +}; + +// ------------------------------------------------------------------------ +// Free functions. +// ------------------------------------------------------------------------ + +namespace detail { +void flush_streams(void); + +struct exec_args { + const tools::fs::path m_prog; + const argv_array& m_argv; + void (*m_prehook)(void); +}; + +void do_exec(void *); +} // namespace detail + +// TODO: The void* cookie can probably be templatized, thus also allowing +// const data structures. +template< class OutStream, class ErrStream > +child +fork(void (*start)(void*), OutStream outsb, ErrStream errsb, void* v) +{ + detail::flush_streams(); + + outsb.prepare(); + errsb.prepare(); + + pid_t pid = ::fork(); + if (pid == -1) { + throw system_error("tools::process::child::fork", + "Failed to fork", errno); + } else if (pid == 0) { + try { + outsb.connect_child(STDOUT_FILENO); + errsb.connect_child(STDERR_FILENO); + start(v); + std::abort(); + } catch (...) { + std::cerr << "Unhandled error while running subprocess\n"; + std::exit(EXIT_FAILURE); + } + } else { + const int stdout_fd = outsb.connect_parent(); + const int stderr_fd = errsb.connect_parent(); + return child(pid, stdout_fd, stderr_fd); + } +} + +template< class OutStream, class ErrStream > +status +exec(const tools::fs::path& prog, const argv_array& argv, + const OutStream& outsb, const ErrStream& errsb, + void (*prehook)(void)) +{ + struct detail::exec_args ea = { prog, argv, prehook }; + child c = fork(detail::do_exec, outsb, errsb, &ea); + +again: + try { + return c.wait(); + } catch (const system_error& e) { + if (e.code() == EINTR) + goto again; + else + throw e; + } +} + +template< class OutStream, class ErrStream > +status +exec(const tools::fs::path& prog, const argv_array& argv, + const OutStream& outsb, const ErrStream& errsb) +{ + return exec(prog, argv, outsb, errsb, NULL); +} + +} // namespace process +} // namespace tools + +#endif // !defined(TOOLS_PROCESS_HPP) |