Gav Wood
11 years ago
32 changed files with 5474 additions and 3 deletions
@ -0,0 +1,50 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process.hpp |
|||
* |
|||
* Convenience header that includes all other Boost.Process public header |
|||
* files. It is important to note that those headers that are specific to |
|||
* a given platform are only included if the library is being used in that |
|||
* same platform. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_HPP |
|||
#define BOOST_PROCESS_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <boost/process/posix_child.hpp> |
|||
# include <boost/process/posix_context.hpp> |
|||
# include <boost/process/posix_operations.hpp> |
|||
# include <boost/process/posix_status.hpp> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <boost/process/win32_child.hpp> |
|||
# include <boost/process/win32_context.hpp> |
|||
# include <boost/process/win32_operations.hpp> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/child.hpp> |
|||
#include <boost/process/context.hpp> |
|||
#include <boost/process/environment.hpp> |
|||
#include <boost/process/operations.hpp> |
|||
#include <boost/process/pistream.hpp> |
|||
#include <boost/process/postream.hpp> |
|||
#include <boost/process/process.hpp> |
|||
#include <boost/process/self.hpp> |
|||
#include <boost/process/status.hpp> |
|||
#include <boost/process/stream_behavior.hpp> |
|||
|
|||
#endif |
@ -0,0 +1,200 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/child.hpp |
|||
* |
|||
* Includes the declaration of the child class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_CHILD_HPP |
|||
#define BOOST_PROCESS_CHILD_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <sys/types.h> |
|||
# include <sys/wait.h> |
|||
# include <cerrno> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/process.hpp> |
|||
#include <boost/process/pistream.hpp> |
|||
#include <boost/process/postream.hpp> |
|||
#include <boost/process/status.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
#include <boost/shared_ptr.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <vector> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Generic implementation of the Child concept. |
|||
* |
|||
* The child class implements the Child concept in an operating system |
|||
* agnostic way. |
|||
*/ |
|||
class child : public process |
|||
{ |
|||
public: |
|||
/**
|
|||
* Gets a reference to the child's standard input stream. |
|||
* |
|||
* Returns a reference to a postream object that represents the |
|||
* standard input communication channel with the child process. |
|||
*/ |
|||
postream &get_stdin() const |
|||
{ |
|||
BOOST_ASSERT(stdin_); |
|||
|
|||
return *stdin_; |
|||
} |
|||
|
|||
/**
|
|||
* Gets a reference to the child's standard output stream. |
|||
* |
|||
* Returns a reference to a pistream object that represents the |
|||
* standard output communication channel with the child process. |
|||
*/ |
|||
pistream &get_stdout() const |
|||
{ |
|||
BOOST_ASSERT(stdout_); |
|||
|
|||
return *stdout_; |
|||
} |
|||
|
|||
/**
|
|||
* Gets a reference to the child's standard error stream. |
|||
* |
|||
* Returns a reference to a pistream object that represents the |
|||
* standard error communication channel with the child process. |
|||
*/ |
|||
pistream &get_stderr() const |
|||
{ |
|||
BOOST_ASSERT(stderr_); |
|||
|
|||
return *stderr_; |
|||
} |
|||
|
|||
/**
|
|||
* Blocks and waits for the child process to terminate. |
|||
* |
|||
* Returns a status object that represents the child process' |
|||
* finalization condition. The child process object ceases to be |
|||
* valid after this call. |
|||
* |
|||
* \remark Blocking remarks: This call blocks if the child |
|||
* process has not finalized execution and waits until |
|||
* it terminates. |
|||
*/ |
|||
status wait() |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
int s; |
|||
if (::waitpid(get_id(), &s, 0) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::child::wait: waitpid(2) failed")); |
|||
return status(s); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
::WaitForSingleObject(process_handle_.get(), INFINITE); |
|||
DWORD code; |
|||
if (!::GetExitCodeProcess(process_handle_.get(), &code)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::child::wait: GetExitCodeProcess failed")); |
|||
return status(code); |
|||
#endif |
|||
} |
|||
|
|||
/**
|
|||
* Creates a new child object that represents the just spawned child |
|||
* process \a id. |
|||
* |
|||
* The \a fhstdin, \a fhstdout and \a fhstderr file handles represent |
|||
* the parent's handles used to communicate with the corresponding |
|||
* data streams. They needn't be valid but their availability must |
|||
* match the redirections configured by the launcher that spawned this |
|||
* process. |
|||
* |
|||
* The \a fhprocess handle represents a handle to the child process. |
|||
* It is only used on Windows as the implementation of wait() needs a |
|||
* process handle. |
|||
*/ |
|||
child(id_type id, detail::file_handle fhstdin, detail::file_handle fhstdout, detail::file_handle fhstderr, detail::file_handle fhprocess = detail::file_handle()) |
|||
: process(id) |
|||
#if defined(BOOST_WINDOWS_API) |
|||
, process_handle_(fhprocess.release(), ::CloseHandle) |
|||
#endif |
|||
{ |
|||
if (fhstdin.valid()) |
|||
stdin_.reset(new postream(fhstdin)); |
|||
if (fhstdout.valid()) |
|||
stdout_.reset(new pistream(fhstdout)); |
|||
if (fhstderr.valid()) |
|||
stderr_.reset(new pistream(fhstderr)); |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* The standard input stream attached to the child process. |
|||
* |
|||
* This postream object holds the communication channel with the |
|||
* child's process standard input. It is stored in a pointer because |
|||
* this field is only valid when the user requested to redirect this |
|||
* data stream. |
|||
*/ |
|||
boost::shared_ptr<postream> stdin_; |
|||
|
|||
/**
|
|||
* The standard output stream attached to the child process. |
|||
* |
|||
* This postream object holds the communication channel with the |
|||
* child's process standard output. It is stored in a pointer because |
|||
* this field is only valid when the user requested to redirect this |
|||
* data stream. |
|||
*/ |
|||
boost::shared_ptr<pistream> stdout_; |
|||
|
|||
/**
|
|||
* The standard error stream attached to the child process. |
|||
* |
|||
* This postream object holds the communication channel with the |
|||
* child's process standard error. It is stored in a pointer because |
|||
* this field is only valid when the user requested to redirect this |
|||
* data stream. |
|||
*/ |
|||
boost::shared_ptr<pistream> stderr_; |
|||
|
|||
#if defined(BOOST_WINDOWS_API) |
|||
/**
|
|||
* Process handle owned by RAII object. |
|||
*/ |
|||
boost::shared_ptr<void> process_handle_; |
|||
#endif |
|||
}; |
|||
|
|||
/**
|
|||
* Collection of child objects. |
|||
* |
|||
* This convenience type represents a collection of child objects backed |
|||
* by a vector. |
|||
*/ |
|||
typedef std::vector<child> children; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,41 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/config.hpp |
|||
* |
|||
* Defines macros that are used by the library's code to determine the |
|||
* operating system it is running under and the features it supports. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_CONFIG_HPP |
|||
#define BOOST_PROCESS_CONFIG_HPP |
|||
|
|||
#include <boost/config.hpp> |
|||
#include <boost/system/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
# if !defined(BOOST_PROCESS_POSIX_PATH_MAX) |
|||
/**
|
|||
* The macro BOOST_PROCESS_POSIX_PATH_MAX is set to a positive integer |
|||
* value which specifies the system's maximal supported path length. |
|||
* By default it is set to 259. You should set the macro to PATH_MAX |
|||
* which should be defined in limits.h provided by your operating system |
|||
* if you experience problems when instantiating a context. The |
|||
* constructor of basic_work_directory_context tries to find out |
|||
* dynamically the maximal supported path length but uses |
|||
* BOOST_PROCESS_POSIX_PATH_MAX if it fails. |
|||
*/ |
|||
# define BOOST_PROCESS_POSIX_PATH_MAX 259 |
|||
# endif |
|||
#endif |
|||
|
|||
#endif |
@ -0,0 +1,209 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/context.hpp |
|||
* |
|||
* Includes the declaration of the context class and several accessory |
|||
* base classes. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_CONTEXT_HPP |
|||
#define BOOST_PROCESS_CONTEXT_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <boost/scoped_array.hpp> |
|||
# include <cerrno> |
|||
# include <unistd.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/environment.hpp> |
|||
#include <boost/process/stream_behavior.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Base context class that defines the child's work directory. |
|||
* |
|||
* Base context class that defines the necessary fields to configure a |
|||
* child's work directory. This class is useless on its own because no |
|||
* function in the library will accept it as a valid Context |
|||
* implementation. |
|||
*/ |
|||
template <class Path> |
|||
class basic_work_directory_context |
|||
{ |
|||
public: |
|||
/**
|
|||
* Constructs a new work directory context. |
|||
* |
|||
* Constructs a new work directory context making the work directory |
|||
* described by the new object point to the caller's current working |
|||
* directory. |
|||
*/ |
|||
basic_work_directory_context() |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
errno = 0; |
|||
long size = ::pathconf(".", _PC_PATH_MAX); |
|||
if (size == -1 && errno) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::basic_work_directory_context::basic_work_directory_context: pathconf(2) failed")); |
|||
else if (size == -1) |
|||
size = BOOST_PROCESS_POSIX_PATH_MAX; |
|||
boost::scoped_array<char> cwd(new char[size]); |
|||
if (!::getcwd(cwd.get(), size)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::basic_work_directory_context::basic_work_directory_context: getcwd(2) failed")); |
|||
work_directory = cwd.get(); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
char cwd[MAX_PATH]; |
|||
if (!::GetCurrentDirectoryA(sizeof(cwd), cwd)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::basic_work_directory_context::basic_work_directory_context: GetCurrentDirectory failed")); |
|||
work_directory = cwd; |
|||
#endif |
|||
BOOST_ASSERT(!work_directory.empty()); |
|||
} |
|||
|
|||
/**
|
|||
* The process' initial work directory. |
|||
* |
|||
* The work directory is the directory in which the process starts |
|||
* execution. |
|||
*/ |
|||
Path work_directory; |
|||
}; |
|||
|
|||
/**
|
|||
* Base context class that defines the child's environment. |
|||
* |
|||
* Base context class that defines the necessary fields to configure a |
|||
* child's environment variables. This class is useless on its own |
|||
* because no function in the library will accept it as a valid Context |
|||
* implementation. |
|||
*/ |
|||
class environment_context |
|||
{ |
|||
public: |
|||
/**
|
|||
* The process' environment. |
|||
* |
|||
* Contains the list of environment variables, alongside with their |
|||
* values, that will be passed to the spawned child process. |
|||
*/ |
|||
boost::process::environment environment; |
|||
}; |
|||
|
|||
/**
|
|||
* Process startup execution context. |
|||
* |
|||
* The context class groups all the parameters needed to configure a |
|||
* process' environment during its creation. |
|||
*/ |
|||
template <class Path> |
|||
class basic_context : public basic_work_directory_context<Path>, public environment_context |
|||
{ |
|||
public: |
|||
/**
|
|||
* Child's stdin behavior. |
|||
*/ |
|||
stream_behavior stdin_behavior; |
|||
|
|||
/**
|
|||
* Child's stdout behavior. |
|||
*/ |
|||
stream_behavior stdout_behavior; |
|||
|
|||
/**
|
|||
* Child's stderr behavior. |
|||
*/ |
|||
stream_behavior stderr_behavior; |
|||
}; |
|||
|
|||
typedef basic_context<std::string> context; |
|||
|
|||
/**
|
|||
* Represents a child process in a pipeline. |
|||
* |
|||
* This convenience class is a triplet that holds all the data required |
|||
* to spawn a new child process in a pipeline. |
|||
*/ |
|||
template <class Executable, class Arguments, class Context> |
|||
class basic_pipeline_entry |
|||
{ |
|||
public: |
|||
/**
|
|||
* The executable to launch. |
|||
*/ |
|||
Executable executable; |
|||
|
|||
/**
|
|||
* The set of arguments to pass to the executable. |
|||
*/ |
|||
Arguments arguments; |
|||
|
|||
/**
|
|||
* The child's execution context. |
|||
*/ |
|||
Context context; |
|||
|
|||
/**
|
|||
* The type of the Executable concept used in this template |
|||
* instantiation. |
|||
*/ |
|||
typedef Executable executable_type; |
|||
|
|||
/**
|
|||
* The type of the Arguments concept used in this template |
|||
* instantiation. |
|||
*/ |
|||
typedef Arguments arguments_type; |
|||
|
|||
/**
|
|||
* The type of the Context concept used in this template |
|||
* instantiation. |
|||
*/ |
|||
typedef Context context_type; |
|||
|
|||
/**
|
|||
* Constructs a new pipeline_entry object. |
|||
* |
|||
* Given the executable, set of arguments and execution triplet, |
|||
* constructs a new pipeline_entry object that holds the three |
|||
* values. |
|||
*/ |
|||
basic_pipeline_entry(const Executable &exe, const Arguments &args, const Context &ctx) |
|||
: executable(exe), |
|||
arguments(args), |
|||
context(ctx) |
|||
{ |
|||
} |
|||
}; |
|||
|
|||
/**
|
|||
* Default instantiation of basic_pipeline_entry. |
|||
*/ |
|||
typedef basic_pipeline_entry<std::string, std::vector<std::string>, context> pipeline_entry; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,406 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/detail/file_handle.hpp |
|||
* |
|||
* Includes the declaration of the file_handle class. This file is for |
|||
* internal usage only and must not be included by the library user. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_DETAIL_FILE_HANDLE_HPP |
|||
#define BOOST_PROCESS_DETAIL_FILE_HANDLE_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <cerrno> |
|||
# include <unistd.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/assert.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
namespace detail { |
|||
|
|||
/**
|
|||
* Simple RAII model for system file handles. |
|||
* |
|||
* The \a file_handle class is a simple RAII model for native system file |
|||
* handles. This class wraps one of such handles grabbing its ownership, |
|||
* and automaticaly closes it upon destruction. It is basically used |
|||
* inside the library to avoid leaking open file handles, shall an |
|||
* unexpected execution trace occur. |
|||
* |
|||
* A \a file_handle object can be copied but doing so invalidates the |
|||
* source object. There can only be a single valid \a file_handle object |
|||
* for a given system file handle. This is similar to std::auto_ptr's |
|||
* semantics. |
|||
* |
|||
* This class also provides some convenience methods to issue special file |
|||
* operations under their respective platforms. |
|||
*/ |
|||
class file_handle |
|||
{ |
|||
public: |
|||
#if defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* Opaque name for the native handle type. |
|||
* |
|||
* Each operating system identifies file handles using a specific type. |
|||
* The \a handle_type type is used to transparently refer to file |
|||
* handles regarless of the operating system in which this class is |
|||
* used. |
|||
* |
|||
* If this class is used on a POSIX system, \a NativeSystemHandle is |
|||
* an integer type while it is a \a HANDLE on a Windows system. |
|||
*/ |
|||
typedef NativeSystemHandle handle_type; |
|||
#elif defined(BOOST_POSIX_API) |
|||
typedef int handle_type; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
typedef HANDLE handle_type; |
|||
#endif |
|||
|
|||
/**
|
|||
* Constructs an invalid file handle. |
|||
* |
|||
* This constructor creates a new \a file_handle object that represents |
|||
* an invalid file handle. An invalid file handle can be copied but |
|||
* cannot be manipulated in any way (except checking for its validity). |
|||
* |
|||
* \see valid() |
|||
*/ |
|||
file_handle() |
|||
: handle_(invalid_value()) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Constructs a new file handle from a native file handle. |
|||
* |
|||
* This constructor creates a new \a file_handle object that takes |
|||
* ownership of the given \a h native file handle. The user must not |
|||
* close \a h on his own during the lifetime of the new object. |
|||
* Ownership can be reclaimed using release(). |
|||
* |
|||
* \pre The native file handle must be valid; a close operation must |
|||
* succeed on it. |
|||
* \see release() |
|||
*/ |
|||
file_handle(handle_type h) |
|||
: handle_(h) |
|||
{ |
|||
BOOST_ASSERT(handle_ != invalid_value()); |
|||
} |
|||
|
|||
/**
|
|||
* Copy constructor; invalidates the source handle. |
|||
* |
|||
* This copy constructor creates a new file handle from a given one. |
|||
* Ownership of the native file handle is transferred to the new |
|||
* object, effectively invalidating the source file handle. This |
|||
* avoids having two live \a file_handle objects referring to the |
|||
* same native file handle. The source file handle needs not be |
|||
* valid in the name of simplicity. |
|||
* |
|||
* \post The source file handle is invalid. |
|||
* \post The new file handle owns the source's native file handle. |
|||
*/ |
|||
file_handle(const file_handle &fh) |
|||
: handle_(fh.handle_) |
|||
{ |
|||
fh.handle_ = invalid_value(); |
|||
} |
|||
|
|||
/**
|
|||
* Releases resources if the handle is valid. |
|||
* |
|||
* If the file handle is valid, the destructor closes it. |
|||
* |
|||
* \see valid() |
|||
*/ |
|||
~file_handle() |
|||
{ |
|||
if (valid()) |
|||
close(); |
|||
} |
|||
|
|||
/**
|
|||
* Assignment operator; invalidates the source handle. |
|||
* |
|||
* This assignment operator transfers ownership of the RHS file |
|||
* handle to the LHS one, effectively invalidating the source file |
|||
* handle. This avoids having two live \a file_handle objects |
|||
* referring to the same native file handle. The source file |
|||
* handle needs not be valid in the name of simplicity. |
|||
* |
|||
* \post The RHS file handle is invalid. |
|||
* \post The LHS file handle owns RHS' native file handle. |
|||
* \return A reference to the LHS file handle. |
|||
*/ |
|||
file_handle &operator=(const file_handle &fh) |
|||
{ |
|||
handle_ = fh.handle_; |
|||
fh.handle_ = invalid_value(); |
|||
return *this; |
|||
} |
|||
|
|||
/**
|
|||
* Checks whether the file handle is valid or not. |
|||
* |
|||
* Returns a boolean indicating whether the file handle is valid or |
|||
* not. If the file handle is invalid, no other methods can be |
|||
* executed other than the destructor. |
|||
* |
|||
* \return true if the file handle is valid; false otherwise. |
|||
*/ |
|||
bool valid() const |
|||
{ |
|||
return handle_ != invalid_value(); |
|||
} |
|||
|
|||
/**
|
|||
* Closes the file handle. |
|||
* |
|||
* Explicitly closes the file handle, which must be valid. Upon |
|||
* exit, the handle is not valid any more. |
|||
* |
|||
* \pre The file handle is valid. |
|||
* \post The file handle is invalid. |
|||
* \post The native file handle is closed. |
|||
*/ |
|||
void close() |
|||
{ |
|||
BOOST_ASSERT(valid()); |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
::close(handle_); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
::CloseHandle(handle_); |
|||
#endif |
|||
|
|||
handle_ = invalid_value(); |
|||
} |
|||
|
|||
/**
|
|||
* Reclaims ownership of the native file handle. |
|||
* |
|||
* Explicitly reclaims ownership of the native file handle contained |
|||
* in the \a file_handle object, returning the native file handle. |
|||
* The caller is responsible of closing it later on. |
|||
* |
|||
* \pre The file handle is valid. |
|||
* \post The file handle is invalid. |
|||
* \return The native file handle. |
|||
*/ |
|||
handle_type release() |
|||
{ |
|||
BOOST_ASSERT(valid()); |
|||
|
|||
handle_type h = handle_; |
|||
handle_ = invalid_value(); |
|||
return h; |
|||
} |
|||
|
|||
/**
|
|||
* Gets the native file handle. |
|||
* |
|||
* Returns the native file handle for the \a file_handle object. |
|||
* The caller can issue any operation on it except closing it. |
|||
* If closing is required, release() shall be used. |
|||
* |
|||
* \pre The file handle is valid. |
|||
* \post The file handle is valid. |
|||
* \return The native file handle. |
|||
*/ |
|||
handle_type get() const |
|||
{ |
|||
BOOST_ASSERT(valid()); |
|||
|
|||
return handle_; |
|||
} |
|||
|
|||
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* Changes the native file handle to the given one. |
|||
* |
|||
* Given a new native file handle \a h, this operation assigns this |
|||
* handle to the current object, closing its old native file handle. |
|||
* In other words, it first calls dup2() to remap the old handle to |
|||
* the new one and then closes the old handle. |
|||
* |
|||
* If \a h is open, it is automatically closed by dup2(). |
|||
* |
|||
* This operation is only available in POSIX systems. |
|||
* |
|||
* \pre The file handle is valid. |
|||
* \pre The native file handle \a h is valid; i.e., it must be |
|||
* closeable. |
|||
* \post The file handle's native file handle is \a h. |
|||
* \throw boost::system::system_error If the internal remapping |
|||
* operation fails. |
|||
*/ |
|||
void posix_remap(handle_type h) |
|||
{ |
|||
BOOST_ASSERT(valid()); |
|||
|
|||
if (::dup2(handle_, h) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::file_handle::posix_remap: dup2(2) failed")); |
|||
|
|||
if (::close(handle_) == -1) |
|||
{ |
|||
::close(h); |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::file_handle::posix_remap: close(2) failed")); |
|||
} |
|||
|
|||
handle_ = h; |
|||
} |
|||
|
|||
/**
|
|||
* Duplicates an open native file handle. |
|||
* |
|||
* Given a native file handle \a h1, this routine duplicates it so |
|||
* that it ends up being identified by the native file handle \a h2 |
|||
* and returns a new \a file_handle owning \a h2. |
|||
* |
|||
* This operation is only available in POSIX systems. |
|||
* |
|||
* \pre The native file handle \a h1 is open. |
|||
* \pre The native file handle \a h2 is valid (non-negative). |
|||
* \post The native file handle \a h1 is closed. |
|||
* \post The native file handle \a h2 is the same as the old \a h1 |
|||
* from the operating system's point of view. |
|||
* \return A new \a file_handle object that owns \a h2. |
|||
* \throw boost::system::system_error If dup2() fails. |
|||
*/ |
|||
static file_handle posix_dup(int h1, int h2) |
|||
{ |
|||
if (::dup2(h1, h2) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::file_handle::posix_dup: dup2(2) failed")); |
|||
|
|||
return file_handle(h2); |
|||
} |
|||
#endif |
|||
|
|||
#if defined(BOOST_WINDOWS_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* Duplicates the \a h native file handle. |
|||
* |
|||
* Given a native file handle \a h, this routine constructs a new |
|||
* \a file_handle object that owns a new duplicate of \a h. The |
|||
* duplicate's inheritable flag is set to the value of \a inheritable. |
|||
* |
|||
* This operation is only available in Windows systems. |
|||
* |
|||
* \pre The native file handle \a h is valid. |
|||
* \return A file handle owning a duplicate of \a h. |
|||
* \throw boost::system::system_error If DuplicateHandle() fails. |
|||
*/ |
|||
static file_handle win32_dup(HANDLE h, bool inheritable) |
|||
{ |
|||
HANDLE h2; |
|||
if (!::DuplicateHandle(::GetCurrentProcess(), h, ::GetCurrentProcess(), &h2, 0, inheritable ? TRUE : FALSE, DUPLICATE_SAME_ACCESS)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::file_handle::win32_dup: DuplicateHandle failed")); |
|||
|
|||
return file_handle(h2); |
|||
} |
|||
|
|||
/**
|
|||
* Creates a new duplicate of a standard file handle. |
|||
* |
|||
* Constructs a new \a file_handle object that owns a duplicate of a |
|||
* standard file handle. The \a d parameter specifies which standard |
|||
* file handle to duplicate and can be one of \a STD_INPUT_HANDLE, |
|||
* \a STD_OUTPUT_HANDLE or \a STD_ERROR_HANDLE. The duplicate's |
|||
* inheritable flag is set to the value of \a inheritable. |
|||
* |
|||
* This operation is only available in Windows systems. |
|||
* |
|||
* \pre \a d refers to one of the standard handles as described above. |
|||
* \return A file handle owning a duplicate of the standard handle |
|||
* referred to by \a d. |
|||
* \throw boost::system::system_error If GetStdHandle() or |
|||
* DuplicateHandle() fails. |
|||
*/ |
|||
static file_handle win32_std(DWORD d, bool inheritable) |
|||
{ |
|||
BOOST_ASSERT(d == STD_INPUT_HANDLE || d == STD_OUTPUT_HANDLE || d == STD_ERROR_HANDLE); |
|||
|
|||
HANDLE h = ::GetStdHandle(d); |
|||
if (h == INVALID_HANDLE_VALUE) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::file_handle::win32_std: GetStdHandle failed")); |
|||
|
|||
return win32_dup(h, inheritable); |
|||
} |
|||
|
|||
/**
|
|||
* Changes the file handle's inheritable flag. |
|||
* |
|||
* Changes the file handle's inheritable flag to \a i. It is not |
|||
* necessary for the file handle's flag to be different than \a i. |
|||
* |
|||
* This operation is only available in Windows systems. |
|||
* |
|||
* \pre The file handle is valid. |
|||
* \post The native file handle's inheritable flag is set to \a i. |
|||
* \throw boost::system::system_error If the property change fails. |
|||
*/ |
|||
void win32_set_inheritable(bool i) |
|||
{ |
|||
BOOST_ASSERT(valid()); |
|||
|
|||
if (!::SetHandleInformation(handle_, HANDLE_FLAG_INHERIT, i ? HANDLE_FLAG_INHERIT : 0)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::file_handle::win32_set_inheritable: SetHandleInformation failed")); |
|||
} |
|||
#endif |
|||
|
|||
private: |
|||
/**
|
|||
* Internal handle value. |
|||
* |
|||
* This variable holds the native handle value for the file handle |
|||
* hold by this object. It is interesting to note that this needs |
|||
* to be mutable because the copy constructor and the assignment |
|||
* operator invalidate the source object. |
|||
*/ |
|||
mutable handle_type handle_; |
|||
|
|||
/**
|
|||
* Constant function representing an invalid handle value. |
|||
* |
|||
* Returns the platform-specific handle value that represents an |
|||
* invalid handle. This is a constant function rather than a regular |
|||
* constant because, in the latter case, we cannot define it under |
|||
* Windows due to the value being of a complex type. |
|||
*/ |
|||
static handle_type invalid_value() |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
return -1; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
return INVALID_HANDLE_VALUE; |
|||
#endif |
|||
} |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,187 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/detail/pipe.hpp |
|||
* |
|||
* Includes the declaration of the pipe class. This file is for |
|||
* internal usage only and must not be included by the library user. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_DETAIL_PIPE_HPP |
|||
#define BOOST_PROCESS_DETAIL_PIPE_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <unistd.h> |
|||
# include <cerrno> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# if defined(BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE) |
|||
# include <boost/lexical_cast.hpp> |
|||
# include <string> |
|||
# endif |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
namespace detail { |
|||
|
|||
/**
|
|||
* Simple RAII model for anonymous pipes. |
|||
* |
|||
* The pipe class is a simple RAII model for anonymous pipes. It |
|||
* provides a portable constructor that allocates a new %pipe and creates |
|||
* a pipe object that owns the two file handles associated to it: the |
|||
* read end and the write end. |
|||
* |
|||
* These handles can be retrieved for modification according to |
|||
* file_handle semantics. Optionally, their ownership can be transferred |
|||
* to external \a file_handle objects which comes handy when the two |
|||
* ends need to be used in different places (i.e. after a POSIX fork() |
|||
* system call). |
|||
* |
|||
* Pipes can be copied following the same semantics as file handles. |
|||
* In other words, copying a %pipe object invalidates the source one. |
|||
* |
|||
* \see file_handle |
|||
*/ |
|||
class pipe |
|||
{ |
|||
public: |
|||
/**
|
|||
* Creates a new %pipe. |
|||
* |
|||
* The default pipe constructor allocates a new anonymous %pipe |
|||
* and assigns its ownership to the created pipe object. On Windows |
|||
* when the macro BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE is defined |
|||
* a named pipe is created. This is required if asynchronous I/O |
|||
* should be used as asynchronous I/O is only supported by named |
|||
* pipes on Windows. |
|||
* |
|||
* \throw boost::system::system_error If the anonymous %pipe |
|||
* creation fails. |
|||
*/ |
|||
pipe() |
|||
{ |
|||
file_handle::handle_type hs[2]; |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
if (::pipe(hs) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::pipe::pipe: pipe(2) failed")); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
SECURITY_ATTRIBUTES sa; |
|||
ZeroMemory(&sa, sizeof(sa)); |
|||
sa.nLength = sizeof(sa); |
|||
sa.lpSecurityDescriptor = NULL; |
|||
sa.bInheritHandle = FALSE; |
|||
|
|||
# if defined(BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE) |
|||
static unsigned int nextid = 0; |
|||
std::string pipe = "\\\\.\\pipe\\boost_process_" + boost::lexical_cast<std::string>(::GetCurrentProcessId()) + "_" + boost::lexical_cast<std::string>(nextid++); |
|||
hs[0] = ::CreateNamedPipeA(pipe.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, &sa); |
|||
if (hs[0] == INVALID_HANDLE_VALUE) |
|||
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category, "boost::process::detail::pipe::pipe: CreateNamedPipe failed")); |
|||
hs[1] = ::CreateFileA(pipe.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); |
|||
if (hs[1] == INVALID_HANDLE_VALUE) |
|||
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category, "boost::process::detail::pipe::pipe: CreateFile failed")); |
|||
|
|||
OVERLAPPED overlapped; |
|||
ZeroMemory(&overlapped, sizeof(overlapped)); |
|||
overlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
|||
if (!overlapped.hEvent) |
|||
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category, "boost::process::detail::pipe::pipe: CreateEvent failed")); |
|||
BOOL b = ::ConnectNamedPipe(hs[0], &overlapped); |
|||
if (!b) |
|||
{ |
|||
if (::GetLastError() == ERROR_IO_PENDING) |
|||
{ |
|||
if (::WaitForSingleObject(overlapped.hEvent, INFINITE) == WAIT_FAILED) |
|||
{ |
|||
::CloseHandle(overlapped.hEvent); |
|||
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category, "boost::process::detail::pipe::pipe: WaitForSingleObject failed")); |
|||
} |
|||
} |
|||
else if (::GetLastError() != ERROR_PIPE_CONNECTED) |
|||
{ |
|||
::CloseHandle(overlapped.hEvent); |
|||
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category, "boost::process::detail::pipe::pipe: ConnectNamedPipe failed")); |
|||
} |
|||
} |
|||
::CloseHandle(overlapped.hEvent); |
|||
# else |
|||
if (!::CreatePipe(&hs[0], &hs[1], &sa, 0)) |
|||
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category, "boost::process::detail::pipe::pipe: CreatePipe failed")); |
|||
# endif |
|||
#endif |
|||
|
|||
read_end_ = file_handle(hs[0]); |
|||
write_end_ = file_handle(hs[1]); |
|||
} |
|||
|
|||
/**
|
|||
* Returns the %pipe's read end file handle. |
|||
* |
|||
* Obtains a reference to the %pipe's read end file handle. Care |
|||
* should be taken to not duplicate the returned object if ownership |
|||
* shall remain to the %pipe. |
|||
* |
|||
* Duplicating the returned object invalidates its corresponding file |
|||
* handle in the %pipe. |
|||
* |
|||
* \return A reference to the %pipe's read end file handle. |
|||
*/ |
|||
file_handle &rend() |
|||
{ |
|||
return read_end_; |
|||
} |
|||
|
|||
/**
|
|||
* Returns the %pipe's write end file handle. |
|||
* |
|||
* Obtains a reference to the %pipe's write end file handle. Care |
|||
* should be taken to not duplicate the returned object if ownership |
|||
* shall remain to the %pipe. |
|||
* |
|||
* Duplicating the returned object invalidates its corresponding file |
|||
* handle in the %pipe. |
|||
* |
|||
* \return A reference to the %pipe's write end file handle. |
|||
*/ |
|||
file_handle &wend() |
|||
{ |
|||
return write_end_; |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* The %pipe's read end file handle. |
|||
*/ |
|||
file_handle read_end_; |
|||
|
|||
/**
|
|||
* The %pipe's write end file handle. |
|||
*/ |
|||
file_handle write_end_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,495 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/detail/posix_ops.hpp |
|||
* |
|||
* Provides some convenience functions to start processes under POSIX |
|||
* operating systems. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_DETAIL_POSIX_OPS_HPP |
|||
#define BOOST_PROCESS_DETAIL_POSIX_OPS_HPP |
|||
|
|||
#include <boost/process/environment.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/pipe.hpp> |
|||
#include <boost/process/detail/stream_info.hpp> |
|||
#include <boost/scoped_array.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
#include <map> |
|||
#include <utility> |
|||
#include <string> |
|||
#include <cerrno> |
|||
#include <cstdlib> |
|||
#include <cstring> |
|||
#include <fcntl.h> |
|||
#include <unistd.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
namespace detail { |
|||
|
|||
/**
|
|||
* Converts the command line to an array of C strings. |
|||
* |
|||
* Converts the command line's list of arguments to the format expected |
|||
* by the \a argv parameter in the POSIX execve() system call. |
|||
* |
|||
* This operation is only available on POSIX systems. |
|||
* |
|||
* \return The first argument of the pair is an integer that indicates |
|||
* how many strings are stored in the second argument. The |
|||
* second argument is a NULL-terminated, dynamically allocated |
|||
* array of dynamically allocated strings holding the arguments |
|||
* to the executable. The caller is responsible of freeing them. |
|||
*/ |
|||
template <class Arguments> |
|||
inline std::pair<std::size_t, char**> collection_to_posix_argv(const Arguments &args) |
|||
{ |
|||
std::size_t nargs = args.size(); |
|||
BOOST_ASSERT(nargs > 0); |
|||
|
|||
char **argv = new char*[nargs + 1]; |
|||
typename Arguments::size_type i = 0; |
|||
for (typename Arguments::const_iterator it = args.begin(); it != args.end(); ++it) |
|||
{ |
|||
argv[i] = new char[it->size() + 1]; |
|||
std::strncpy(argv[i], it->c_str(), it->size() + 1); |
|||
++i; |
|||
} |
|||
argv[nargs] = 0; |
|||
|
|||
return std::pair<std::size_t, char**>(nargs, argv); |
|||
} |
|||
|
|||
/**
|
|||
* Converts an environment to a char** table as used by execve(). |
|||
* |
|||
* Converts the environment's contents to the format used by the |
|||
* execve() system call. The returned char** array is allocated |
|||
* in dynamic memory; the caller must free it when not used any |
|||
* more. Each entry is also allocated in dynamic memory and is a |
|||
* NULL-terminated string of the form var=value; these must also be |
|||
* released by the caller. |
|||
* |
|||
* \return A dynamically allocated char** array that represents |
|||
* the environment's content. Each array entry is a |
|||
* NULL-terminated string of the form var=value. |
|||
*/ |
|||
inline char **environment_to_envp(const environment &env) |
|||
{ |
|||
char **envp = new char*[env.size() + 1]; |
|||
|
|||
environment::size_type i = 0; |
|||
for (environment::const_iterator it = env.begin(); it != env.end(); ++it) |
|||
{ |
|||
std::string s = it->first + "=" + it->second; |
|||
envp[i] = new char[s.size() + 1]; |
|||
std::strncpy(envp[i], s.c_str(), s.size() + 1); |
|||
++i; |
|||
} |
|||
envp[i] = 0; |
|||
|
|||
return envp; |
|||
} |
|||
|
|||
/**
|
|||
* Holds a mapping between native file descriptors and their corresponding |
|||
* pipes to set up communication between the parent and the %child process. |
|||
*/ |
|||
typedef std::map<int, stream_info> info_map; |
|||
|
|||
/**
|
|||
* Helper class to configure a POSIX %child. |
|||
* |
|||
* This helper class is used to hold all the attributes that configure a |
|||
* new POSIX %child process and to centralize all the actions needed to |
|||
* make them effective. |
|||
* |
|||
* All its fields are public for simplicity. It is only intended for |
|||
* internal use and it is heavily coupled with the Context |
|||
* implementations. |
|||
*/ |
|||
struct posix_setup |
|||
{ |
|||
/**
|
|||
* The work directory. |
|||
* |
|||
* This string specifies the directory in which the %child process |
|||
* starts execution. It cannot be empty. |
|||
*/ |
|||
std::string work_directory; |
|||
|
|||
/**
|
|||
* The chroot directory, if any. |
|||
* |
|||
* Specifies the directory in which the %child process is chrooted |
|||
* before execution. Empty if this feature is not desired. |
|||
*/ |
|||
std::string chroot; |
|||
|
|||
/**
|
|||
* The user credentials. |
|||
* |
|||
* UID that specifies the user credentials to use to run the %child |
|||
* process. Defaults to the current UID. |
|||
*/ |
|||
uid_t uid; |
|||
|
|||
/**
|
|||
* The effective user credentials. |
|||
* |
|||
* EUID that specifies the effective user credentials to use to run |
|||
* the %child process. Defaults to the current EUID. |
|||
*/ |
|||
uid_t euid; |
|||
|
|||
/**
|
|||
* The group credentials. |
|||
* |
|||
* GID that specifies the group credentials to use to run the %child |
|||
* process. Defaults to the current GID. |
|||
*/ |
|||
gid_t gid; |
|||
|
|||
/**
|
|||
* The effective group credentials. |
|||
* |
|||
* EGID that specifies the effective group credentials to use to run |
|||
* the %child process. Defaults to the current EGID. |
|||
*/ |
|||
gid_t egid; |
|||
|
|||
/**
|
|||
* Creates a new properties set. |
|||
* |
|||
* Creates a new object that has sensible default values for all the |
|||
* properties. |
|||
*/ |
|||
posix_setup() |
|||
: uid(::getuid()), |
|||
euid(::geteuid()), |
|||
gid(::getgid()), |
|||
egid(::getegid()) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Sets up the execution environment. |
|||
* |
|||
* Modifies the current execution environment (that of the %child) so |
|||
* that the properties become effective. |
|||
* |
|||
* \throw boost::system::system_error If any error ocurred during |
|||
* environment configuration. The child process should abort |
|||
* execution if this happens because its start conditions |
|||
* cannot be met. |
|||
*/ |
|||
void operator()() const |
|||
{ |
|||
if (!chroot.empty() && ::chroot(chroot.c_str()) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: chroot(2) failed")); |
|||
|
|||
if (gid != ::getgid() && ::setgid(gid) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setgid(2) failed")); |
|||
|
|||
if (egid != ::getegid() && ::setegid(egid) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setegid(2) failed")); |
|||
|
|||
if (uid != ::getuid() && ::setuid(uid) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setuid(2) failed")); |
|||
|
|||
if (euid != ::geteuid() && ::seteuid(euid) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: seteuid(2) failed")); |
|||
|
|||
BOOST_ASSERT(!work_directory.empty()); |
|||
if (::chdir(work_directory.c_str()) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: chdir(2) failed")); |
|||
} |
|||
}; |
|||
|
|||
/**
|
|||
* Configures child process' input streams. |
|||
* |
|||
* Sets up the current process' input streams to behave according to the |
|||
* information in the \a info map. \a closeflags is modified to reflect |
|||
* those descriptors that should not be closed because they where modified |
|||
* by the function. |
|||
* |
|||
* Modifies the current execution environment, so this should only be run |
|||
* on the child process after the fork(2) has happened. |
|||
* |
|||
* \throw boost::system::system_error If any error occurs during the |
|||
* configuration. |
|||
*/ |
|||
inline void setup_input(info_map &info, bool *closeflags, int maxdescs) |
|||
{ |
|||
for (info_map::iterator it = info.begin(); it != info.end(); ++it) |
|||
{ |
|||
int d = it->first; |
|||
stream_info &si = it->second; |
|||
|
|||
BOOST_ASSERT(d < maxdescs); |
|||
closeflags[d] = false; |
|||
|
|||
switch (si.type_) |
|||
{ |
|||
case stream_info::use_file: |
|||
{ |
|||
int fd = ::open(si.file_.c_str(), O_RDONLY); |
|||
if (fd == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::setup_input: open(2) of " + si.file_ + " failed")); |
|||
if (fd != d) |
|||
{ |
|||
file_handle h(fd); |
|||
h.posix_remap(d); |
|||
h.release(); |
|||
} |
|||
break; |
|||
} |
|||
case stream_info::use_handle: |
|||
{ |
|||
if (si.handle_.get() != d) |
|||
si.handle_.posix_remap(d); |
|||
break; |
|||
} |
|||
case stream_info::use_pipe: |
|||
{ |
|||
si.pipe_->wend().close(); |
|||
if (d != si.pipe_->rend().get()) |
|||
si.pipe_->rend().posix_remap(d); |
|||
break; |
|||
} |
|||
default: |
|||
{ |
|||
BOOST_ASSERT(si.type_ == stream_info::inherit); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Configures child process' output streams. |
|||
* |
|||
* Sets up the current process' output streams to behave according to the |
|||
* information in the \a info map. \a closeflags is modified to reflect |
|||
* those descriptors that should not be closed because they where |
|||
* modified by the function. |
|||
* |
|||
* Modifies the current execution environment, so this should only be run |
|||
* on the child process after the fork(2) has happened. |
|||
* |
|||
* \throw boost::system::system_error If any error occurs during the |
|||
* configuration. |
|||
*/ |
|||
inline void setup_output(info_map &info, bool *closeflags, int maxdescs) |
|||
{ |
|||
for (info_map::iterator it = info.begin(); it != info.end(); ++it) |
|||
{ |
|||
int d = it->first; |
|||
stream_info &si = it->second; |
|||
|
|||
BOOST_ASSERT(d < maxdescs); |
|||
closeflags[d] = false; |
|||
|
|||
switch (si.type_) |
|||
{ |
|||
case stream_info::redirect: |
|||
{ |
|||
break; |
|||
} |
|||
case stream_info::use_file: |
|||
{ |
|||
int fd = ::open(si.file_.c_str(), O_WRONLY); |
|||
if (fd == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::setup_output: open(2) of " + si.file_ + " failed")); |
|||
if (fd != d) |
|||
{ |
|||
file_handle h(fd); |
|||
h.posix_remap(d); |
|||
h.release(); |
|||
} |
|||
break; |
|||
} |
|||
case stream_info::use_handle: |
|||
{ |
|||
if (si.handle_.get() != d) |
|||
si.handle_.posix_remap(d); |
|||
break; |
|||
} |
|||
case stream_info::use_pipe: |
|||
{ |
|||
si.pipe_->rend().close(); |
|||
if (d != si.pipe_->wend().get()) |
|||
si.pipe_->wend().posix_remap(d); |
|||
break; |
|||
} |
|||
default: |
|||
{ |
|||
BOOST_ASSERT(si.type_ == stream_info::inherit); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
for (info_map::const_iterator it = info.begin(); it != info.end(); ++it) |
|||
{ |
|||
int d = it->first; |
|||
const stream_info &si = it->second; |
|||
|
|||
if (si.type_ == stream_info::redirect) |
|||
file_handle::posix_dup(si.desc_to_, d).release(); |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Starts a new child process in a POSIX operating system. |
|||
* |
|||
* This helper functions is provided to simplify the Context's task when |
|||
* it comes to starting up a new process in a POSIX operating system. |
|||
* The function hides all the details of the fork/exec pair of calls as |
|||
* well as all the setup of communication pipes and execution environment. |
|||
* |
|||
* \param exe The executable to spawn the child process. |
|||
* \param args The arguments for the executable. |
|||
* \param env The environment variables that the new child process |
|||
* receives. |
|||
* \param infoin A map describing all input file descriptors to be |
|||
* redirected. |
|||
* \param infoout A map describing all output file descriptors to be |
|||
* redirected. |
|||
* \param setup A helper object used to configure the child's execution |
|||
* environment. |
|||
* \return The new process' PID. The caller is responsible of creating |
|||
* an appropriate Child representation for it. |
|||
*/ |
|||
template <class Executable, class Arguments> |
|||
inline pid_t posix_start(const Executable &exe, const Arguments &args, const environment &env, info_map &infoin, info_map &infoout, const posix_setup &setup) |
|||
{ |
|||
pid_t pid = ::fork(); |
|||
if (pid == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_start: fork(2) failed")); |
|||
else if (pid == 0) |
|||
{ |
|||
#if defined(F_MAXFD) |
|||
int maxdescs = ::fcntl(-1, F_MAXFD, 0); |
|||
if (maxdescs == -1) |
|||
maxdescs = ::sysconf(_SC_OPEN_MAX); |
|||
#else |
|||
int maxdescs = ::sysconf(_SC_OPEN_MAX); |
|||
#endif |
|||
if (maxdescs == -1) |
|||
maxdescs = 1024; |
|||
try |
|||
{ |
|||
boost::scoped_array<bool> closeflags(new bool[maxdescs]); |
|||
for (int i = 0; i < maxdescs; ++i) |
|||
closeflags[i] = true; |
|||
|
|||
setup_input(infoin, closeflags.get(), maxdescs); |
|||
setup_output(infoout, closeflags.get(), maxdescs); |
|||
|
|||
for (int i = 0; i < maxdescs; ++i) |
|||
{ |
|||
if (closeflags[i]) |
|||
::close(i); |
|||
} |
|||
|
|||
setup(); |
|||
} |
|||
catch (const boost::system::system_error &e) |
|||
{ |
|||
::write(STDERR_FILENO, e.what(), std::strlen(e.what())); |
|||
::write(STDERR_FILENO, "\n", 1); |
|||
std::exit(EXIT_FAILURE); |
|||
} |
|||
|
|||
std::pair<std::size_t, char**> argcv = collection_to_posix_argv(args); |
|||
char **envp = environment_to_envp(env); |
|||
|
|||
::execve(exe.c_str(), argcv.second, envp); |
|||
boost::system::system_error e(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_start: execve(2) failed"); |
|||
|
|||
for (std::size_t i = 0; i < argcv.first; ++i) |
|||
delete[] argcv.second[i]; |
|||
delete[] argcv.second; |
|||
|
|||
for (std::size_t i = 0; i < env.size(); ++i) |
|||
delete[] envp[i]; |
|||
delete[] envp; |
|||
|
|||
::write(STDERR_FILENO, e.what(), std::strlen(e.what())); |
|||
::write(STDERR_FILENO, "\n", 1); |
|||
std::exit(EXIT_FAILURE); |
|||
} |
|||
|
|||
BOOST_ASSERT(pid > 0); |
|||
|
|||
for (info_map::iterator it = infoin.begin(); it != infoin.end(); ++it) |
|||
{ |
|||
stream_info &si = it->second; |
|||
if (si.type_ == stream_info::use_pipe) |
|||
si.pipe_->rend().close(); |
|||
} |
|||
|
|||
for (info_map::iterator it = infoout.begin(); it != infoout.end(); ++it) |
|||
{ |
|||
stream_info &si = it->second; |
|||
if (si.type_ == stream_info::use_pipe) |
|||
si.pipe_->wend().close(); |
|||
} |
|||
|
|||
return pid; |
|||
} |
|||
|
|||
/**
|
|||
* Locates a communication pipe and returns one of its endpoints. |
|||
* |
|||
* Given a \a info map, and a file descriptor \a desc, searches for its |
|||
* communicataion pipe in the map and returns one of its endpoints as |
|||
* indicated by the \a out flag. This is intended to be used by a |
|||
* parent process after a fork(2) call. |
|||
* |
|||
* \pre If the info map contains the given descriptor, it is configured |
|||
* to use a pipe. |
|||
* \post The info map does not contain the given descriptor. |
|||
* \return If the file descriptor is found in the map, returns the pipe's |
|||
* read end if out is true; otherwise its write end. If the |
|||
* descriptor is not found returns an invalid file handle. |
|||
*/ |
|||
inline file_handle posix_info_locate_pipe(info_map &info, int desc, bool out) |
|||
{ |
|||
file_handle fh; |
|||
|
|||
info_map::iterator it = info.find(desc); |
|||
if (it != info.end()) |
|||
{ |
|||
stream_info &si = it->second; |
|||
if (si.type_ == stream_info::use_pipe) |
|||
{ |
|||
fh = out ? si.pipe_->rend().release() : si.pipe_->wend().release(); |
|||
BOOST_ASSERT(fh.valid()); |
|||
} |
|||
info.erase(it); |
|||
} |
|||
|
|||
return fh; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,176 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/detail/stream_info.hpp |
|||
* |
|||
* Provides the definition of the stream_info structure. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_DETAIL_STREAM_INFO_HPP |
|||
#define BOOST_PROCESS_DETAIL_STREAM_INFO_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <unistd.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/stream_behavior.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/pipe.hpp> |
|||
#include <boost/optional.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <string> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
namespace detail { |
|||
|
|||
/**
|
|||
* Configuration data for a file descriptor. |
|||
* |
|||
* This convenience structure provides a compact way to pass information |
|||
* around on how to configure a file descriptor. It is a lower-level |
|||
* representation of stream_behavior, as it can hold the same information |
|||
* but in a way that can be used by the underlying operating system. |
|||
*/ |
|||
struct stream_info |
|||
{ |
|||
/**
|
|||
* Supported stream types. |
|||
*/ |
|||
enum type |
|||
{ |
|||
/**
|
|||
* Matches stream_behavior::close. |
|||
*/ |
|||
close, |
|||
|
|||
/**
|
|||
* Matches stream_behavior::inherit. |
|||
*/ |
|||
inherit, |
|||
|
|||
/**
|
|||
* Matches stream_behavior::redirect_to_stdout and |
|||
* stream_behavior::posix_redirect. |
|||
*/ |
|||
redirect, |
|||
|
|||
/**
|
|||
* Matches stream_behavior::silence. |
|||
*/ |
|||
use_file, |
|||
|
|||
/**
|
|||
* TODO: Matches nothing yet ... |
|||
*/ |
|||
use_handle, |
|||
|
|||
/**
|
|||
* Matches stream_behavior::capture. |
|||
*/ |
|||
use_pipe |
|||
}; |
|||
|
|||
/**
|
|||
* Stream type. |
|||
*/ |
|||
type type_; |
|||
|
|||
/**
|
|||
* Descriptor to use when stream type is set to \a redirect. |
|||
*/ |
|||
int desc_to_; |
|||
|
|||
/**
|
|||
* File to use when stream type is set to \a use_file. |
|||
*/ |
|||
std::string file_; |
|||
|
|||
/**
|
|||
* Handle to use when stream type is set to \a use_handle. |
|||
*/ |
|||
file_handle handle_; |
|||
|
|||
/**
|
|||
* Pipe to use when stream type is set to \a use_pipe. |
|||
*/ |
|||
boost::optional<pipe> pipe_; |
|||
|
|||
/**
|
|||
* Constructs a new stream_info object. |
|||
*/ |
|||
stream_info(const stream_behavior &sb, bool out) |
|||
{ |
|||
switch (sb.type_) |
|||
{ |
|||
case stream_behavior::close: |
|||
{ |
|||
type_ = close; |
|||
break; |
|||
} |
|||
case stream_behavior::inherit: |
|||
{ |
|||
type_ = inherit; |
|||
break; |
|||
} |
|||
case stream_behavior::redirect_to_stdout: |
|||
{ |
|||
type_ = redirect; |
|||
#if defined(BOOST_POSIX_API) |
|||
desc_to_ = STDOUT_FILENO; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
desc_to_ = 1; |
|||
#endif |
|||
break; |
|||
} |
|||
#if defined(BOOST_POSIX_API) |
|||
case stream_behavior::posix_redirect: |
|||
{ |
|||
type_ = redirect; |
|||
desc_to_ = sb.desc_to_; |
|||
break; |
|||
} |
|||
#endif |
|||
case stream_behavior::silence: |
|||
{ |
|||
type_ = use_file; |
|||
#if defined(BOOST_POSIX_API) |
|||
file_ = out ? "/dev/null" : "/dev/zero"; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
file_ = "NUL"; |
|||
#endif |
|||
break; |
|||
} |
|||
case stream_behavior::capture: |
|||
{ |
|||
type_ = use_pipe; |
|||
pipe_ = pipe(); |
|||
break; |
|||
} |
|||
default: |
|||
{ |
|||
BOOST_ASSERT(false); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,231 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/detail/systembuf.hpp |
|||
* |
|||
* Includes the declaration of the systembuf class. This file is for |
|||
* internal usage only and must not be included by the library user. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP |
|||
#define BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <sys/types.h> |
|||
# include <unistd.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/noncopyable.hpp> |
|||
#include <boost/scoped_array.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <streambuf> |
|||
#include <cstddef> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
class postream; |
|||
|
|||
namespace detail { |
|||
|
|||
/**
|
|||
* std::streambuf implementation for system file handles. |
|||
* |
|||
* systembuf provides a std::streambuf implementation for system file |
|||
* handles. Contrarywise to file_handle, this class does \b not take |
|||
* ownership of the native file handle; this should be taken care of |
|||
* somewhere else. |
|||
* |
|||
* This class follows the expected semantics of a std::streambuf object. |
|||
* However, it is not copyable to avoid introducing inconsistences with |
|||
* the on-disk file and the in-memory buffers. |
|||
*/ |
|||
class systembuf : public std::streambuf, public boost::noncopyable |
|||
{ |
|||
friend class ::boost::process::postream; |
|||
|
|||
public: |
|||
#if defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* Opaque name for the native handle type. |
|||
*/ |
|||
typedef NativeHandleType handle_type; |
|||
#elif defined(BOOST_POSIX_API) |
|||
typedef int handle_type; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
typedef HANDLE handle_type; |
|||
#endif |
|||
|
|||
/**
|
|||
* Constructs a new systembuf for the given file handle. |
|||
* |
|||
* This constructor creates a new systembuf object that reads or |
|||
* writes data from/to the \a h native file handle. This handle |
|||
* is \b not owned by the created systembuf object; the code |
|||
* should take care of it externally. |
|||
* |
|||
* This class buffers input and output; the buffer size may be |
|||
* tuned through the \a bufsize parameter, which defaults to 8192 |
|||
* bytes. |
|||
* |
|||
* \see pistream and postream |
|||
*/ |
|||
explicit systembuf(handle_type h, std::size_t bufsize = 8192) |
|||
: handle_(h), |
|||
bufsize_(bufsize), |
|||
read_buf_(new char[bufsize]), |
|||
write_buf_(new char[bufsize]) |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
BOOST_ASSERT(handle_ >= 0); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
BOOST_ASSERT(handle_ != INVALID_HANDLE_VALUE); |
|||
#endif |
|||
BOOST_ASSERT(bufsize_ > 0); |
|||
|
|||
setp(write_buf_.get(), write_buf_.get() + bufsize_); |
|||
} |
|||
|
|||
protected: |
|||
/**
|
|||
* Reads new data from the native file handle. |
|||
* |
|||
* This operation is called by input methods when there is no more |
|||
* data in the input buffer. The function fills the buffer with new |
|||
* data, if available. |
|||
* |
|||
* \pre All input positions are exhausted (gptr() >= egptr()). |
|||
* \post The input buffer has new data, if available. |
|||
* \returns traits_type::eof() if a read error occurrs or there are |
|||
* no more data to be read. Otherwise returns |
|||
* traits_type::to_int_type(*gptr()). |
|||
*/ |
|||
virtual int_type underflow() |
|||
{ |
|||
BOOST_ASSERT(gptr() >= egptr()); |
|||
|
|||
bool ok; |
|||
#if defined(BOOST_POSIX_API) |
|||
ssize_t cnt = ::read(handle_, read_buf_.get(), bufsize_); |
|||
ok = (cnt != -1 && cnt != 0); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
DWORD cnt; |
|||
BOOL res = ::ReadFile(handle_, read_buf_.get(), bufsize_, &cnt, NULL); |
|||
ok = (res && cnt > 0); |
|||
#endif |
|||
|
|||
if (!ok) |
|||
return traits_type::eof(); |
|||
else |
|||
{ |
|||
setg(read_buf_.get(), read_buf_.get(), read_buf_.get() + cnt); |
|||
return traits_type::to_int_type(*gptr()); |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Makes room in the write buffer for additional data. |
|||
* |
|||
* This operation is called by output methods when there is no more |
|||
* space in the output buffer to hold a new element. The function |
|||
* first flushes the buffer's contents to disk and then clears it to |
|||
* leave room for more characters. The given \a c character is |
|||
* stored at the beginning of the new space. |
|||
* |
|||
* \pre All output positions are exhausted (pptr() >= epptr()). |
|||
* \post The output buffer has more space if no errors occurred |
|||
* during the write to disk. |
|||
* \post *(pptr() - 1) is \a c. |
|||
* \returns traits_type::eof() if a write error occurrs. Otherwise |
|||
* returns traits_type::not_eof(c). |
|||
*/ |
|||
virtual int_type overflow(int c) |
|||
{ |
|||
BOOST_ASSERT(pptr() >= epptr()); |
|||
|
|||
if (sync() == -1) |
|||
return traits_type::eof(); |
|||
|
|||
if (!traits_type::eq_int_type(c, traits_type::eof())) |
|||
{ |
|||
traits_type::assign(*pptr(), c); |
|||
pbump(1); |
|||
} |
|||
|
|||
return traits_type::not_eof(c); |
|||
} |
|||
|
|||
/**
|
|||
* Flushes the output buffer to disk. |
|||
* |
|||
* Synchronizes the systembuf buffers with the contents of the file |
|||
* associated to this object through the native file handle. The |
|||
* output buffer is flushed to disk and cleared to leave new room |
|||
* for more data. |
|||
* |
|||
* \returns 0 on success, -1 if an error occurred. |
|||
*/ |
|||
virtual int sync() |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
ssize_t cnt = pptr() - pbase(); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
long cnt = pptr() - pbase(); |
|||
#endif |
|||
|
|||
bool ok; |
|||
#if defined(BOOST_POSIX_API) |
|||
ok = ::write(handle_, pbase(), cnt) == cnt; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
DWORD rcnt; |
|||
BOOL res = ::WriteFile(handle_, pbase(), cnt, &rcnt, NULL); |
|||
ok = (res && static_cast<long>(rcnt) == cnt); |
|||
#endif |
|||
|
|||
if (ok) |
|||
pbump(-cnt); |
|||
return ok ? 0 : -1; |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* Native file handle used by the systembuf object. |
|||
*/ |
|||
handle_type handle_; |
|||
|
|||
/**
|
|||
* Internal buffer size used during read and write operations. |
|||
*/ |
|||
std::size_t bufsize_; |
|||
|
|||
/**
|
|||
* Internal buffer used during read operations. |
|||
*/ |
|||
boost::scoped_array<char> read_buf_; |
|||
|
|||
/**
|
|||
* Internal buffer used during write operations. |
|||
*/ |
|||
boost::scoped_array<char> write_buf_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,355 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/detail/win32_ops.hpp |
|||
* |
|||
* Provides some convenience functions to start processes under |
|||
* Windows-compatible operating systems. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_DETAIL_WIN32_OPS_HPP |
|||
#define BOOST_PROCESS_DETAIL_WIN32_OPS_HPP |
|||
|
|||
#include <boost/process/environment.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/pipe.hpp> |
|||
#include <boost/process/detail/stream_info.hpp> |
|||
#include <boost/scoped_ptr.hpp> |
|||
#include <boost/shared_array.hpp> |
|||
#include <boost/scoped_array.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
#include <vector> |
|||
#include <string> |
|||
#include <cstddef> |
|||
#include <string.h> |
|||
#include <windows.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
namespace detail { |
|||
|
|||
/**
|
|||
* Converts the command line to a plain string. Converts the command line's |
|||
* list of arguments to the format expected by the \a lpCommandLine parameter |
|||
* in the CreateProcess() system call. |
|||
* |
|||
* This operation is only available on Windows systems. |
|||
* |
|||
* \return A dynamically allocated string holding the command line |
|||
* to be passed to the executable. It is returned in a |
|||
* shared_array object to ensure its release at some point. |
|||
*/ |
|||
template <class Arguments> |
|||
inline boost::shared_array<char> collection_to_win32_cmdline(const Arguments &args) |
|||
{ |
|||
typedef std::vector<std::string> arguments_t; |
|||
arguments_t args2; |
|||
typename Arguments::size_type i = 0; |
|||
std::size_t size = 0; |
|||
for (typename Arguments::const_iterator it = args.begin(); it != args.end(); ++it) |
|||
{ |
|||
std::string arg = *it; |
|||
|
|||
std::string::size_type pos = 0; |
|||
while ( (pos = arg.find('"', pos)) != std::string::npos) |
|||
{ |
|||
arg.replace(pos, 1, "\\\""); |
|||
pos += 2; |
|||
} |
|||
|
|||
if (arg.find(' ') != std::string::npos) |
|||
arg = '\"' + arg + '\"'; |
|||
|
|||
if (i++ != args.size() - 1) |
|||
arg += ' '; |
|||
|
|||
args2.push_back(arg); |
|||
size += arg.size() + 1; |
|||
} |
|||
|
|||
boost::shared_array<char> cmdline(new char[size]); |
|||
cmdline.get()[0] = '\0'; |
|||
for (arguments_t::size_type i = 0; i < args.size(); ++i) |
|||
#if defined(__CYGWIN__) |
|||
::strncat(cmdline.get(), args2[i].c_str(), args2[i].size()); |
|||
#else |
|||
::strcat_s(cmdline.get(), size, args2[i].c_str()); |
|||
#endif |
|||
|
|||
return cmdline; |
|||
} |
|||
|
|||
/**
|
|||
* Converts an environment to a string used by CreateProcess(). |
|||
* |
|||
* Converts the environment's contents to the format used by the |
|||
* CreateProcess() system call. The returned char* string is |
|||
* allocated in dynamic memory; the caller must free it when not |
|||
* used any more. This is enforced by the use of a shared pointer. |
|||
* |
|||
* \return A dynamically allocated char* string that represents |
|||
* the environment's content. This string is of the form |
|||
* var1=value1\\0var2=value2\\0\\0. |
|||
*/ |
|||
inline boost::shared_array<char> environment_to_win32_strings(const environment &env) |
|||
{ |
|||
boost::shared_array<char> envp; |
|||
|
|||
if (env.empty()) |
|||
{ |
|||
envp.reset(new char[2]); |
|||
::ZeroMemory(envp.get(), 2); |
|||
} |
|||
else |
|||
{ |
|||
std::string s; |
|||
for (environment::const_iterator it = env.begin(); it != env.end(); ++it) |
|||
{ |
|||
s += it->first + "=" + it->second; |
|||
s.push_back(0); |
|||
} |
|||
|
|||
envp.reset(new char[s.size() + 1]); |
|||
#if defined(__CYGWIN__) |
|||
::memcpy(envp.get(), s.c_str(), s.size() + 1); |
|||
#else |
|||
::memcpy_s(envp.get(), s.size() + 1, s.c_str(), s.size() + 1); |
|||
#endif |
|||
} |
|||
|
|||
return envp; |
|||
} |
|||
|
|||
/**
|
|||
* Helper class to configure a Win32 %child. |
|||
* |
|||
* This helper class is used to hold all the attributes that configure a |
|||
* new Win32 %child process. |
|||
* |
|||
* All its fields are public for simplicity. It is only intended for |
|||
* internal use and it is heavily coupled with the Context |
|||
* implementations. |
|||
*/ |
|||
struct win32_setup |
|||
{ |
|||
/**
|
|||
* The work directory. |
|||
* |
|||
* This string specifies the directory in which the %child process |
|||
* starts execution. It cannot be empty. |
|||
*/ |
|||
std::string work_directory; |
|||
|
|||
/**
|
|||
* The process startup properties. |
|||
* |
|||
* This Win32-specific object holds a list of properties that describe |
|||
* how the new process should be started. The \a STARTF_USESTDHANDLES |
|||
* flag should not be set in it because it is automatically configured |
|||
* by win32_start(). |
|||
*/ |
|||
STARTUPINFOA *startupinfo; |
|||
}; |
|||
|
|||
/**
|
|||
* Starts a new child process in a Win32 operating system. |
|||
* |
|||
* This helper functions is provided to simplify the Context's task when |
|||
* it comes to starting up a new process in a Win32 operating system. |
|||
* |
|||
* \param exe The executable to spawn the child process. |
|||
* \param args The arguments for the executable. |
|||
* \param env The environment variables that the new child process |
|||
* receives. |
|||
* \param infoin Information that describes stdin's behavior. |
|||
* \param infoout Information that describes stdout's behavior. |
|||
* \param infoerr Information that describes stderr's behavior. |
|||
* \param setup A helper object holding extra child information. |
|||
* \return The new process' information as returned by the CreateProcess() |
|||
* system call. The caller is responsible of creating an |
|||
* appropriate Child representation for it. |
|||
* \pre \a setup.startupinfo_ cannot have the \a STARTF_USESTDHANDLES set |
|||
* in the \a dwFlags field. |
|||
*/ |
|||
template <class Executable, class Arguments> |
|||
inline PROCESS_INFORMATION win32_start(const Executable &exe, const Arguments &args, const environment &env, stream_info &infoin, stream_info &infoout, stream_info &infoerr, const win32_setup &setup) |
|||
{ |
|||
file_handle chin, chout, cherr; |
|||
|
|||
BOOST_ASSERT(setup.startupinfo->cb >= sizeof(STARTUPINFOA)); |
|||
BOOST_ASSERT(!(setup.startupinfo->dwFlags & STARTF_USESTDHANDLES)); |
|||
|
|||
boost::scoped_ptr<STARTUPINFOA> si(new STARTUPINFOA); |
|||
::CopyMemory(si.get(), setup.startupinfo, sizeof(*setup.startupinfo)); |
|||
si->dwFlags |= STARTF_USESTDHANDLES; |
|||
|
|||
switch (infoin.type_) |
|||
{ |
|||
case stream_info::close: |
|||
{ |
|||
break; |
|||
} |
|||
case stream_info::inherit: |
|||
{ |
|||
chin = file_handle::win32_std(STD_INPUT_HANDLE, true); |
|||
break; |
|||
} |
|||
case stream_info::use_file: |
|||
{ |
|||
HANDLE h = ::CreateFileA(infoin.file_.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
|||
if (h == INVALID_HANDLE_VALUE) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateFile failed")); |
|||
chin = file_handle(h); |
|||
break; |
|||
} |
|||
case stream_info::use_handle: |
|||
{ |
|||
chin = infoin.handle_; |
|||
chin.win32_set_inheritable(true); |
|||
break; |
|||
} |
|||
case stream_info::use_pipe: |
|||
{ |
|||
infoin.pipe_->rend().win32_set_inheritable(true); |
|||
chin = infoin.pipe_->rend(); |
|||
break; |
|||
} |
|||
default: |
|||
{ |
|||
BOOST_ASSERT(false); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
si->hStdInput = chin.valid() ? chin.get() : INVALID_HANDLE_VALUE; |
|||
|
|||
switch (infoout.type_) |
|||
{ |
|||
case stream_info::close: |
|||
{ |
|||
break; |
|||
} |
|||
case stream_info::inherit: |
|||
{ |
|||
chout = file_handle::win32_std(STD_OUTPUT_HANDLE, true); |
|||
break; |
|||
} |
|||
case stream_info::use_file: |
|||
{ |
|||
HANDLE h = ::CreateFileA(infoout.file_.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
|||
if (h == INVALID_HANDLE_VALUE) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateFile failed")); |
|||
chout = file_handle(h); |
|||
break; |
|||
} |
|||
case stream_info::use_handle: |
|||
{ |
|||
chout = infoout.handle_; |
|||
chout.win32_set_inheritable(true); |
|||
break; |
|||
} |
|||
case stream_info::use_pipe: |
|||
{ |
|||
infoout.pipe_->wend().win32_set_inheritable(true); |
|||
chout = infoout.pipe_->wend(); |
|||
break; |
|||
} |
|||
default: |
|||
{ |
|||
BOOST_ASSERT(false); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
si->hStdOutput = chout.valid() ? chout.get() : INVALID_HANDLE_VALUE; |
|||
|
|||
switch (infoerr.type_) |
|||
{ |
|||
case stream_info::close: |
|||
{ |
|||
break; |
|||
} |
|||
case stream_info::inherit: |
|||
{ |
|||
cherr = file_handle::win32_std(STD_ERROR_HANDLE, true); |
|||
break; |
|||
} |
|||
case stream_info::redirect: |
|||
{ |
|||
BOOST_ASSERT(infoerr.desc_to_ == 1); |
|||
BOOST_ASSERT(chout.valid()); |
|||
cherr = file_handle::win32_dup(chout.get(), true); |
|||
break; |
|||
} |
|||
case stream_info::use_file: |
|||
{ |
|||
HANDLE h = ::CreateFileA(infoerr.file_.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
|||
if (h == INVALID_HANDLE_VALUE) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateFile failed")); |
|||
cherr = file_handle(h); |
|||
break; |
|||
} |
|||
case stream_info::use_handle: |
|||
{ |
|||
cherr = infoerr.handle_; |
|||
cherr.win32_set_inheritable(true); |
|||
break; |
|||
} |
|||
case stream_info::use_pipe: |
|||
{ |
|||
infoerr.pipe_->wend().win32_set_inheritable(true); |
|||
cherr = infoerr.pipe_->wend(); |
|||
break; |
|||
} |
|||
default: |
|||
{ |
|||
BOOST_ASSERT(false); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
si->hStdError = cherr.valid() ? cherr.get() : INVALID_HANDLE_VALUE; |
|||
|
|||
PROCESS_INFORMATION pi; |
|||
::ZeroMemory(&pi, sizeof(pi)); |
|||
|
|||
boost::shared_array<char> cmdline = collection_to_win32_cmdline(args); |
|||
|
|||
boost::scoped_array<char> executable(new char[exe.size() + 1]); |
|||
#if defined(__CYGWIN__) |
|||
::strcpy(executable.get(), exe.c_str()); |
|||
#else |
|||
::strcpy_s(executable.get(), exe.size() + 1, exe.c_str()); |
|||
#endif |
|||
|
|||
boost::scoped_array<char> workdir(new char[setup.work_directory.size() + 1]); |
|||
#if defined(__CYGWIN__) |
|||
::strcpy(workdir.get(), setup.work_directory.c_str()); |
|||
#else |
|||
::strcpy_s(workdir.get(), setup.work_directory.size() + 1, setup.work_directory.c_str()); |
|||
#endif |
|||
|
|||
boost::shared_array<char> envstrs = environment_to_win32_strings(env); |
|||
|
|||
if (!::CreateProcessA(executable.get(), cmdline.get(), NULL, NULL, TRUE, 0, envstrs.get(), workdir.get(), si.get(), &pi)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateProcess failed")); |
|||
|
|||
return pi; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,50 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/environment.hpp |
|||
* |
|||
* Includes the declaration of the environment class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_ENVIRONMENT_HPP |
|||
#define BOOST_PROCESS_ENVIRONMENT_HPP |
|||
|
|||
#include <string> |
|||
#include <map> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Representation of a process' environment variables. |
|||
* |
|||
* The environment is a map that stablishes an unidirectional |
|||
* association between variable names and their values and is |
|||
* represented by a string to string map. |
|||
* |
|||
* Variables may be defined to the empty string. Be aware that doing so |
|||
* is not portable: POSIX systems will treat such variables as being |
|||
* defined to the empty value, but Windows systems are not able to |
|||
* distinguish them from undefined variables. |
|||
* |
|||
* Neither POSIX nor Windows systems support a variable with no name. |
|||
* |
|||
* It is worthy to note that the environment is sorted alphabetically. |
|||
* This is provided for-free by the map container used to implement this |
|||
* type, and this behavior is required by Windows systems. |
|||
*/ |
|||
typedef std::map<std::string, std::string> environment; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,583 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/operations.hpp |
|||
* |
|||
* Provides miscellaneous free functions. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_OPERATIONS_HPP |
|||
#define BOOST_PROCESS_OPERATIONS_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <boost/process/detail/posix_ops.hpp> |
|||
# include <stdlib.h> |
|||
# include <unistd.h> |
|||
# if defined(__CYGWIN__) |
|||
# include <boost/scoped_array.hpp> |
|||
# include <sys/cygwin.h> |
|||
# endif |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <boost/process/detail/win32_ops.hpp> |
|||
# include <boost/algorithm/string/predicate.hpp> |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/child.hpp> |
|||
#include <boost/process/stream_behavior.hpp> |
|||
#include <boost/process/status.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/pipe.hpp> |
|||
#include <boost/process/detail/stream_info.hpp> |
|||
#include <boost/filesystem/path.hpp> |
|||
#include <boost/algorithm/string/predicate.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
#include <boost/scoped_array.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <string> |
|||
#include <vector> |
|||
#include <stdexcept> |
|||
#include <cstddef> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Locates the executable program \a file in all the directory components |
|||
* specified in \a path. If \a path is empty, the value of the PATH |
|||
* environment variable is used. |
|||
* |
|||
* The path variable is interpreted following the same conventions used |
|||
* to parse the PATH environment variable in the underlying platform. |
|||
* |
|||
* \throw boost::filesystem::filesystem_error If the file cannot be found |
|||
* in the path. |
|||
*/ |
|||
inline std::string find_executable_in_path(const std::string &file, std::string path = "") |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
BOOST_ASSERT(file.find('/') == std::string::npos); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos); |
|||
#endif |
|||
|
|||
std::string result; |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
if (path.empty()) |
|||
{ |
|||
const char *envpath = ::getenv("PATH"); |
|||
// if (!envpath)
|
|||
// boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: retrieving PATH failed", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
|
|||
|
|||
path = envpath; |
|||
} |
|||
BOOST_ASSERT(!path.empty()); |
|||
|
|||
#if defined(__CYGWIN__) |
|||
if (!::cygwin_posix_path_list_p(path.c_str())) |
|||
{ |
|||
int size = ::cygwin_win32_to_posix_path_list_buf_size(path.c_str()); |
|||
boost::scoped_array<char> cygpath(new char[size]); |
|||
::cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get()); |
|||
path = cygpath.get(); |
|||
} |
|||
#endif |
|||
|
|||
std::string::size_type pos1 = 0, pos2; |
|||
do |
|||
{ |
|||
pos2 = path.find(':', pos1); |
|||
std::string dir = (pos2 != std::string::npos) ? path.substr(pos1, pos2 - pos1) : path.substr(pos1); |
|||
std::string f = dir + (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file; |
|||
if (!::access(f.c_str(), X_OK)) |
|||
result = f; |
|||
pos1 = pos2 + 1; |
|||
} while (pos2 != std::string::npos && result.empty()); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
const char *exts[] = { "", ".exe", ".com", ".bat", NULL }; |
|||
const char **ext = exts; |
|||
while (*ext) |
|||
{ |
|||
char buf[MAX_PATH]; |
|||
char *dummy; |
|||
DWORD size = ::SearchPathA(path.empty() ? NULL : path.c_str(), file.c_str(), *ext, MAX_PATH, buf, &dummy); |
|||
BOOST_ASSERT(size < MAX_PATH); |
|||
if (size > 0) |
|||
{ |
|||
result = buf; |
|||
break; |
|||
} |
|||
++ext; |
|||
} |
|||
#endif |
|||
|
|||
// if (result.empty())
|
|||
// boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: file not found", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
|
|||
|
|||
return result; |
|||
} |
|||
|
|||
/**
|
|||
* Extracts the program name from a given executable. |
|||
* |
|||
* \return The program name. On Windows the program name |
|||
* is returned without a file extension. |
|||
*/ |
|||
inline std::string executable_to_progname(const std::string &exe) |
|||
{ |
|||
std::string::size_type begin = 0; |
|||
std::string::size_type end = std::string::npos; |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
std::string::size_type slash = exe.rfind('/'); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
std::string::size_type slash = exe.find_last_of("/\\"); |
|||
#endif |
|||
if (slash != std::string::npos) |
|||
begin = slash + 1; |
|||
|
|||
#if defined(BOOST_WINDOWS_API) |
|||
if (exe.size() > 4 && |
|||
(boost::algorithm::iends_with(exe, ".exe") || boost::algorithm::iends_with(exe, ".com") || boost::algorithm::iends_with(exe, ".bat"))) |
|||
end = exe.size() - 4; |
|||
#endif |
|||
|
|||
return exe.substr(begin, end - begin); |
|||
} |
|||
|
|||
/**
|
|||
* Starts a new child process. |
|||
* |
|||
* Launches a new process based on the binary image specified by the |
|||
* executable, the set of arguments to be passed to it and several |
|||
* parameters that describe the execution context. |
|||
* |
|||
* \remark Blocking remarks: This function may block if the device |
|||
* holding the executable blocks when loading the image. This might |
|||
* happen if, e.g., the binary is being loaded from a network share. |
|||
* |
|||
* \return A handle to the new child process. |
|||
*/ |
|||
template <class Executable, class Arguments, class Context> |
|||
inline child launch(const Executable &exe, const Arguments &args, const Context &ctx) |
|||
{ |
|||
detail::file_handle fhstdin, fhstdout, fhstderr; |
|||
|
|||
BOOST_ASSERT(!args.empty()); |
|||
BOOST_ASSERT(!ctx.work_directory.empty()); |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
detail::info_map infoin, infoout; |
|||
|
|||
if (ctx.stdin_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false); |
|||
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si)); |
|||
} |
|||
|
|||
if (ctx.stdout_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true); |
|||
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si)); |
|||
} |
|||
|
|||
if (ctx.stderr_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true); |
|||
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si)); |
|||
} |
|||
|
|||
detail::posix_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
child::id_type pid = detail::posix_start(exe, args, ctx.environment, infoin, infoout, s); |
|||
|
|||
if (ctx.stdin_behavior.get_type() == stream_behavior::capture) |
|||
{ |
|||
fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false); |
|||
BOOST_ASSERT(fhstdin.valid()); |
|||
} |
|||
|
|||
if (ctx.stdout_behavior.get_type() == stream_behavior::capture) |
|||
{ |
|||
fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true); |
|||
BOOST_ASSERT(fhstdout.valid()); |
|||
} |
|||
|
|||
if (ctx.stderr_behavior.get_type() == stream_behavior::capture) |
|||
{ |
|||
fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true); |
|||
BOOST_ASSERT(fhstderr.valid()); |
|||
} |
|||
|
|||
return child(pid, fhstdin, fhstdout, fhstderr); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
detail::stream_info behin = detail::stream_info(ctx.stdin_behavior, false); |
|||
if (behin.type_ == detail::stream_info::use_pipe) |
|||
fhstdin = behin.pipe_->wend(); |
|||
detail::stream_info behout = detail::stream_info(ctx.stdout_behavior, true); |
|||
if (behout.type_ == detail::stream_info::use_pipe) |
|||
fhstdout = behout.pipe_->rend(); |
|||
detail::stream_info beherr = detail::stream_info(ctx.stderr_behavior, true); |
|||
if (beherr.type_ == detail::stream_info::use_pipe) |
|||
fhstderr = beherr.pipe_->rend(); |
|||
|
|||
STARTUPINFOA si; |
|||
::ZeroMemory(&si, sizeof(si)); |
|||
si.cb = sizeof(si); |
|||
|
|||
detail::win32_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
s.startupinfo = &si; |
|||
|
|||
PROCESS_INFORMATION pi = detail::win32_start(exe, args, ctx.environment, behin, behout, beherr, s); |
|||
|
|||
if (!::CloseHandle(pi.hThread)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch: CloseHandle failed")); |
|||
|
|||
return child(pi.dwProcessId, fhstdin, fhstdout, fhstderr, detail::file_handle(pi.hProcess)); |
|||
#endif |
|||
} |
|||
|
|||
/**
|
|||
* Launches a shell-based command. |
|||
* |
|||
* Executes the given command through the default system shell. The |
|||
* command is subject to pattern expansion, redirection and pipelining. |
|||
* The shell is launched as described by the parameters in the execution |
|||
* context. |
|||
* |
|||
* This function behaves similarly to the system(3) system call. In a |
|||
* POSIX system, the command is fed to /bin/sh whereas under a Windows |
|||
* system, it is fed to cmd.exe. It is difficult to write portable |
|||
* commands as the first parameter, but this function comes in handy in |
|||
* multiple situations. |
|||
*/ |
|||
template <class Context> |
|||
inline child launch_shell(const std::string &command, const Context &ctx) |
|||
{ |
|||
std::string exe; |
|||
std::vector<std::string> args; |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
exe = "/bin/sh"; |
|||
args.push_back("sh"); |
|||
args.push_back("-c"); |
|||
args.push_back(command); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
char sysdir[MAX_PATH]; |
|||
UINT size = ::GetSystemDirectoryA(sysdir, sizeof(sysdir)); |
|||
if (!size) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_shell: GetWindowsDirectory failed")); |
|||
BOOST_ASSERT(size < MAX_PATH); |
|||
|
|||
exe = std::string(sysdir) + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe"); |
|||
args.push_back("cmd"); |
|||
args.push_back("/c"); |
|||
args.push_back(command); |
|||
#endif |
|||
|
|||
return launch(exe, args, ctx); |
|||
} |
|||
|
|||
/**
|
|||
* Launches a pipelined set of child processes. |
|||
* |
|||
* Given a collection of pipeline_entry objects describing how to launch |
|||
* a set of child processes, spawns them all and connects their inputs and |
|||
* outputs in a way that permits pipelined communication. |
|||
* |
|||
* \pre Let 1..N be the processes in the collection: the input behavior of |
|||
* the 2..N processes must be set to close_stream(). |
|||
* \pre Let 1..N be the processes in the collection: the output behavior of |
|||
* the 1..N-1 processes must be set to close_stream(). |
|||
* \remark Blocking remarks: This function may block if the device holding |
|||
* the executable of one of the entries blocks when loading the |
|||
* image. This might happen if, e.g., the binary is being loaded |
|||
* from a network share. |
|||
* \return A set of Child objects that represent all the processes spawned |
|||
* by this call. You should use wait_children() to wait for their |
|||
* termination. |
|||
*/ |
|||
template <class Entries> |
|||
inline children launch_pipeline(const Entries &entries) |
|||
{ |
|||
BOOST_ASSERT(entries.size() >= 2); |
|||
|
|||
children cs; |
|||
detail::file_handle fhinvalid; |
|||
|
|||
boost::scoped_array<detail::pipe> pipes(new detail::pipe[entries.size() - 1]); |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
{ |
|||
typename Entries::size_type i = 0; |
|||
const typename Entries::value_type::context_type &ctx = entries[i].context; |
|||
|
|||
detail::info_map infoin, infoout; |
|||
|
|||
if (ctx.stdin_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false); |
|||
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si)); |
|||
} |
|||
|
|||
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info si2(close_stream(), true); |
|||
si2.type_ = detail::stream_info::use_handle; |
|||
si2.handle_ = pipes[i].wend().release(); |
|||
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2)); |
|||
|
|||
if (ctx.stderr_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true); |
|||
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si)); |
|||
} |
|||
|
|||
detail::posix_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s); |
|||
|
|||
detail::file_handle fhstdin; |
|||
|
|||
if (ctx.stdin_behavior.get_type() == stream_behavior::capture) |
|||
{ |
|||
fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false); |
|||
BOOST_ASSERT(fhstdin.valid()); |
|||
} |
|||
|
|||
cs.push_back(child(pid, fhstdin, fhinvalid, fhinvalid)); |
|||
} |
|||
|
|||
for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i) |
|||
{ |
|||
const typename Entries::value_type::context_type &ctx = entries[i].context; |
|||
detail::info_map infoin, infoout; |
|||
|
|||
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info si1(close_stream(), false); |
|||
si1.type_ = detail::stream_info::use_handle; |
|||
si1.handle_ = pipes[i - 1].rend().release(); |
|||
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1)); |
|||
|
|||
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info si2(close_stream(), true); |
|||
si2.type_ = detail::stream_info::use_handle; |
|||
si2.handle_ = pipes[i].wend().release(); |
|||
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2)); |
|||
|
|||
if (ctx.stderr_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true); |
|||
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si)); |
|||
} |
|||
|
|||
detail::posix_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s); |
|||
|
|||
cs.push_back(child(pid, fhinvalid, fhinvalid, fhinvalid)); |
|||
} |
|||
|
|||
{ |
|||
typename Entries::size_type i = entries.size() - 1; |
|||
const typename Entries::value_type::context_type &ctx = entries[i].context; |
|||
|
|||
detail::info_map infoin, infoout; |
|||
|
|||
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info si1(close_stream(), false); |
|||
si1.type_ = detail::stream_info::use_handle; |
|||
si1.handle_ = pipes[i - 1].rend().release(); |
|||
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1)); |
|||
|
|||
if (ctx.stdout_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true); |
|||
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si)); |
|||
} |
|||
|
|||
if (ctx.stderr_behavior.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true); |
|||
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si)); |
|||
} |
|||
|
|||
detail::posix_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s); |
|||
|
|||
detail::file_handle fhstdout, fhstderr; |
|||
|
|||
if (ctx.stdout_behavior.get_type() == stream_behavior::capture) |
|||
{ |
|||
fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true); |
|||
BOOST_ASSERT(fhstdout.valid()); |
|||
} |
|||
|
|||
if (ctx.stderr_behavior.get_type() == stream_behavior::capture) |
|||
{ |
|||
fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true); |
|||
BOOST_ASSERT(fhstderr.valid()); |
|||
} |
|||
|
|||
cs.push_back(child(pid, fhinvalid, fhstdout, fhstderr)); |
|||
} |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
STARTUPINFOA si; |
|||
detail::win32_setup s; |
|||
s.startupinfo = &si; |
|||
|
|||
{ |
|||
typename Entries::size_type i = 0; |
|||
const typename Entries::value_type::context_type &ctx = entries[i].context; |
|||
|
|||
detail::stream_info sii = detail::stream_info(ctx.stdin_behavior, false); |
|||
detail::file_handle fhstdin; |
|||
if (sii.type_ == detail::stream_info::use_pipe) |
|||
fhstdin = sii.pipe_->wend(); |
|||
|
|||
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info sio(close_stream(), true); |
|||
sio.type_ = detail::stream_info::use_handle; |
|||
sio.handle_ = pipes[i].wend().release(); |
|||
|
|||
detail::stream_info sie(ctx.stderr_behavior, true); |
|||
|
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
::ZeroMemory(&si, sizeof(si)); |
|||
si.cb = sizeof(si); |
|||
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s); |
|||
|
|||
if (!::CloseHandle(pi.hThread)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed")); |
|||
|
|||
cs.push_back(child(pi.dwProcessId, fhstdin, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess))); |
|||
} |
|||
|
|||
for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i) |
|||
{ |
|||
const typename Entries::value_type::context_type &ctx = entries[i].context; |
|||
|
|||
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info sii(close_stream(), false); |
|||
sii.type_ = detail::stream_info::use_handle; |
|||
sii.handle_ = pipes[i - 1].rend().release(); |
|||
|
|||
detail::stream_info sio(close_stream(), true); |
|||
sio.type_ = detail::stream_info::use_handle; |
|||
sio.handle_ = pipes[i].wend().release(); |
|||
|
|||
detail::stream_info sie(ctx.stderr_behavior, true); |
|||
|
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
::ZeroMemory(&si, sizeof(si)); |
|||
si.cb = sizeof(si); |
|||
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s); |
|||
|
|||
if (!::CloseHandle(pi.hThread)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed")); |
|||
|
|||
cs.push_back(child(pi.dwProcessId, fhinvalid, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess))); |
|||
} |
|||
|
|||
{ |
|||
typename Entries::size_type i = entries.size() - 1; |
|||
const typename Entries::value_type::context_type &ctx = entries[i].context; |
|||
|
|||
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close); |
|||
detail::stream_info sii(close_stream(), false); |
|||
sii.type_ = detail::stream_info::use_handle; |
|||
sii.handle_ = pipes[i - 1].rend().release(); |
|||
|
|||
detail::file_handle fhstdout, fhstderr; |
|||
|
|||
detail::stream_info sio(ctx.stdout_behavior, true); |
|||
if (sio.type_ == detail::stream_info::use_pipe) |
|||
fhstdout = sio.pipe_->rend(); |
|||
detail::stream_info sie(ctx.stderr_behavior, true); |
|||
if (sie.type_ == detail::stream_info::use_pipe) |
|||
fhstderr = sie.pipe_->rend(); |
|||
|
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
::ZeroMemory(&si, sizeof(si)); |
|||
si.cb = sizeof(si); |
|||
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s); |
|||
|
|||
if (!::CloseHandle(pi.hThread)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed")); |
|||
|
|||
cs.push_back(child(pi.dwProcessId, fhinvalid, fhstdout, fhstderr, detail::file_handle(pi.hProcess))); |
|||
} |
|||
#endif |
|||
|
|||
return cs; |
|||
} |
|||
|
|||
/**
|
|||
* Waits for a collection of children to terminate. |
|||
* |
|||
* Given a collection of Child objects (such as std::vector<child> or |
|||
* the convenience children type), waits for the termination of all of |
|||
* them. |
|||
* |
|||
* \remark Blocking remarks: This call blocks if any of the children |
|||
* processes in the collection has not finalized execution and |
|||
* waits until it terminates. |
|||
* |
|||
* \return The exit status of the first process that returns an error |
|||
* code or, if all of them executed correctly, the exit status |
|||
* of the last process in the collection. |
|||
*/ |
|||
template <class Children> |
|||
inline status wait_children(Children &cs) |
|||
{ |
|||
BOOST_ASSERT(cs.size() >= 2); |
|||
|
|||
typename Children::iterator it = cs.begin(); |
|||
while (it != cs.end()) |
|||
{ |
|||
const status s = it->wait(); |
|||
++it; |
|||
if (it == cs.end()) |
|||
return s; |
|||
else if (!s.exited() || s.exit_status() != EXIT_SUCCESS) |
|||
{ |
|||
while (it != cs.end()) |
|||
{ |
|||
it->wait(); |
|||
++it; |
|||
} |
|||
return s; |
|||
} |
|||
} |
|||
|
|||
BOOST_ASSERT(false); |
|||
return cs.begin()->wait(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,116 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/pistream.hpp |
|||
* |
|||
* Includes the declaration of the pistream class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_PISTREAM_HPP |
|||
#define BOOST_PROCESS_PISTREAM_HPP |
|||
|
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/systembuf.hpp> |
|||
#include <boost/noncopyable.hpp> |
|||
#include <istream> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Child process' output stream. |
|||
* |
|||
* The pistream class represents an output communication channel with the |
|||
* child process. The child process writes data to this stream and the |
|||
* parent process can read it through the pistream object. In other |
|||
* words, from the child's point of view, the communication channel is an |
|||
* output one, but from the parent's point of view it is an input one; |
|||
* hence the confusing pistream name. |
|||
* |
|||
* pistream objects cannot be copied because they own the file handle |
|||
* they use to communicate with the child and because they buffer data |
|||
* that flows through the communication channel. |
|||
* |
|||
* A pistream object behaves as a std::istream stream in all senses. |
|||
* The class is only provided because it must provide a method to let |
|||
* the caller explicitly close the communication channel. |
|||
* |
|||
* \remark Blocking remarks: Functions that read data from this |
|||
* stream can block if the associated file handle blocks during |
|||
* the read. As this class is used to communicate with child |
|||
* processes through anonymous pipes, the most typical blocking |
|||
* condition happens when the child has no more data to send to |
|||
* the pipe's system buffer. When this happens, the buffer |
|||
* eventually empties and the system blocks until the writer |
|||
* generates some data. |
|||
*/ |
|||
class pistream : public std::istream, public boost::noncopyable |
|||
{ |
|||
public: |
|||
/**
|
|||
* Creates a new process' output stream. |
|||
* |
|||
* Given a file handle, this constructor creates a new pistream |
|||
* object that owns the given file handle \a fh. Ownership of |
|||
* \a fh is transferred to the created pistream object. |
|||
* |
|||
* \pre \a fh is valid. |
|||
* \post \a fh is invalid. |
|||
* \post The new pistream object owns \a fh. |
|||
*/ |
|||
explicit pistream(detail::file_handle &fh) |
|||
: std::istream(0), |
|||
handle_(fh), |
|||
systembuf_(handle_.get()) |
|||
{ |
|||
rdbuf(&systembuf_); |
|||
} |
|||
|
|||
/**
|
|||
* Returns the file handle managed by this stream. |
|||
* |
|||
* The file handle must not be copied. Copying invalidates |
|||
* the source file handle making the pistream unusable. |
|||
*/ |
|||
detail::file_handle &handle() |
|||
{ |
|||
return handle_; |
|||
} |
|||
|
|||
/**
|
|||
* Closes the file handle managed by this stream. |
|||
* |
|||
* Explicitly closes the file handle managed by this stream. This |
|||
* function can be used by the user to tell the child process it's |
|||
* not willing to receive more data. |
|||
*/ |
|||
void close() |
|||
{ |
|||
handle_.close(); |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* The file handle managed by this stream. |
|||
*/ |
|||
detail::file_handle handle_; |
|||
|
|||
/**
|
|||
* The systembuf object used to manage this stream's data. |
|||
*/ |
|||
detail::systembuf systembuf_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,178 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/posix_child.hpp |
|||
* |
|||
* Includes the declaration of the posix_child class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_POSIX_CHILD_HPP |
|||
#define BOOST_PROCESS_POSIX_CHILD_HPP |
|||
|
|||
#include <boost/process/child.hpp> |
|||
#include <boost/process/pistream.hpp> |
|||
#include <boost/process/postream.hpp> |
|||
#include <boost/process/detail/pipe.hpp> |
|||
#include <boost/process/detail/posix_ops.hpp> |
|||
#include <boost/process/detail/stream_info.hpp> |
|||
#include <boost/shared_ptr.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <map> |
|||
#include <unistd.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* POSIX implementation of the Child concept. |
|||
* |
|||
* The posix_child class implements the Child concept in a POSIX |
|||
* operating system. |
|||
* |
|||
* A POSIX child differs from a regular child (represented by a |
|||
* child object) in that it supports more than three communication |
|||
* channels with its parent. These channels are identified by regular |
|||
* file descriptors (integers). |
|||
* |
|||
* This class is built on top of the generic child so as to allow its |
|||
* trivial adoption. When a program is changed to use the POSIX-specific |
|||
* context (posix_context), it will most certainly need to migrate its |
|||
* use of the child class to posix_child. Doing so is only a matter of |
|||
* redefining the appropriate object and later using the required extra |
|||
* features: there should be no need to modify the existing code (e.g. |
|||
* method calls) in any other way. |
|||
*/ |
|||
class posix_child : public child |
|||
{ |
|||
public: |
|||
/**
|
|||
* Gets a reference to the child's input stream \a desc. |
|||
* |
|||
* Returns a reference to a postream object that represents one of |
|||
* the multiple input communication channels with the child process. |
|||
* The channel is identified by \a desc as seen from the child's |
|||
* point of view. The parent can use the returned stream to send |
|||
* data to the child. |
|||
* |
|||
* Giving this function the STDIN_FILENO constant (defined in |
|||
* unistd.h) is a synonym for the get_stdin() call inherited from |
|||
* child. |
|||
*/ |
|||
postream &get_input(int desc) const |
|||
{ |
|||
if (desc == STDIN_FILENO) |
|||
return posix_child::get_stdin(); |
|||
else |
|||
{ |
|||
input_map_t::const_iterator it = input_map_.find(desc); |
|||
BOOST_ASSERT(it != input_map_.end()); |
|||
return *it->second; |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Gets a reference to the child's output stream \a desc. |
|||
* |
|||
* Returns a reference to a pistream object that represents one of |
|||
* the multiple output communication channels with the child process. |
|||
* The channel is identified by \a desc as seen from the child's |
|||
* point of view. The parent can use the returned stream to retrieve |
|||
* data from the child. |
|||
* |
|||
* Giving this function the STDOUT_FILENO or STDERR_FILENO constants |
|||
* (both defined in unistd.h) are synonyms for the get_stdout() and |
|||
* get_stderr() calls inherited from child, respectively. |
|||
*/ |
|||
pistream &get_output(int desc) const |
|||
{ |
|||
if (desc == STDOUT_FILENO) |
|||
return posix_child::get_stdout(); |
|||
else if (desc == STDERR_FILENO) |
|||
return posix_child::get_stderr(); |
|||
else |
|||
{ |
|||
output_map_t::const_iterator it = output_map_.find(desc); |
|||
BOOST_ASSERT(it != output_map_.end()); |
|||
return *it->second; |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Constructs a new POSIX child object representing a just |
|||
* spawned child process. |
|||
* |
|||
* Creates a new child object that represents the just spawned process |
|||
* \a id. |
|||
* |
|||
* The \a infoin and \a infoout maps contain the pipes used to handle |
|||
* the redirections of the child process; at the moment, no other |
|||
* stream_info types are supported. If the launcher was asked to |
|||
* redirect any of the three standard flows, their pipes must be |
|||
* present in these maps. |
|||
*/ |
|||
posix_child(id_type id, detail::info_map &infoin, detail::info_map &infoout) |
|||
: child(id, |
|||
detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false), |
|||
detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true), |
|||
detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true)) |
|||
{ |
|||
for (detail::info_map::iterator it = infoin.begin(); it != infoin.end(); ++it) |
|||
{ |
|||
detail::stream_info &si = it->second; |
|||
if (si.type_ == detail::stream_info::use_pipe) |
|||
{ |
|||
BOOST_ASSERT(si.pipe_->wend().valid()); |
|||
boost::shared_ptr<postream> st(new postream(si.pipe_->wend())); |
|||
input_map_.insert(input_map_t::value_type(it->first, st)); |
|||
} |
|||
} |
|||
|
|||
for (detail::info_map::iterator it = infoout.begin(); it != infoout.end(); ++it) |
|||
{ |
|||
detail::stream_info &si = it->second; |
|||
if (si.type_ == detail::stream_info::use_pipe) |
|||
{ |
|||
BOOST_ASSERT(si.pipe_->rend().valid()); |
|||
boost::shared_ptr<pistream> st(new pistream(si.pipe_->rend())); |
|||
output_map_.insert(output_map_t::value_type(it->first, st)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* Maps child's file descriptors to postream objects. |
|||
*/ |
|||
typedef std::map<int, boost::shared_ptr<postream> > input_map_t; |
|||
|
|||
/**
|
|||
* Contains all relationships between child's input file |
|||
* descriptors and their corresponding postream objects. |
|||
*/ |
|||
input_map_t input_map_; |
|||
|
|||
/**
|
|||
* Maps child's file descriptors to pistream objects. |
|||
*/ |
|||
typedef std::map<int, boost::shared_ptr<pistream> > output_map_t; |
|||
|
|||
/**
|
|||
* Contains all relationships between child's output file |
|||
* descriptors and their corresponding pistream objects. |
|||
*/ |
|||
output_map_t output_map_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,118 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/posix_context.hpp |
|||
* |
|||
* Includes the declaration of the posix_context class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_POSIX_CONTEXT_HPP |
|||
#define BOOST_PROCESS_POSIX_CONTEXT_HPP |
|||
|
|||
#include <boost/process/context.hpp> |
|||
#include <boost/process/stream_behavior.hpp> |
|||
#include <map> |
|||
#include <string> |
|||
#include <unistd.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Holds a mapping between native file descriptors and their corresponding |
|||
* pipes to set up communication between the parent and the %child process. |
|||
*/ |
|||
typedef std::map<int, stream_behavior> behavior_map; |
|||
|
|||
template <class Path> |
|||
class posix_basic_context : public basic_work_directory_context<Path>, public environment_context |
|||
{ |
|||
public: |
|||
/**
|
|||
* Constructs a new POSIX-specific context. |
|||
* |
|||
* Constructs a new context. It is configured as follows: |
|||
* * All communcation channels with the child process are closed. |
|||
* * There are no channel mergings. |
|||
* * The initial work directory of the child processes is set to the |
|||
* current working directory. |
|||
* * The environment variables table is empty. |
|||
* * The credentials are the same as those of the current process. |
|||
*/ |
|||
posix_basic_context() |
|||
: uid(::getuid()), |
|||
euid(::geteuid()), |
|||
gid(::getgid()), |
|||
egid(::getegid()) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* List of input streams that will be redirected. |
|||
*/ |
|||
behavior_map input_behavior; |
|||
|
|||
/**
|
|||
* List of output streams that will be redirected. |
|||
*/ |
|||
behavior_map output_behavior; |
|||
|
|||
/**
|
|||
* The user credentials. |
|||
* |
|||
* UID that specifies the user credentials to use to run the %child |
|||
* process. Defaults to the current UID. |
|||
*/ |
|||
uid_t uid; |
|||
|
|||
/**
|
|||
* The effective user credentials. |
|||
* |
|||
* EUID that specifies the effective user credentials to use to run |
|||
* the %child process. Defaults to the current EUID. |
|||
*/ |
|||
uid_t euid; |
|||
|
|||
/**
|
|||
* The group credentials. |
|||
* |
|||
* GID that specifies the group credentials to use to run the %child |
|||
* process. Defaults to the current GID. |
|||
*/ |
|||
gid_t gid; |
|||
|
|||
/**
|
|||
* The effective group credentials. |
|||
* |
|||
* EGID that specifies the effective group credentials to use to run |
|||
* the %child process. Defaults to the current EGID. |
|||
*/ |
|||
gid_t egid; |
|||
|
|||
/**
|
|||
* The chroot directory, if any. |
|||
* |
|||
* Specifies the directory in which the %child process is chrooted |
|||
* before execution. Empty if this feature is not desired. |
|||
*/ |
|||
Path chroot; |
|||
}; |
|||
|
|||
/**
|
|||
* Default instantiation of posix_basic_context. |
|||
*/ |
|||
typedef posix_basic_context<std::string> posix_context; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,81 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/posix_operations.hpp |
|||
* |
|||
* Provides miscellaneous free functions specific to POSIX operating |
|||
* systems. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_POSIX_OPERATIONS_HPP |
|||
#define BOOST_PROCESS_POSIX_OPERATIONS_HPP |
|||
|
|||
#include <boost/process/posix_child.hpp> |
|||
#include <boost/process/posix_context.hpp> |
|||
#include <boost/process/stream_behavior.hpp> |
|||
#include <boost/process/detail/stream_info.hpp> |
|||
#include <boost/process/detail/posix_ops.hpp> |
|||
#include <sys/types.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Starts a new child process. |
|||
* |
|||
* Given an executable and the set of arguments passed to it, starts |
|||
* a new process with all the parameters configured in the context. |
|||
* The context can be reused afterwards to launch other different |
|||
* processes. |
|||
* |
|||
* \return A handle to the new child process. |
|||
*/ |
|||
template <class Executable, class Arguments, class Posix_Context> |
|||
inline posix_child posix_launch(const Executable &exe, const Arguments &args, const Posix_Context &ctx) |
|||
{ |
|||
detail::info_map input_info; |
|||
for (behavior_map::const_iterator it = ctx.input_behavior.begin(); it != ctx.input_behavior.end(); ++it) |
|||
{ |
|||
if (it->second.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(it->second, false); |
|||
input_info.insert(detail::info_map::value_type(it->first, si)); |
|||
} |
|||
} |
|||
|
|||
detail::info_map output_info; |
|||
for (behavior_map::const_iterator it = ctx.output_behavior.begin(); it != ctx.output_behavior.end(); ++it) |
|||
{ |
|||
if (it->second.get_type() != stream_behavior::close) |
|||
{ |
|||
detail::stream_info si = detail::stream_info(it->second, true); |
|||
output_info.insert(detail::info_map::value_type(it->first, si)); |
|||
} |
|||
} |
|||
|
|||
detail::posix_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
s.uid = ctx.uid; |
|||
s.euid = ctx.euid; |
|||
s.gid = ctx.gid; |
|||
s.egid = ctx.egid; |
|||
s.chroot = ctx.chroot; |
|||
|
|||
pid_t pid = detail::posix_start(exe, args, ctx.environment, input_info, output_info, s); |
|||
|
|||
return posix_child(pid, input_info, output_info); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,128 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/posix_status.hpp |
|||
* |
|||
* Includes the declaration of the posix_status class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_POSIX_STATUS_HPP |
|||
#define BOOST_PROCESS_POSIX_STATUS_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <sys/wait.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/status.hpp> |
|||
#include <boost/assert.hpp> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Status returned by a finalized %child process on a POSIX system. |
|||
* |
|||
* This class represents the %status returned by a child process after it |
|||
* has terminated. It contains some methods not available in the status |
|||
* class that provide information only available in POSIX systems. |
|||
*/ |
|||
class posix_status : public status |
|||
{ |
|||
public: |
|||
/**
|
|||
* Creates a posix_status object from an existing status object. |
|||
* |
|||
* Creates a new status object representing the exit status of a |
|||
* child process. The construction is done based on an existing |
|||
* status object which already contains all the available |
|||
* information: this class only provides controlled access to it. |
|||
*/ |
|||
posix_status(const status &s) |
|||
: status(s) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Returns whether the process exited due to an external |
|||
* signal. |
|||
*/ |
|||
bool signaled() const |
|||
{ |
|||
return WIFSIGNALED(flags_); |
|||
} |
|||
|
|||
/**
|
|||
* If signaled, returns the terminating signal code. |
|||
* |
|||
* If the process was signaled, returns the terminating signal code. |
|||
* |
|||
* \pre signaled() is true. |
|||
*/ |
|||
int term_signal() const |
|||
{ |
|||
BOOST_ASSERT(signaled()); |
|||
|
|||
return WTERMSIG(flags_); |
|||
} |
|||
|
|||
/**
|
|||
* If signaled, returns whether the process dumped core. |
|||
* |
|||
* If the process was signaled, returns whether the process |
|||
* produced a core dump. |
|||
* |
|||
* \pre signaled() is true. |
|||
*/ |
|||
bool dumped_core() const |
|||
{ |
|||
BOOST_ASSERT(signaled()); |
|||
|
|||
#ifdef WCOREDUMP |
|||
return WCOREDUMP(flags_); |
|||
#else |
|||
return false; |
|||
#endif |
|||
} |
|||
|
|||
/**
|
|||
* Returns whether the process was stopped by an external |
|||
* signal. |
|||
*/ |
|||
bool stopped() const |
|||
{ |
|||
return WIFSTOPPED(flags_); |
|||
} |
|||
|
|||
/**
|
|||
* If stopped, returns the stop signal code. |
|||
* |
|||
* If the process was stopped, returns the stop signal code. |
|||
* |
|||
* \pre stopped() is true. |
|||
*/ |
|||
int stop_signal() const |
|||
{ |
|||
BOOST_ASSERT(stopped()); |
|||
|
|||
return WSTOPSIG(flags_); |
|||
} |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,117 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/postream.hpp |
|||
* |
|||
* Includes the declaration of the postream class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_POSTREAM_HPP |
|||
#define BOOST_PROCESS_POSTREAM_HPP |
|||
|
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/systembuf.hpp> |
|||
#include <boost/noncopyable.hpp> |
|||
#include <ostream> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Child process' input stream. |
|||
* |
|||
* The postream class represents an input communication channel with the |
|||
* child process. The child process reads data from this stream and the |
|||
* parent process can write to it through the postream object. In other |
|||
* words, from the child's point of view, the communication channel is an |
|||
* input one, but from the parent's point of view it is an output one; |
|||
* hence the confusing postream name. |
|||
* |
|||
* postream objects cannot be copied because they own the file handle |
|||
* they use to communicate with the child and because they buffer data |
|||
* that flows through the communication channel. |
|||
* |
|||
* A postream object behaves as a std::ostream stream in all senses. |
|||
* The class is only provided because it must provide a method to let |
|||
* the caller explicitly close the communication channel. |
|||
* |
|||
* \remark Blocking remarks: Functions that write data to this |
|||
* stream can block if the associated file handle blocks during |
|||
* the write. As this class is used to communicate with child |
|||
* processes through anonymous pipes, the most typical blocking |
|||
* condition happens when the child is not processing the data |
|||
* in the pipe's system buffer. When this happens, the buffer |
|||
* eventually fills up and the system blocks until the reader |
|||
* consumes some data, leaving some new room. |
|||
*/ |
|||
class postream : public std::ostream, public boost::noncopyable |
|||
{ |
|||
public: |
|||
/**
|
|||
* Creates a new process' input stream. |
|||
* |
|||
* Given a file handle, this constructor creates a new postream |
|||
* object that owns the given file handle \a fh. Ownership of |
|||
* \a fh is transferred to the created postream object. |
|||
* |
|||
* \pre \a fh is valid. |
|||
* \post \a fh is invalid. |
|||
* \post The new postream object owns \a fh. |
|||
*/ |
|||
explicit postream(detail::file_handle &fh) |
|||
: std::ostream(0), |
|||
handle_(fh), |
|||
systembuf_(handle_.get()) |
|||
{ |
|||
rdbuf(&systembuf_); |
|||
} |
|||
|
|||
/**
|
|||
* Returns the file handle managed by this stream. |
|||
* |
|||
* The file handle must not be copied. Copying invalidates |
|||
* the source file handle making the postream unusable. |
|||
*/ |
|||
detail::file_handle &handle() |
|||
{ |
|||
return handle_; |
|||
} |
|||
|
|||
/**
|
|||
* Closes the file handle managed by this stream. |
|||
* |
|||
* Explicitly closes the file handle managed by this stream. This |
|||
* function can be used by the user to tell the child process there |
|||
* is no more data to send. |
|||
*/ |
|||
void close() |
|||
{ |
|||
systembuf_.sync(); |
|||
handle_.close(); |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* The file handle managed by this stream. |
|||
*/ |
|||
detail::file_handle handle_; |
|||
|
|||
/**
|
|||
* The systembuf object used to manage this stream's data. |
|||
*/ |
|||
detail::systembuf systembuf_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,130 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/process.hpp |
|||
* |
|||
* Includes the declaration of the process class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_PROCESS_HPP |
|||
#define BOOST_PROCESS_PROCESS_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <cerrno> |
|||
# include <signal.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <cstdlib> |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Generic implementation of the Process concept. |
|||
* |
|||
* The process class implements the Process concept in an operating system |
|||
* agnostic way. |
|||
*/ |
|||
class process |
|||
{ |
|||
public: |
|||
#if defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* Opaque name for the native process' identifier type. |
|||
* |
|||
* Each operating system identifies processes using a specific type. |
|||
* The \a id_type type is used to transparently refer to a process |
|||
* regardless of the operating system in which this class is used. |
|||
* |
|||
* This type is guaranteed to be an integral type on all supported |
|||
* platforms. |
|||
*/ |
|||
typedef NativeProcessId id_type; |
|||
#elif defined(BOOST_POSIX_API) |
|||
typedef pid_t id_type; |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
typedef DWORD id_type; |
|||
#endif |
|||
|
|||
/**
|
|||
* Constructs a new process object. |
|||
* |
|||
* Creates a new process object that represents a running process |
|||
* within the system. |
|||
*/ |
|||
process(id_type id) |
|||
: id_(id) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Returns the process' identifier. |
|||
*/ |
|||
id_type get_id() const |
|||
{ |
|||
return id_; |
|||
} |
|||
|
|||
/**
|
|||
* Terminates the process execution. |
|||
* |
|||
* Forces the termination of the process execution. Some platforms |
|||
* allow processes to ignore some external termination notifications |
|||
* or to capture them for a proper exit cleanup. You can set the |
|||
* \a force flag to true in them to force their termination regardless |
|||
* of any exit handler. |
|||
* |
|||
* After this call, accessing this object can be dangerous because the |
|||
* process identifier may have been reused by a different process. It |
|||
* might still be valid, though, if the process has refused to die. |
|||
* |
|||
* \throw boost::system::system_error If the system call used to |
|||
* terminate the process fails. |
|||
*/ |
|||
void terminate(bool force = false) const |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
if (::kill(id_, force ? SIGKILL : SIGTERM) == -1) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::process::terminate: kill(2) failed")); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
HANDLE h = ::OpenProcess(PROCESS_TERMINATE, FALSE, id_); |
|||
if (h == NULL) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::process::terminate: OpenProcess failed")); |
|||
if (!::TerminateProcess(h, EXIT_FAILURE)) |
|||
{ |
|||
::CloseHandle(h); |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::process::terminate: TerminateProcess failed")); |
|||
} |
|||
if (!::CloseHandle(h)) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::process::terminate: CloseHandle failed")); |
|||
#endif |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* The process' identifier. |
|||
*/ |
|||
id_type id_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,134 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/self.hpp |
|||
* |
|||
* Includes the declaration of the self class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_SELF_HPP |
|||
#define BOOST_PROCESS_SELF_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <unistd.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
# include <windows.h> |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/process/process.hpp> |
|||
#include <boost/process/environment.hpp> |
|||
#include <boost/system/system_error.hpp> |
|||
#include <boost/throw_exception.hpp> |
|||
#include <boost/noncopyable.hpp> |
|||
#include <string> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
extern "C" |
|||
{ |
|||
extern char **environ; |
|||
} |
|||
#endif |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Generic implementation of the Process concept. |
|||
* |
|||
* The self singleton provides access to the current process. |
|||
*/ |
|||
class self : public process, boost::noncopyable |
|||
{ |
|||
public: |
|||
/**
|
|||
* Returns the self instance representing the caller's process. |
|||
*/ |
|||
static self &get_instance() |
|||
{ |
|||
static self *instance = 0; |
|||
if (!instance) |
|||
instance = new self; |
|||
return *instance; |
|||
} |
|||
|
|||
/**
|
|||
* Returns the current environment. |
|||
* |
|||
* Returns the current process' environment variables. Modifying the |
|||
* returned object has no effect on the current environment. |
|||
*/ |
|||
static environment get_environment() |
|||
{ |
|||
environment e; |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
char **env = ::environ; |
|||
while (*env) |
|||
{ |
|||
std::string s = *env; |
|||
std::string::size_type pos = s.find('='); |
|||
e.insert(boost::process::environment::value_type(s.substr(0, pos), s.substr(pos + 1))); |
|||
++env; |
|||
} |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
#ifdef GetEnvironmentStrings |
|||
#undef GetEnvironmentStrings |
|||
#endif |
|||
char *environ = ::GetEnvironmentStrings(); |
|||
if (!environ) |
|||
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::self::get_environment: GetEnvironmentStrings failed")); |
|||
try |
|||
{ |
|||
char *env = environ; |
|||
while (*env) |
|||
{ |
|||
std::string s = env; |
|||
std::string::size_type pos = s.find('='); |
|||
e.insert(boost::process::environment::value_type(s.substr(0, pos), s.substr(pos + 1))); |
|||
env += s.size() + 1; |
|||
} |
|||
} |
|||
catch (...) |
|||
{ |
|||
::FreeEnvironmentStringsA(environ); |
|||
throw; |
|||
} |
|||
::FreeEnvironmentStringsA(environ); |
|||
#endif |
|||
|
|||
return e; |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* Constructs a new self object. |
|||
* |
|||
* Creates a new self object that represents the current process. |
|||
*/ |
|||
self() : |
|||
#if defined(BOOST_POSIX_API) |
|||
process(::getpid()) |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
process(::GetCurrentProcessId()) |
|||
#endif |
|||
{ |
|||
} |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,105 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/status.hpp |
|||
* |
|||
* Includes the declaration of the status class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_STATUS_HPP |
|||
#define BOOST_PROCESS_STATUS_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
#if defined(BOOST_POSIX_API) |
|||
# include <sys/wait.h> |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
#else |
|||
# error "Unsupported platform." |
|||
#endif |
|||
|
|||
#include <boost/assert.hpp> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
class child; |
|||
|
|||
/**
|
|||
* Status returned by a finalized %child process. |
|||
* |
|||
* This class represents the %status returned by a child process after it |
|||
* has terminated. It only provides that information available under all |
|||
* supported platforms. |
|||
* |
|||
* \see posix_status |
|||
*/ |
|||
class status |
|||
{ |
|||
friend class child; |
|||
|
|||
public: |
|||
/**
|
|||
* Returns whether the process exited gracefully or not. |
|||
*/ |
|||
bool exited() const |
|||
{ |
|||
#if defined(BOOST_POSIX_API) |
|||
return WIFEXITED(flags_); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
return true; |
|||
#endif |
|||
} |
|||
|
|||
/**
|
|||
* If exited, returns the exit code. |
|||
* |
|||
* If the process exited, returns the exit code it returned. |
|||
* |
|||
* \pre exited() is true. |
|||
*/ |
|||
int exit_status() const |
|||
{ |
|||
BOOST_ASSERT(exited()); |
|||
#if defined(BOOST_POSIX_API) |
|||
return WEXITSTATUS(flags_); |
|||
#elif defined(BOOST_WINDOWS_API) |
|||
return flags_; |
|||
#endif |
|||
} |
|||
|
|||
protected: |
|||
/**
|
|||
* Creates a status object based on exit information. |
|||
* |
|||
* Creates a new status object representing the exit status of a |
|||
* child process. |
|||
* |
|||
* \param flags In a POSIX system this parameter contains the |
|||
* flags returned by the ::waitpid() call. In a |
|||
* Windows system it contains the exit code only. |
|||
*/ |
|||
status(int flags) |
|||
: flags_(flags) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* OS-specific codification of exit status. |
|||
*/ |
|||
int flags_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,234 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/stream_behavior.hpp |
|||
* |
|||
* Includes the declaration of the stream_behavior class and associated |
|||
* free functions. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_STREAM_BEHAVIOR_HPP |
|||
#define BOOST_PROCESS_STREAM_BEHAVIOR_HPP |
|||
|
|||
#include <boost/process/config.hpp> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
namespace detail { |
|||
struct stream_info; |
|||
} |
|||
|
|||
/**
|
|||
* Describes the possible states for a communication stream. |
|||
*/ |
|||
class stream_behavior |
|||
{ |
|||
public: |
|||
friend struct detail::stream_info; |
|||
friend stream_behavior capture_stream(); |
|||
friend stream_behavior close_stream(); |
|||
friend stream_behavior inherit_stream(); |
|||
friend stream_behavior redirect_stream_to_stdout(); |
|||
friend stream_behavior silence_stream(); |
|||
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
friend stream_behavior posix_redirect_stream(int to); |
|||
#endif |
|||
|
|||
/**
|
|||
* Describes the possible states for a communication stream. |
|||
*/ |
|||
enum type |
|||
{ |
|||
/**
|
|||
* The child's stream is connected to the parent by using an |
|||
* anonymous pipe so that they can send and receive data to/from |
|||
* each other. |
|||
*/ |
|||
capture, |
|||
|
|||
/**
|
|||
* The child's stream is closed upon startup so that it will not |
|||
* have any access to it. |
|||
*/ |
|||
close, |
|||
|
|||
/**
|
|||
* The child's stream is connected to the same stream used by the |
|||
* parent. In other words, the corresponding parent's stream is |
|||
* inherited. |
|||
*/ |
|||
inherit, |
|||
|
|||
/**
|
|||
* The child's stream is connected to child's standard output. |
|||
* This is typically used when configuring the standard error |
|||
* stream. |
|||
*/ |
|||
redirect_to_stdout, |
|||
|
|||
/**
|
|||
* The child's stream is redirected to a null device so that its |
|||
* input is always zero or its output is lost, depending on |
|||
* whether the stream is an input or an output one. It is |
|||
* important to notice that this is different from close because |
|||
* the child is still able to write data. If we closed, e.g. |
|||
* stdout, the child might not work at all! |
|||
*/ |
|||
silence, |
|||
|
|||
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* The child redirects the stream's output to the provided file |
|||
* descriptor. This is a generalization of the portable |
|||
* redirect_to_stdout behavior. |
|||
*/ |
|||
posix_redirect |
|||
#endif |
|||
}; |
|||
|
|||
/**
|
|||
* Constructs a new stream behavior of type close. |
|||
* |
|||
* The public constructor creates a new stream behavior that defaults |
|||
* to the close behavior. In general, you will want to use the |
|||
* available free functions to construct a stream behavior (including |
|||
* the close one). |
|||
*/ |
|||
stream_behavior() |
|||
: type_(stream_behavior::close) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Returns this stream's behavior type. |
|||
*/ |
|||
type get_type() const |
|||
{ |
|||
return type_; |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* Constructs a new stream behavior of type \a t. |
|||
* |
|||
* Constructs a new stream behavior of type \a t. It is the |
|||
* responsibility of the caller to fill in any other attributes |
|||
* required by the specified type, if any. |
|||
*/ |
|||
stream_behavior(type t) |
|||
: type_(t) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* This stream's behavior type. |
|||
*/ |
|||
type type_; |
|||
|
|||
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* File descriptor the stream is redirected to. |
|||
*/ |
|||
int desc_to_; |
|||
#endif |
|||
}; |
|||
|
|||
/**
|
|||
* Creates a new stream_behavior of type stream_behavior::capture. |
|||
* |
|||
* Creates a new stream_behavior of type stream_behavior::capture, |
|||
* meaning that the child's stream is connected to the parent by using an |
|||
* anonymous pipe so that they can send and receive data to/from each |
|||
* other. |
|||
*/ |
|||
inline stream_behavior capture_stream() |
|||
{ |
|||
return stream_behavior(stream_behavior::capture); |
|||
} |
|||
|
|||
/**
|
|||
* Creates a new stream_behavior of type stream_behavior::close. |
|||
* |
|||
* Creates a new stream_behavior of type stream_behavior::close, |
|||
* meaning that the child's stream is closed upon startup so that it |
|||
* will not have any access to it. |
|||
*/ |
|||
inline stream_behavior close_stream() |
|||
{ |
|||
return stream_behavior(stream_behavior::close); |
|||
} |
|||
|
|||
/**
|
|||
* Creates a new stream_behavior of type stream_behavior::inherit. |
|||
* |
|||
* Creates a new stream_behavior of type stream_behavior::inherit, |
|||
* meaning that the child's stream is connected to the same stream used |
|||
* by the parent. In other words, the corresponding parent's stream is |
|||
* inherited. |
|||
*/ |
|||
inline stream_behavior inherit_stream() |
|||
{ |
|||
return stream_behavior(stream_behavior::inherit); |
|||
} |
|||
|
|||
/**
|
|||
* Creates a new stream_behavior of type |
|||
* stream_behavior::redirect_to_stdout. |
|||
* |
|||
* Creates a new stream_behavior of type |
|||
* stream_behavior::redirect_to_stdout, meaning that the child's stream is |
|||
* connected to child's standard output. This is typically used when |
|||
* configuring the standard error stream. |
|||
*/ |
|||
inline stream_behavior redirect_stream_to_stdout() |
|||
{ |
|||
return stream_behavior(stream_behavior::redirect_to_stdout); |
|||
} |
|||
|
|||
/**
|
|||
* Creates a new stream_behavior of type stream_behavior::silence. |
|||
* |
|||
* Creates a new stream_behavior of type stream_behavior::silence, |
|||
* meaning that the child's stream is redirected to a null device so that |
|||
* its input is always zero or its output is lost, depending on whether |
|||
* the stream is an input or an output one. It is important to notice |
|||
* that this is different from close because the child is still able to |
|||
* write data. If we closed, e.g. stdout, the child might not work at |
|||
* all! |
|||
*/ |
|||
inline stream_behavior silence_stream() |
|||
{ |
|||
return stream_behavior(stream_behavior::silence); |
|||
} |
|||
|
|||
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) |
|||
/**
|
|||
* Creates a new stream_behavior of type stream_behavior::posix_redirect. |
|||
* |
|||
* Creates a new stream_behavior of type stream_behavior::posix_redirect, |
|||
* meaning that the child's stream is redirected to the \a to child's |
|||
* file descriptor. This is a generalization of the portable |
|||
* redirect_stream_to_stdout() behavior. |
|||
*/ |
|||
inline stream_behavior posix_redirect_stream(int to) |
|||
{ |
|||
stream_behavior sb(stream_behavior::posix_redirect); |
|||
sb.desc_to_ = to; |
|||
return sb; |
|||
} |
|||
#endif |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,128 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/win32_child.hpp |
|||
* |
|||
* Includes the declaration of the win32_child class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_WIN32_CHILD_HPP |
|||
#define BOOST_PROCESS_WIN32_CHILD_HPP |
|||
|
|||
#include <boost/process/child.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <windows.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Windows implementation of the Child concept. |
|||
* |
|||
* The win32_child class implements the Child concept in a Windows |
|||
* operating system. |
|||
* |
|||
* A Windows child differs from a regular %child (represented by a |
|||
* child object) in that it holds additional information about a process. |
|||
* Aside from the standard handle, it also includes a handle to the |
|||
* process' main thread, together with identifiers to both entities. |
|||
* |
|||
* This class is built on top of the generic child so as to allow its |
|||
* trivial adoption. When a program is changed to use the |
|||
* Windows-specific context (win32_context), it will most certainly need |
|||
* to migrate its use of the child class to win32_child. Doing so is only |
|||
* a matter of redefining the appropriate object and later using the |
|||
* required extra features: there should be no need to modify the existing |
|||
* code (e.g. method calls) in any other way. |
|||
*/ |
|||
class win32_child : public child |
|||
{ |
|||
public: |
|||
/**
|
|||
* Constructs a new Windows child object representing a just |
|||
* spawned %child process. |
|||
* |
|||
* Creates a new %child object that represents the process described by |
|||
* the \a pi structure. |
|||
* |
|||
* The \a fhstdin, \a fhstdout and \a fhstderr parameters hold the |
|||
* communication streams used to interact with the %child process if |
|||
* the launcher configured redirections. See the parent class' |
|||
* constructor for more details on these. |
|||
* |
|||
* \see child |
|||
*/ |
|||
win32_child(const PROCESS_INFORMATION &pi, detail::file_handle fhstdin, detail::file_handle fhstdout, detail::file_handle fhstderr) |
|||
: child(pi.dwProcessId, fhstdin, fhstdout, fhstderr, pi.hProcess), |
|||
process_information_(pi), |
|||
thread_handle_(process_information_.hThread) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Returns the process handle. |
|||
* |
|||
* Returns a process-specific handle that can be used to access the |
|||
* process. This is the value of the \a hProcess field in the |
|||
* PROCESS_INFORMATION structure returned by CreateProcess(). |
|||
* |
|||
* \see get_id() |
|||
*/ |
|||
HANDLE get_handle() const |
|||
{ |
|||
return process_information_.hProcess; |
|||
} |
|||
|
|||
/**
|
|||
* Returns the primary thread's handle. |
|||
* |
|||
* Returns a handle to the primary thread of the new process. This is |
|||
* the value of the \a hThread field in the PROCESS_INFORMATION |
|||
* structure returned by CreateProcess(). |
|||
* |
|||
* \see get_primary_thread_id() |
|||
*/ |
|||
HANDLE get_primary_thread_handle() const |
|||
{ |
|||
return process_information_.hThread; |
|||
} |
|||
|
|||
/**
|
|||
* Returns the primary thread's identifier. |
|||
* |
|||
* Returns a system-wide value that identifies the process's primary |
|||
* thread. This is the value of the \a dwThreadId field in the |
|||
* PROCESS_INFORMATION structure returned by CreateProcess(). |
|||
* |
|||
* \see get_primary_thread_handle() |
|||
*/ |
|||
DWORD get_primary_thread_id() const |
|||
{ |
|||
return process_information_.dwThreadId; |
|||
} |
|||
|
|||
private: |
|||
/**
|
|||
* Windows-specific process information. |
|||
*/ |
|||
PROCESS_INFORMATION process_information_; |
|||
|
|||
/**
|
|||
* Thread handle owned by RAII object. |
|||
*/ |
|||
detail::file_handle thread_handle_; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,61 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/win_32context.hpp |
|||
* |
|||
* Includes the declaration of the win32_context class. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_WIN32_CONTEXT_HPP |
|||
#define BOOST_PROCESS_WIN32_CONTEXT_HPP |
|||
|
|||
#include <boost/process/context.hpp> |
|||
#include <string> |
|||
#include <windows.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Generic implementation of the Context concept. |
|||
* |
|||
* The context class implements the Context concept in an operating |
|||
* system agnostic way; it allows spawning new child processes using |
|||
* a single and common interface across different systems. |
|||
*/ |
|||
template <class String> |
|||
class win32_basic_context : public basic_context<String> |
|||
{ |
|||
public: |
|||
/**
|
|||
* Initializes the Win32-specific process startup information with NULL. |
|||
*/ |
|||
win32_basic_context() |
|||
: startupinfo(NULL) |
|||
{ |
|||
} |
|||
|
|||
/**
|
|||
* Win32-specific process startup information. |
|||
*/ |
|||
STARTUPINFOA *startupinfo; |
|||
}; |
|||
|
|||
/**
|
|||
* Default instantiation of win32_basic_context. |
|||
*/ |
|||
typedef win32_basic_context<std::string> win32_context; |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,77 @@ |
|||
//
|
|||
// Boost.Process
|
|||
// ~~~~~~~~~~~~~
|
|||
//
|
|||
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
|||
// Copyright (c) 2008, 2009 Boris Schaeling
|
|||
//
|
|||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|||
//
|
|||
|
|||
/**
|
|||
* \file boost/process/win32_operations.hpp |
|||
* |
|||
* Provides miscellaneous free functions specific to Windows operating |
|||
* systems. |
|||
*/ |
|||
|
|||
#ifndef BOOST_PROCESS_WIN32_OPERATIONS_HPP |
|||
#define BOOST_PROCESS_WIN32_OPERATIONS_HPP |
|||
|
|||
#include <boost/process/win32_child.hpp> |
|||
#include <boost/process/detail/file_handle.hpp> |
|||
#include <boost/process/detail/stream_info.hpp> |
|||
#include <boost/process/detail/win32_ops.hpp> |
|||
#include <windows.h> |
|||
|
|||
namespace boost { |
|||
namespace process { |
|||
|
|||
/**
|
|||
* Starts a new child process. |
|||
* |
|||
* Given an executable and the set of arguments passed to it, starts |
|||
* a new process with all the parameters configured in the context. |
|||
* The context can be reused afterwards to launch other different |
|||
* processes. |
|||
* |
|||
* \return A handle to the new child process. |
|||
*/ |
|||
template <class Executable, class Arguments, class Win32_Context> |
|||
inline win32_child win32_launch(const Executable &exe, const Arguments &args, const Win32_Context &ctx) |
|||
{ |
|||
detail::file_handle fhstdin, fhstdout, fhstderr; |
|||
|
|||
detail::stream_info behin = detail::stream_info(ctx.stdin_behavior, false); |
|||
if (behin.type_ == detail::stream_info::use_pipe) |
|||
fhstdin = behin.pipe_->wend(); |
|||
detail::stream_info behout = detail::stream_info(ctx.stdout_behavior, true); |
|||
if (behout.type_ == detail::stream_info::use_pipe) |
|||
fhstdout = behout.pipe_->rend(); |
|||
detail::stream_info beherr = detail::stream_info(ctx.stderr_behavior, true); |
|||
if (beherr.type_ == detail::stream_info::use_pipe) |
|||
fhstderr = beherr.pipe_->rend(); |
|||
|
|||
detail::win32_setup s; |
|||
s.work_directory = ctx.work_directory; |
|||
|
|||
STARTUPINFOA si; |
|||
if (!ctx.startupinfo) |
|||
{ |
|||
::ZeroMemory(&si, sizeof(si)); |
|||
si.cb = sizeof(si); |
|||
s.startupinfo = &si; |
|||
} |
|||
else |
|||
s.startupinfo = ctx.startupinfo; |
|||
|
|||
PROCESS_INFORMATION pi = detail::win32_start(exe, args, ctx.environment, behin, behout, beherr, s); |
|||
|
|||
return win32_child(pi, fhstdin, fhstdout, fhstderr); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,2 @@ |
|||
from compiler import * |
|||
from parser import * |
@ -0,0 +1,435 @@ |
|||
#!/usr/bin/python |
|||
import re |
|||
import sys |
|||
import os |
|||
from parser import parse |
|||
from opcodes import opcodes, reverse_opcodes |
|||
import json |
|||
|
|||
label_counter = [0] |
|||
|
|||
|
|||
def mklabel(prefix): |
|||
label_counter[0] += 1 |
|||
return prefix + str(label_counter[0] - 1) |
|||
|
|||
# All functions go here |
|||
# |
|||
# Entries go in a format: |
|||
# |
|||
# [ val, inputcount, outputcount, code ] |
|||
|
|||
funtable = [ |
|||
['+', 2, 1, ['<1>', '<0>', 'ADD']], |
|||
['-', 2, 1, ['<1>', '<0>', 'SUB']], |
|||
['*', 2, 1, ['<1>', '<0>', 'MUL']], |
|||
['/', 2, 1, ['<1>', '<0>', 'DIV']], |
|||
['^', 2, 1, ['<1>', '<0>', 'EXP']], |
|||
['%', 2, 1, ['<1>', '<0>', 'MOD']], |
|||
['#/', 2, 1, ['<1>', '<0>', 'SDIV']], |
|||
['#%', 2, 1, ['<1>', '<0>', 'SMOD']], |
|||
['==', 2, 1, ['<1>', '<0>', 'EQ']], |
|||
['<', 2, 1, ['<1>', '<0>', 'LT']], |
|||
['<=', 2, 1, ['<1>', '<0>', 'GT', 'NOT']], |
|||
['>', 2, 1, ['<1>', '<0>', 'GT']], |
|||
['>=', 2, 1, ['<1>', '<0>', 'LT', 'NOT']], |
|||
['!', 1, 1, ['<0>', 'NOT']], |
|||
['or', 2, 1, ['<1>', '<0>', 'DUP', 4, 'PC', |
|||
'ADD', 'JMPI', 'POP', 'SWAP', 'POP']], |
|||
['||', 2, 1, ['<1>', '<0>', 'DUP', 4, 'PC', |
|||
'ADD', 'JMPI', 'POP', 'SWAP', 'POP']], |
|||
['and', 2, 1, ['<1>', '<0>', 'NOT', 'NOT', 'MUL']], |
|||
['&&', 2, 1, ['<1>', '<0>', 'NOT', 'NOT', 'MUL']], |
|||
['xor', 2, 1, ['<1>', '<0>', 'XOR']], |
|||
['&', 2, 1, ['<1>', '<0>', 'AND']], |
|||
['|', 2, 1, ['<1>', '<0>', 'OR']], |
|||
['byte', 2, 1, ['<0>', '<1>', 'BYTE']], |
|||
# Word array methods |
|||
# arr, ind -> val |
|||
['access', 2, 1, ['<0>', '<1>', 32, 'MUL', 'ADD', 'MLOAD']], |
|||
# arr, ind, val |
|||
['arrset', 3, 0, ['<2>', '<0>', '<1>', 32, 'MUL', 'ADD', 'MSTORE']], |
|||
# len (32 MUL) len*32 (MSIZE) len*32 MSIZE (SWAP) MSIZE len*32 (MSIZE ADD) |
|||
# MSIZE MSIZE+len*32 (1) MSIZE MSIZE+len*32 1 (SWAP SUB) MSIZE |
|||
# MSIZE+len*32-1 (0 SWAP MSTORE8) MSIZE |
|||
['array', 1, 1, ['<0>', 32, 'MUL', 'MSIZE', 'SWAP', 'MSIZE', |
|||
'ADD', 1, 'SWAP', 'SUB', 0, 'SWAP', 'MSTORE8']], # len -> arr |
|||
# String array methods |
|||
# arr, ind -> val |
|||
['getch', 2, 1, ['<1>', '<0>', 'ADD', 'MLOAD', 255, 'AND']], |
|||
['setch', 3, 0, ['<2>', '<1>', '<0>', 'ADD', 'MSTORE']], # arr, ind, val |
|||
# len MSIZE (SWAP) MSIZE len (MSIZE ADD) MSIZE MSIZE+len (1) MSIZE |
|||
# MSIZE+len 1 (SWAP SUB) MSIZE MSIZE+len-1 (0 SWAP MSTORE8) MSIZE |
|||
['string', 1, 1, ['<0>', 'MSIZE', 'SWAP', 'MSIZE', 'ADD', |
|||
1, 'SWAP', 'SUB', 0, 'SWAP', 'MSTORE8']], # len -> arr |
|||
# ['send', 2, 1, [0,0,0,0,0,'<1>','<0>','CALL'] ], # to, value, 0, [] -> /dev/null |
|||
# to, value, gas, [] -> /dev/null |
|||
['send', 3, 1, [0, 0, 0, 0, '<2>', '<1>', '<0>', 'CALL']], |
|||
# MSIZE 0 MSIZE (MSTORE) MSIZE (DUP) MSIZE MSIZE (...) MSIZE MSIZE 32 <4> |
|||
# <3> <2> <1> <0> (CALL) MSIZE FLAG (POP) MSIZE (MLOAD) RESULT |
|||
['msg', 5, 1, ['MSIZE', 0, 'MSIZE', 'MSTORE', 'DUP', 32, 'SWAP', '<4>', 32, 'MUL', '<3>', |
|||
'<2>', '<1>', '<0>', 'CALL', 'POP', 'MLOAD']], # to, value, gas, data, datasize -> out32 |
|||
# <5> MSIZE (SWAP) MSIZE <5> (MSIZE SWAP) MSIZE MSIZE <5> (32 MUL) MSIZE MSIZE <5>*32 (DUP ADD 1 SWAP SUB) MSIZE MSIZE <6>*32 MEND (MSTORE8) MSIZE MSIZE <6>*32 (... CALL) |
|||
['msg', 6, 0, ['<5>', 'MSIZE', 'SWAP', 'MSIZE', 'SWAP', 32, 'MUL', 'DUP', 'ADD', 1, 'SWAP', 'SUB', 'MSTORE8', |
|||
'<4>', '<3>', '<2>', '<1>', '<0>', 'CALL', 'POP']], # to, value, gas, data, datasize, outsize -> out |
|||
# value, gas, data, datasize |
|||
['create', 4, 1, ['<3>', '<2>', '<1>', '<0>', 'CREATE']], |
|||
['sha3', 1, 1, [32, 'MSIZE', '<0>', 'MSIZE', 'MSTORE', 'SHA3']], |
|||
['sha3bytes', 1, 1, ['SHA3']], |
|||
['sload', 1, 1, ['<0>', 'SLOAD']], |
|||
['sstore', 2, 0, ['<1>', '<0>', 'SSTORE']], |
|||
['calldataload', 1, 1, ['<0>', 32, 'MUL', 'CALLDATALOAD']], |
|||
['id', 1, 1, ['<0>']], |
|||
# 0 MSIZE (SWAP) MSIZE 0 (MSIZE) MSIZE 0 MSIZE (MSTORE) MSIZE (32 SWAP) 32 |
|||
# MSIZE |
|||
# returns single value |
|||
['return', 1, 0, [ |
|||
'<0>', 'MSIZE', 'SWAP', 'MSIZE', 'MSTORE', 32, 'SWAP', 'RETURN']], |
|||
['return', 2, 0, ['<1>', 32, 'MUL', '<0>', 'RETURN']], |
|||
['suicide', 1, 0, ['<0>', 'SUICIDE']], |
|||
] |
|||
|
|||
# Pseudo-variables representing opcodes |
|||
pseudovars = { |
|||
'msg.datasize': [32, 'CALLDATASIZE', 'DIV'], |
|||
'msg.sender': ['CALLER'], |
|||
'msg.value': ['CALLVALUE'], |
|||
'tx.gasprice': ['GASPRICE'], |
|||
'tx.origin': ['ORIGIN'], |
|||
'tx.gas': ['GAS'], |
|||
'contract.balance': ['BALANCE'], |
|||
'block.prevhash': ['PREVHASH'], |
|||
'block.coinbase': ['COINBASE'], |
|||
'block.timestamp': ['TIMESTAMP'], |
|||
'block.number': ['NUMBER'], |
|||
'block.difficulty': ['DIFFICULTY'], |
|||
'block.gaslimit': ['GASLIMIT'], |
|||
} |
|||
|
|||
|
|||
# A set of methods for detecting raw values (numbers and strings) and |
|||
# converting them to integers |
|||
def frombytes(b): |
|||
return 0 if len(b) == 0 else ord(b[-1]) + 256 * frombytes(b[:-1]) |
|||
|
|||
|
|||
def fromhex(b): |
|||
return 0 if len(b) == 0 else '0123456789abcdef'.find(b[-1]) + 16 * fromhex(b[:-1]) |
|||
|
|||
|
|||
def is_numberlike(b): |
|||
if isinstance(b, (str, unicode)): |
|||
if re.match('^[0-9\-]*$', b): |
|||
return True |
|||
if b[0] in ["'", '"'] and b[-1] in ["'", '"'] and b[0] == b[-1]: |
|||
return True |
|||
if b[:2] == '0x': |
|||
return True |
|||
return False |
|||
|
|||
|
|||
def numberize(b): |
|||
if b[0] in ["'", '"']: |
|||
return frombytes(b[1:-1]) |
|||
elif b[:2] == '0x': |
|||
return fromhex(b[2:]) |
|||
else: |
|||
return int(b) |
|||
|
|||
|
|||
# Apply rewrite rules |
|||
def rewrite(ast): |
|||
if isinstance(ast, (str, unicode)): |
|||
return ast |
|||
elif ast[0] == 'set': |
|||
if ast[1][0] == 'access': |
|||
if ast[1][1] == 'contract.storage': |
|||
return ['sstore', rewrite(ast[1][2]), rewrite(ast[2])] |
|||
else: |
|||
return ['arrset', rewrite(ast[1][1]), rewrite(ast[1][2]), rewrite(ast[2])] |
|||
elif ast[0] == 'access': |
|||
if ast[1] == 'msg.data': |
|||
return ['calldataload', rewrite(ast[2])] |
|||
elif ast[1] == 'contract.storage': |
|||
return ['sload', rewrite(ast[2])] |
|||
elif ast[0] == 'array_lit': |
|||
tempvar = mklabel('_temp') |
|||
o1 = ['set', tempvar, ['array', str(len(ast[1:]))]] |
|||
of = map( |
|||
lambda i: ['arrset', tempvar, str(i), rewrite(ast[i + 1])], range(0, len(ast[1:]))) |
|||
return ['seq'] + [o1] + of + [tempvar] |
|||
return map(rewrite, ast) |
|||
|
|||
|
|||
# Main compiler code |
|||
def arity(ast): |
|||
if isinstance(ast, (str, unicode)): |
|||
return 1 |
|||
elif ast[0] == 'set': |
|||
return 0 |
|||
elif ast[0] == 'if': |
|||
return 0 |
|||
elif ast[0] == 'seq': |
|||
return 1 if len(ast[1:]) and arity(ast[-1]) == 1 else 0 |
|||
else: |
|||
for f in funtable: |
|||
if ast[0] == f[0]: |
|||
return f[2] |
|||
|
|||
|
|||
# Debugging |
|||
def print_wrapper(f): |
|||
def wrapper(*args, **kwargs): |
|||
print args[0] |
|||
u = f(*args, **kwargs) |
|||
print u |
|||
return u |
|||
return wrapper |
|||
|
|||
|
|||
# Right-hand-side expressions (ie. the normal kind) |
|||
#@print_wrapper |
|||
def compile_expr(ast, varhash, lc=[0]): |
|||
# Stop keyword |
|||
if ast == 'stop': |
|||
return ['STOP'] |
|||
# Literals |
|||
elif isinstance(ast, (str, unicode)): |
|||
if is_numberlike(ast): |
|||
return [numberize(ast)] |
|||
elif ast in pseudovars: |
|||
return pseudovars[ast] |
|||
else: |
|||
if ast not in varhash: |
|||
varhash[ast] = len(varhash) * 32 |
|||
return [varhash[ast], 'MLOAD'] |
|||
# Set (specifically, variables) |
|||
elif ast[0] == 'set': |
|||
if not isinstance(ast[1], (str, unicode)): |
|||
raise Exception("Cannot set the value of " + str(ast[1])) |
|||
elif ast[1] in pseudovars: |
|||
raise Exception("Cannot set a pseudovariable!") |
|||
else: |
|||
if ast[1] not in varhash: |
|||
varhash[ast[1]] = len(varhash) * 32 |
|||
return compile_expr(ast[2], varhash, lc) + [varhash[ast[1]], 'MSTORE'] |
|||
# If and if/else statements |
|||
elif ast[0] == 'if': |
|||
f = compile_expr(ast[1], varhash, lc) |
|||
g = compile_expr(ast[2], varhash, lc) |
|||
h = compile_expr(ast[3], varhash, lc) if len(ast) > 3 else None |
|||
label, ref = 'LABEL_' + str(lc[0]), 'REF_' + str(lc[0]) |
|||
lc[0] += 1 |
|||
label2, ref2 = 'LABEL_' + str(lc[0]), 'REF_' + str(lc[0]) |
|||
lc[0] += 1 |
|||
if h: |
|||
return f + ['NOT', ref2, 'JUMPI'] + g + [ref, 'JUMP', label2] + h + [label] |
|||
else: |
|||
return f + ['NOT', ref, 'JUMPI'] + g + [label] |
|||
# While loops |
|||
elif ast[0] == 'while': |
|||
f = compile_expr(ast[1], varhash, lc) |
|||
g = compile_expr(ast[2], varhash, lc) |
|||
beglab, begref = 'LABEL_' + str(lc[0]), 'REF_' + str(lc[0]) |
|||
endlab, endref = 'LABEL_' + str(lc[0] + 1), 'REF_' + str(lc[0] + 1) |
|||
lc[0] += 2 |
|||
return [beglab] + f + ['NOT', endref, 'JUMPI'] + g + [begref, 'JUMP', endlab] |
|||
# Seq |
|||
elif ast[0] == 'seq': |
|||
o = [] |
|||
for arg in ast[1:]: |
|||
o.extend(compile_expr(arg, varhash, lc)) |
|||
if arity(arg) == 1 and arg != ast[-1]: |
|||
o.append('POP') |
|||
return o |
|||
# Functions and operations |
|||
for f in funtable: |
|||
if ast[0] == f[0] and len(ast[1:]) == f[1]: |
|||
if reduce(lambda x, y: x * arity(y), ast[1:], 1): |
|||
iq = f[3][:] |
|||
oq = [] |
|||
while len(iq): |
|||
tok = iq.pop(0) |
|||
if isinstance(tok, (str, unicode)) and tok[0] == '<' and tok[-1] == '>': |
|||
oq.extend( |
|||
compile_expr(ast[1 + int(tok[1:-1])], varhash, lc)) |
|||
else: |
|||
oq.append(tok) |
|||
return oq |
|||
else: |
|||
raise Exception( |
|||
"Arity of argument mismatches for %s: %s" % (f[0], ast)) |
|||
raise Exception("invalid op: " + ast[0]) |
|||
|
|||
|
|||
# Stuff to add once to each program |
|||
def add_wrappers(c, varhash): |
|||
if len(varhash) and 'MSIZE' in c: |
|||
return [0, len(varhash) * 32 - 1, 'MSTORE8'] + c |
|||
else: |
|||
return c |
|||
|
|||
|
|||
# Optimizations |
|||
ops = { |
|||
'ADD': lambda x, y: (x + y) % 2 ** 256, |
|||
'MUL': lambda x, y: (x * y) % 2 ** 256, |
|||
'SUB': lambda x, y: (x - y) % 2 ** 256, |
|||
'DIV': lambda x, y: x / y, |
|||
'EXP': lambda x, y: pow(x, y, 2 ** 256), |
|||
'AND': lambda x, y: x & y, |
|||
'OR': lambda x, y: x | y, |
|||
'XOR': lambda x, y: x ^ y |
|||
} |
|||
|
|||
|
|||
def multipop(li, n): |
|||
if n > 0: |
|||
li.pop() |
|||
multipop(li, n - 1) |
|||
return li |
|||
|
|||
|
|||
def optimize(c): |
|||
iq = c[:] |
|||
oq = [] |
|||
while len(iq): |
|||
oq.append(iq.pop(0)) |
|||
if oq[-1] in ops and len(oq) >= 3: |
|||
if isinstance(oq[-2], (int, long)) and isinstance(oq[-3], (int, long)): |
|||
ntok = ops[oq[-1]](oq[-2], oq[-3]) |
|||
multipop(oq, 3).append(ntok) |
|||
if oq[-1] == 'NOT' and len(oq) >= 2 and oq[-2] == 'NOT': |
|||
multipop(oq, 2) |
|||
if oq[-1] == 'ADD' and len(oq) >= 3 and oq[-2] == 0: |
|||
multipop(oq, 2) |
|||
if oq[-1] in ['SUB', 'ADD'] and len(oq) >= 3 and oq[-3] == 0: |
|||
ntok = oq[-2] |
|||
multipop(oq, 3).append(ntok) |
|||
return oq |
|||
|
|||
|
|||
def compile_to_assembly(source, optimize_flag=1): |
|||
if isinstance(source, (str, unicode)): |
|||
source = parse(source) |
|||
varhash = {} |
|||
c1 = rewrite(source) |
|||
c2 = compile_expr(c1, varhash, [0]) |
|||
c3 = add_wrappers(c2, varhash) |
|||
c4 = optimize(c3) if optimize_flag else c3 |
|||
return c4 |
|||
|
|||
|
|||
def get_vars(source): |
|||
if isinstance(source, (str, unicode)): |
|||
source = parse(source) |
|||
varhash = {} |
|||
c1 = rewrite(source) |
|||
# fill varhash |
|||
compile_expr(c1, varhash, [0]) |
|||
return varhash |
|||
|
|||
|
|||
def log256(n): |
|||
return 0 if n == 0 else 1 + log256(n / 256) |
|||
|
|||
|
|||
def tobytearr(n, L): |
|||
return [] if L == 0 else tobytearr(n / 256, L - 1) + [n % 256] |
|||
|
|||
|
|||
# Dereference labels |
|||
def dereference(c): |
|||
iq = [x for x in c] |
|||
mq = [] |
|||
pos = 0 |
|||
labelmap = {} |
|||
while len(iq): |
|||
front = iq.pop(0) |
|||
if isinstance(front, str) and front[:6] == 'LABEL_': |
|||
labelmap[front[6:]] = pos |
|||
else: |
|||
mq.append(front) |
|||
if isinstance(front, str) and front[:4] == 'REF_': |
|||
pos += 5 |
|||
elif isinstance(front, (int, long)): |
|||
pos += 1 + max(1, log256(front)) |
|||
else: |
|||
pos += 1 |
|||
oq = [] |
|||
for m in mq: |
|||
if isinstance(m, str) and m[:4] == 'REF_': |
|||
oq.append('PUSH4') |
|||
oq.extend(tobytearr(labelmap[m[4:]], 4)) |
|||
elif isinstance(m, (int, long)): |
|||
L = max(1, log256(m)) |
|||
oq.append('PUSH' + str(L)) |
|||
oq.extend(tobytearr(m, L)) |
|||
else: |
|||
oq.append(m) |
|||
return oq |
|||
|
|||
|
|||
def serialize(source): |
|||
def numberize(arg): |
|||
if isinstance(arg, (int, long)): |
|||
return arg |
|||
elif arg in reverse_opcodes: |
|||
return reverse_opcodes[arg] |
|||
elif arg[:4] == 'PUSH': |
|||
return 95 + int(arg[4:]) |
|||
elif re.match('^[0-9]*$', arg): |
|||
return int(arg) |
|||
else: |
|||
raise Exception("Cannot serialize: " + str(arg)) |
|||
return ''.join(map(chr, map(numberize, source))) |
|||
|
|||
|
|||
def deserialize(source): |
|||
o = [] |
|||
i, j = 0, -1 |
|||
while i < len(source): |
|||
p = ord(source[i]) |
|||
if j >= 0: |
|||
o.append(p) |
|||
elif p >= 96 and p <= 127: |
|||
o.append('PUSH' + str(p - 95)) |
|||
else: |
|||
o.append(opcodes[p][0]) |
|||
if p >= 96 and p <= 127: |
|||
j = p - 95 |
|||
j -= 1 |
|||
i += 1 |
|||
return o |
|||
|
|||
|
|||
def assemble(asm): |
|||
return serialize(dereference(asm)) |
|||
|
|||
|
|||
def compile(source): |
|||
return assemble(compile_to_assembly(parse(source))) |
|||
|
|||
|
|||
def encode_datalist(vals): |
|||
def enc(n): |
|||
if isinstance(n, (int, long)): |
|||
return ''.join(map(chr, tobytearr(n, 32))) |
|||
elif isinstance(n, (str, unicode)) and len(n) == 40: |
|||
return '\x00' * 12 + n.decode('hex') |
|||
elif isinstance(n, (str, unicode)): |
|||
return '\x00' * (32 - len(n)) + n |
|||
elif n is True: |
|||
return 1 |
|||
elif n is False or n is None: |
|||
return 0 |
|||
return ''.join(map(enc, vals)) |
|||
|
|||
|
|||
def decode_datalist(arr): |
|||
if isinstance(arr, list): |
|||
arr = ''.join(map(chr, arr)) |
|||
o = [] |
|||
for i in range(0, len(arr), 32): |
|||
o.append(frombytes(arr[i:i + 32])) |
|||
return o |
@ -0,0 +1,56 @@ |
|||
opcodes = { |
|||
0x00: ['STOP', 0, 0], |
|||
0x01: ['ADD', 2, 1], |
|||
0x02: ['SUB', 2, 1], |
|||
0x03: ['MUL', 2, 1], |
|||
0x04: ['DIV', 2, 1], |
|||
0x05: ['SDIV', 2, 1], |
|||
0x06: ['MOD', 2, 1], |
|||
0x07: ['SMOD', 2, 1], |
|||
0x08: ['EXP', 2, 1], |
|||
0x09: ['NEG', 2, 1], |
|||
0x0a: ['LT', 2, 1], |
|||
0x0b: ['GT', 2, 1], |
|||
0x0c: ['EQ', 2, 1], |
|||
0x0d: ['NOT', 1, 1], |
|||
0x10: ['AND', 2, 1], |
|||
0x11: ['OR', 2, 1], |
|||
0x12: ['XOR', 2, 1], |
|||
0x13: ['BYTE', 2, 1], |
|||
0x20: ['SHA3', 2, 1], |
|||
0x30: ['ADDRESS', 0, 1], |
|||
0x31: ['BALANCE', 0, 1], |
|||
0x32: ['ORIGIN', 0, 1], |
|||
0x33: ['CALLER', 0, 1], |
|||
0x34: ['CALLVALUE', 0, 1], |
|||
0x35: ['CALLDATALOAD', 1, 1], |
|||
0x36: ['CALLDATASIZE', 0, 1], |
|||
0x37: ['GASPRICE', 0, 1], |
|||
0x40: ['PREVHASH', 0, 1], |
|||
0x41: ['COINBASE', 0, 1], |
|||
0x42: ['TIMESTAMP', 0, 1], |
|||
0x43: ['NUMBER', 0, 1], |
|||
0x44: ['DIFFICULTY', 0, 1], |
|||
0x45: ['GASLIMIT', 0, 1], |
|||
0x50: ['POP', 1, 0], |
|||
0x51: ['DUP', 1, 2], |
|||
0x52: ['SWAP', 2, 2], |
|||
0x53: ['MLOAD', 1, 1], |
|||
0x54: ['MSTORE', 2, 0], |
|||
0x55: ['MSTORE8', 2, 0], |
|||
0x56: ['SLOAD', 1, 1], |
|||
0x57: ['SSTORE', 2, 0], |
|||
0x58: ['JUMP', 1, 0], |
|||
0x59: ['JUMPI', 2, 0], |
|||
0x5a: ['PC', 0, 1], |
|||
0x5b: ['MSIZE', 0, 1], |
|||
0x5c: ['GAS', 0, 1], |
|||
0x60: ['PUSH', 0, 1], #encompasses 96...127 |
|||
0xf0: ['CREATE', 4, 1], |
|||
0xf1: ['CALL', 7, 1], |
|||
0xf2: ['RETURN', 2, 1], |
|||
0xff: ['SUICIDE', 1, 1], |
|||
} |
|||
reverse_opcodes = {} |
|||
for o in opcodes: |
|||
reverse_opcodes[opcodes[o][0]] = o |
@ -0,0 +1,290 @@ |
|||
import re |
|||
|
|||
# Number of spaces at the beginning of a line |
|||
def spaces(ln): |
|||
spaces = 0 |
|||
while spaces < len(ln) and ln[spaces] == ' ': spaces += 1 |
|||
return spaces |
|||
|
|||
# Main parse function |
|||
def parse(document): |
|||
return parse_lines(document.split('\n')) |
|||
|
|||
# Parse the statement-level structure, including if and while statements |
|||
def parse_lines(lns): |
|||
o = [] |
|||
i = 0 |
|||
while i < len(lns): |
|||
main = lns[i] |
|||
# Skip empty lines |
|||
if len(main.strip()) == 0: |
|||
i += 1 |
|||
continue |
|||
if spaces(main) > 0: |
|||
raise Exception("Line "+str(i)+" indented too much!") |
|||
# Grab the child block of an if statement |
|||
start_child_block = i+1 |
|||
indent = 99999999 |
|||
i += 1 |
|||
while i < len(lns): |
|||
sp = spaces(lns[i]) |
|||
if sp == 0: break |
|||
indent = min(sp,indent) |
|||
i += 1 |
|||
child_block = map(lambda x:x[indent:],lns[start_child_block:i]) |
|||
# Calls parse_line to parse the individual line |
|||
out = parse_line(main) |
|||
# Include the child block into the parsed expression |
|||
if out[0] in ['if', 'else', 'while', 'else if']: |
|||
if len(child_block) == 0: |
|||
raise Exception("If/else/while statement must have sub-clause! (%d)" % i) |
|||
else: |
|||
out.append(parse_lines(child_block)) |
|||
else: |
|||
if len(child_block) > 0: |
|||
raise Exception("Not an if/else/while statement, can't have sub-clause! (%d)" % i) |
|||
# This is somewhat complicated. Essentially, it converts something like |
|||
# "if c1 then s1 elif c2 then s2 elif c3 then s3 else s4" (with appropriate |
|||
# indenting) to [ if c1 s1 [ if c2 s2 [ if c3 s3 s4 ] ] ] |
|||
if out[0] == 'else if': |
|||
if len(o) == 0: raise Exception("Cannot start with else if! (%d)" % i) |
|||
u = o[-1] |
|||
while len(u) == 4: u = u[-1] |
|||
u.append(['if'] + out[1:]) |
|||
elif out[0] == 'else': |
|||
if len(o) == 0: raise Exception("Cannot start with else! (%d)" % i) |
|||
u = o[-1] |
|||
while len(u) == 4: u = u[-1] |
|||
u.append(out[1]) |
|||
else: |
|||
# Normal case: just add the parsed line to the output |
|||
o.append(out) |
|||
return o[0] if len(o) == 1 else ['seq'] + o |
|||
|
|||
# Tokens contain one or more chars of the same type, with a few exceptions |
|||
def chartype(c): |
|||
if c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.': |
|||
return 'alphanum' |
|||
elif c in '\t ': return 'space' |
|||
elif c in '()[]': return 'brack' |
|||
elif c == '"': return 'dquote' |
|||
elif c == "'": return 'squote' |
|||
else: return 'symb' |
|||
|
|||
# Converts something like "b[4] = x+2 > y*-3" to |
|||
# [ 'b', '[', '4', ']', '=', 'x', '+', '2', '>', 'y', '*', '-', '3' ] |
|||
def tokenize(ln): |
|||
tp = 'space' |
|||
i = 0 |
|||
o = [] |
|||
global cur |
|||
cur = '' |
|||
# Comments |
|||
if '//' in ln: ln = ln[:ln.find('//')] |
|||
# Finish a token and start a new one |
|||
def nxt(): |
|||
global cur |
|||
if len(cur) >= 2 and cur[-1] == '-': |
|||
o.extend([cur[:-1],'-']) |
|||
elif len(cur.strip()) >= 1: |
|||
o.append(cur) |
|||
cur = '' |
|||
# Main loop |
|||
while i < len(ln): |
|||
c = chartype(ln[i]) |
|||
# Inside a string |
|||
if tp == 'squote' or tp == "dquote": |
|||
if c == tp: |
|||
cur += ln[i] |
|||
nxt() |
|||
i += 1 |
|||
tp = 'space' |
|||
elif ln[i:i+2] == '\\x': |
|||
cur += ln[i+2:i+4].decode('hex') |
|||
i += 4 |
|||
elif ln[i:i+2] == '\\n': |
|||
cur += '\x0a' |
|||
i += 2 |
|||
elif ln[i] == '\\': |
|||
cur += ln[i+1] |
|||
i += 2 |
|||
else: |
|||
cur += ln[i] |
|||
i += 1 |
|||
# Not inside a string |
|||
else: |
|||
if c == 'brack' or tp == 'brack': nxt() |
|||
elif c == 'space': nxt() |
|||
elif c != 'space' and tp == 'space': nxt() |
|||
elif c == 'symb' and tp != 'symb': nxt() |
|||
elif c == 'alphanum' and tp == 'symb': nxt() |
|||
elif c == 'squote' or c == "dquote": nxt() |
|||
cur += ln[i] |
|||
tp = c |
|||
i += 1 |
|||
nxt() |
|||
if o[-1] in [':',':\n','\n']: o.pop() |
|||
if tp in ['squote','dquote']: raise Exception("Unclosed string: "+ln) |
|||
return o |
|||
|
|||
# This is the part where we turn a token list into an abstract syntax tree |
|||
precedence = { |
|||
'^': 1, |
|||
'*': 2, |
|||
'/': 3, |
|||
'%': 4, |
|||
'#/': 2, |
|||
'#%': 2, |
|||
'+': 3, |
|||
'-': 3, |
|||
'<': 4, |
|||
'<=': 4, |
|||
'>': 4, |
|||
'>=': 4, |
|||
'==': 5, |
|||
'and': 6, |
|||
'&&': 6, |
|||
'or': 7, |
|||
'||': 7, |
|||
'!': 0 |
|||
} |
|||
|
|||
def toktype(token): |
|||
if token is None: return None |
|||
elif token in ['(','[']: return 'left_paren' |
|||
elif token in [')',']']: return 'right_paren' |
|||
elif token == ',': return 'comma' |
|||
elif token == ':': return 'colon' |
|||
elif token in ['!']: return 'unary_operation' |
|||
elif not isinstance(token,str): return 'compound' |
|||
elif token in precedence: return 'binary_operation' |
|||
elif re.match('^[0-9a-z\-\.]*$',token): return 'alphanum' |
|||
elif token[0] in ['"',"'"] and token[0] == token[-1]: return 'alphanum' |
|||
else: raise Exception("Invalid token: "+token) |
|||
|
|||
# https://en.wikipedia.org/wiki/Shunting-yard_algorithm |
|||
# |
|||
# The algorithm works by maintaining three stacks: iq, stack, oq. Initially, |
|||
# the tokens are placed in order on the iq. Then, one by one, the tokens are |
|||
# processed. Values are moved immediately to the output queue. Operators are |
|||
# pushed onto the stack, but if an operator comes along with lower precendence |
|||
# then all operators on the stack with higher precedence are applied first. |
|||
# For example: |
|||
# iq = 2 + 3 * 5 + 7, stack = \, oq = \ |
|||
# iq = + 3 * 5 + 7, stack = \, oq = 2 |
|||
# iq = 3 * 5 + 7, stack = +, oq = 2 |
|||
# iq = * 5 + 7, stack = +, oq = 2 3 |
|||
# iq = 5 + 7, stack = + *, oq = 2 3 (since * > + in precedence) |
|||
# iq = + 7, stack = + *, oq = 2 3 5 |
|||
# iq = 7, stack = + +, oq = 2 [* 3 5] (since + > * in precedence) |
|||
# iq = \, stack = + +, oq = 2 [* 3 5] 7 |
|||
# iq = \, stack = +, oq = 2 [+ [* 3 5] 7] |
|||
# iq = \, stack = \, oq = [+ 2 [+ [* 3 5] 7] ] |
|||
# |
|||
# Functions, where function arguments begin with a left bracket preceded by |
|||
# the function name, are separated by commas, and end with a right bracket, |
|||
# are also included in this algorithm, though in a different way |
|||
def shunting_yard(tokens): |
|||
iq = [x for x in tokens] |
|||
oq = [] |
|||
stack = [] |
|||
prev,tok = None,None |
|||
# The normal Shunting-Yard algorithm simply converts expressions into |
|||
# reverse polish notation. Here, we try to be slightly more ambitious |
|||
# and build up the AST directly on the output queue |
|||
# eg. say oq = [ 2, 5, 3 ] and we add "+" then "*" |
|||
# we get first [ 2, [ +, 5, 3 ] ] then [ [ *, 2, [ +, 5, 3 ] ] ] |
|||
def popstack(stack,oq): |
|||
tok = stack.pop() |
|||
typ = toktype(tok) |
|||
if typ == 'binary_operation': |
|||
a,b = oq.pop(), oq.pop() |
|||
oq.append([ tok, b, a]) |
|||
elif typ == 'unary_operation': |
|||
a = oq.pop() |
|||
oq.append([ tok, a ]) |
|||
elif typ == 'right_paren': |
|||
args = [] |
|||
while toktype(oq[-1]) != 'left_paren': |
|||
args.insert(0,oq.pop()) |
|||
oq.pop() |
|||
if tok == ']' and args[0] != 'id': |
|||
oq.append(['access'] + args) |
|||
elif tok == ']': |
|||
oq.append(['array_lit'] + args[1:]) |
|||
elif tok == ')' and len(args) and args[0] != 'id': |
|||
oq.append(args) |
|||
else: |
|||
oq.append(args[1]) |
|||
# The main loop |
|||
while len(iq) > 0: |
|||
prev = tok |
|||
tok = iq.pop(0) |
|||
typ = toktype(tok) |
|||
if typ == 'alphanum': |
|||
oq.append(tok) |
|||
elif typ == 'left_paren': |
|||
# Handle cases like 3 * (2 + 5) by using 'id' as a default function |
|||
# name |
|||
if toktype(prev) != 'alphanum' and toktype(prev) != 'rparen': |
|||
oq.append('id') |
|||
# Say the statement is "... f(45...". At the start, we would have f |
|||
# as the last item on the oq. So we move it onto the stack, put the |
|||
# leftparen on the oq, and move f back to the stack, so we have ( f |
|||
# as the last two items on the oq. We also put the leftparen on the |
|||
# stack so we have a separator on both the stack and the oq |
|||
stack.append(oq.pop()) |
|||
oq.append(tok) |
|||
oq.append(stack.pop()) |
|||
stack.append(tok) |
|||
elif typ == 'right_paren': |
|||
# eg. f(27, 3 * 5 + 4). First, we finish evaluating all the |
|||
# arithmetic inside the last argument. Then, we run popstack |
|||
# to coalesce all of the function arguments sitting on the |
|||
# oq into a single list |
|||
while len(stack) and toktype(stack[-1]) != 'left_paren': |
|||
popstack(stack,oq) |
|||
if len(stack): |
|||
stack.pop() |
|||
stack.append(tok) |
|||
popstack(stack,oq) |
|||
elif typ == 'unary_operation' or typ == 'binary_operation': |
|||
# -5 -> 0 - 5 |
|||
if tok == '-' and toktype(prev) not in ['alphanum', 'right_paren']: |
|||
oq.append('0') |
|||
# Handle BEDMAS operator precedence |
|||
prec = precedence[tok] |
|||
while len(stack) and toktype(stack[-1]) == 'binary_operation' and precedence[stack[-1]] < prec: |
|||
popstack(stack,oq) |
|||
stack.append(tok) |
|||
elif typ == 'comma': |
|||
# Finish evaluating all arithmetic before the comma |
|||
while len(stack) and toktype(stack[-1]) != 'left_paren': |
|||
popstack(stack,oq) |
|||
elif typ == 'colon': |
|||
# Colon is like a comma except it stays in the argument list |
|||
while len(stack) and toktype(stack[-1]) != 'right_paren': |
|||
popstack(stack,oq) |
|||
oq.append(tok) |
|||
while len(stack): |
|||
popstack(stack,oq) |
|||
if len(oq) == 1: |
|||
return oq[0] |
|||
else: |
|||
raise Exception("Wrong number of items left on stack: "+str(oq)) |
|||
|
|||
def parse_line(ln): |
|||
tokens = tokenize(ln.strip()) |
|||
if tokens[0] == 'if' or tokens[0] == 'while': |
|||
return [ tokens[0], shunting_yard(tokens[1:]) ] |
|||
elif len(tokens) >= 2 and tokens[0] == 'else' and tokens[1] == 'if': |
|||
return [ 'else if', shunting_yard(tokens[2:]) ] |
|||
elif len(tokens) >= 1 and tokens[0] == 'elif': |
|||
return [ 'else if', shunting_yard(tokens[1:]) ] |
|||
elif len(tokens) == 1 and tokens[0] == 'else': |
|||
return [ 'else' ] |
|||
elif '=' in tokens: |
|||
eqplace = tokens.index('=') |
|||
return [ 'set', shunting_yard(tokens[:eqplace]), shunting_yard(tokens[eqplace+1:]) ] |
|||
else: |
|||
return shunting_yard(tokens) |
@ -0,0 +1,39 @@ |
|||
#!/usr/bin/python |
|||
import os |
|||
import sys |
|||
import re |
|||
import json |
|||
import serpent |
|||
|
|||
|
|||
def main(): |
|||
if len(sys.argv) == 1: |
|||
print "serpent <command> <arg1> <arg2> ..." |
|||
else: |
|||
cmd = sys.argv[2] if sys.argv[1][0] == '-' else sys.argv[1] |
|||
if sys.argv[1] == '-s': |
|||
args = re.findall(r'\S\S*', sys.stdin.read()) + sys.argv[3:] |
|||
elif sys.argv[1] == '-B': |
|||
args = [sys.stdin.read()] + sys.argv[3:] |
|||
elif sys.argv[1] == '-b': |
|||
args = [sys.stdin.read()[:-1]] + sys.argv[3:] # remove trailing \n |
|||
elif sys.argv[1] == '-j': |
|||
args = [json.loads(sys.stdin.read())] + sys.argv[3:] |
|||
elif sys.argv[1] == '-J': |
|||
args = json.loads(sys.stdin.read()) + sys.argv[3:] |
|||
elif sys.argv[1] == '-h': |
|||
args = [x.decode('hex') for x in sys.argv[3:]] |
|||
else: |
|||
cmd = sys.argv[1] |
|||
args = sys.argv[2:] |
|||
if args[0] in os.listdir(os.getcwd()): |
|||
args[0] = open(args[0]).read() |
|||
o = getattr(serpent, cmd)(*args) |
|||
if isinstance(o, (list, dict)): |
|||
print json.dumps(o) |
|||
else: |
|||
print o.encode('hex') |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
main() |
Loading…
Reference in new issue