Home | Libraries | People | FAQ | More |
There is only one header file you need to include to make use of all Boost.Process features:
#include <boost/process/all.hpp>
All examples in this tutorial assume this header file is included.
As many Boost libraries put the main header file in the boost directory you have another option:
#include <boost/process.hpp>
Please note that the Boost C++ community plans to remove header files from the boost directory. As newer libraries are expected to put all header files in a subdirectory like boost/process including all.hpp is recommended.
Boost.Process provides the free-standing function create_child()
to create child processes. You only need to pass the name of an executable
as a std::string
.
boost::process::create_child("hostname");
The function throws an exception of type boost::system::system_error
if no executable hostname is found in the current work directory. create_child()
does not automatically search
the filesystem for an executable. If you know the executable is not in the
current work directory you must add the path (either relative or absolute).
If you know the executable can be found in the directories of the environment
variable PATH you can call find_executable_in_path()
.
This function returns the absolute path to the executable. If it can't find
the executable in PATH it throws boost::filesystem::filesystem_error
.
std::string exe = boost::process::find_executable_in_path("hostname"); boost::process::create_child(exe);
Additionally you can pass command line options to create_child()
.
While the command line options must be of type std::string
you can store them in any STL container.
std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args = boost::assign::list_of("-?"); boost::process::create_child(exe, args);
If you like to run a command on the shell you can use another free-standing
function called shell()
.
boost::process::shell("mkdir test");
Apart from the executable name and command line options a third parameter
can be passed to create_child()
:
The context
class is used to configure the runtime context of a new process.
The context
class provides only a few member variables. You can set the process name
with context::process_name
, the work directory with
context::work_dir
and environment variables with
context::env
.
std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args; boost::process::context ctx; ctx.process_name = "hostname"; ctx.work_dir = "C:\\"; ctx.env.insert(std::make_pair("new_variable", "value")); boost::process::create_child(exe, args, ctx);
In practice you are probably more often using the member variable context::streams
which is used to configure child
standard streams.
The type of context::streams
is std::map<stream_id, boost::function<stream_ends (stream_type)>
>
. While stream_id
is an identifier
for a stream the value is a so-called stream behavior. These are functions
or function objects which describe how a child stream should be configured.
They return a pair of handles - a so-called stream_ends
- of which one
is used as a child stream and the other possibly to access the child stream
from the parent process (it's the other end of a pipe).
Boost.Process provides the following stream behavior classes:
It depends on these classes if and how a stream can be used by a child process.
As context::streams
is based on boost::function
you are free to define
new stream behaviors.
By default, standard streams are inherited. If you want to configure standard streams differently simply assign a stream behavior function.
std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args; boost::process::context ctx; ctx.streams[boost::process::stdout_id] = boost::process::behavior::null(); boost::process::create_child(exe, args, ctx);
In the code above the behavior of the standard output stream is changed.
Instead of inheriting the standard output stream data written to it will
be discarded. The stream is not closed by behavior::null
but any data is ignored.
The most interesting stream behaviors are behavior::pipe
, behavior::named_pipe
and behavior::async_pipe
. You use them to
exchange data between a parent and a child process.
std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args; boost::process::context ctx; ctx.streams[boost::process::stdout_id] = boost::process::behavior::pipe(); boost::process::child c = boost::process::create_child(exe, args, ctx); boost::process::pistream is(c.get_handle(boost::process::stdout_id)); std::cout << is.rdbuf() << std::flush;
If a parent process wants to communicate with a child process the return
value of create_child()
should not be discarded.
The return value of type child
provides access to the
newly created child process. The member function child::get_handle()
is provided to access the other ends of the child process' standard streams.
The handles returned by child::get_handle()
can be used to instantiate pistream
or postream
. These classes are
defined to support synchronous I/O. They are derived from std::istream
and std::ostream
and provide two additional member
functions close()
and handle()
.
For asynchronous I/O another class called pipe
has to be instantiated.
Just like the streams from the C++ standard pistream
and postream
only support synchronous
(or blocking) I/O. For asynchronous I/O Boost.Process provides the class
pipe
.
boost::asio::io_service ioservice; boost::array<char, 4096> buf; void handler(const boost::system::error_code &ec, std::size_t bytes_transferred); int main() { std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args; boost::process::context ctx; ctx.streams[boost::process::stdout_id] = boost::process::behavior::async_pipe(); boost::process::child c = boost::process::create_child(exe, args, ctx); boost::process::handle h = c.get_handle(boost::process::stdout_id); boost::process::pipe read_end(ioservice, h.release()); read_end.async_read_some(boost::asio::buffer(buf), handler); ioservice.run(); } void handler(const boost::system::error_code &ec, std::size_t bytes_transferred) { std::cout << std::string(buf.data(), bytes_transferred) << std::flush; }
Asynchronous I/O operations are based on Boost.Asio. As pipe
is an I/O object it must
be initialized with an I/O service object. In order to connect the I/O object
a handle must also be passed to the constructor of pipe
.
The handle
class is a wrapper for a file descriptor on POSIX and a HANDLE on Windows
systems. Boost.Process provides the class to avoid leaking handles as the
destructor closes them automatically. As both objects of type handle
and pipe
own handles handle::release()
must be called to pass ownership of the native handle from the handle
instance to the pipe
instance.
Once the pipe
instance has been created and setup asynchronous I/O operations can be used.
Note | |
---|---|
|
child
provides the member function child::wait()
to wait for a process to exit.
int main() { std::string exe = boost::process::find_executable_in_path("hostname"); boost::process::child c = boost::process::create_child(exe); int exit_code = c.wait(); #if defined(BOOST_POSIX_API) if (WIFEXITED(exit_code)) exit_code = WEXITSTATUS(exit_code); #endif std::cout << exit_code << std::endl; }
Caution | |
---|---|
On POSIX systems you must use the macros defined in |
child
does not provide any member function to wait asynchronously as Boost.Process
follows Boost.Asio guidelines. Instead status
must be used to create
an I/O object.
boost::asio::io_service ioservice; void end_wait(const boost::system::error_code &ec, int exit_code); int main() { std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args; boost::process::child c = boost::process::create_child(exe, args); boost::process::status s(ioservice); s.async_wait(c.get_id(), end_wait); ioservice.run(); } void end_wait(const boost::system::error_code &ec, int exit_code) { if (!ec) { #if defined(BOOST_POSIX_API) if (WIFEXITED(exit_code)) exit_code = WEXITSTATUS(exit_code); #endif std::cout << "exit code: " << exit_code << std::endl; } }
By passing a process ID to status::async_wait()
an asynchronous wait operation is initiated. As status::async_wait()
expects a process ID status
is loosely coupled with
other components of Boost.Process. Even if you don't create processes with
create_child()
you can use status
to wait asynchronously
as long as you have a process ID.
Note | |
---|---|
The type of the process ID is |
If you don't want to wait for a process to exit you can call child::terminate()
to terminate a process.
std::string exe = boost::process::find_executable_in_path("hostname"); boost::process::child c = boost::process::create_child(exe); c.terminate();
When creating a child process its standard streams must be configured. By default, standard streams are inherited. Boost.Process provides various stream behaviors to change the default configuration. However as developers might want to do something else with standard streams it's possible to create new stream behaviors.
The following code defines a stream behavior class redirect_to
which can be used to redirect streams.
class redirect_to { public: redirect_to(boost::process::handle h) : h_(h) { } boost::process::stream_ends operator()(boost::process::stream_type) const { return boost::process::stream_ends(h_, boost::process::handle()); } private: boost::process::handle h_; };
The following program uses redirect_to
to redirect the standard error stream of a child process to its standard
output stream.
boost::process::stream_ends forward(boost::process::stream_ends ends) { return ends; } int main() { std::string executable = boost::process::find_executable_in_path( "hostname"); std::vector<std::string> args; boost::process::stream_ends ends = boost::process::behavior::pipe() (boost::process::output_stream); boost::process::context ctx; ctx.streams[boost::process::stdout_id] = boost::bind(forward, ends); ctx.streams[boost::process::stderr_id] = redirect_to(ends.child); boost::process::child c = boost::process::create_child( executable, args, ctx); boost::process::pistream is(c.get_handle(boost::process::stdout_id)); std::cout << is.rdbuf() << std::flush; }
context::setup
is an extension point to setup a
child process calling platform specific functions. On POSIX systems context::setup
is invoked after fork()
has been called but before execve()
is called. context::setup
is executed within the child process
only.
First define a function which configures a new child process. The function
could for example call chroot()
to change the root directory.
void setup() { chroot("/tmp"); }
Then bind the function to context::setup
.
int main() { std::string exe = boost::process::find_executable_in_path("hostname"); std::vector<std::string> args; boost::process::context ctx; ctx.setup = &setup; boost::process::create_child(exe, args, ctx); }
Caution | |
---|---|
When you use |
While not really an extension point it is possible to configure more than the standard streams for child processes on POSIX platforms.
int main() { std::string exe = boost::process::find_executable_in_path("dbus-daemon"); std::vector<std::string> args = boost::assign::list_of("--fork") ("--session")("--print-address=3")("--print-pid=4"); boost::process::context ctx; ctx.streams[3] = boost::process::behavior::pipe( boost::process::output_stream); ctx.streams[4] = boost::process::behavior::pipe( boost::process::output_stream); boost::process::child c = boost::process::create_child(exe, args, ctx); boost::process::pistream isaddress(c.get_handle(3)); std::cout << isaddress.rdbuf() << std::endl; boost::process::pistream ispid(c.get_handle(4)); std::cout << ispid.rdbuf() << std::endl; }
This example uses the D-Bus message bus daemon to write data to the file descriptors 3 and 4. As on POSIX platforms a child's streams can be identified via integers configuring arbitrary many streams is possible.
context::setup
can be used to configure a child
process using Windows specific options. On Windows a reference to a STARTUPINFOA
object is passed to context::setup
.
void setup(STARTUPINFOA &sainfo) { sainfo.dwFlags |= STARTF_USEPOSITION | STARTF_USESIZE; sainfo.dwX = 0; sainfo.dwY = 0; sainfo.dwXSize = 640; sainfo.dwYSize = 480; }
Here window position and size are set (as a hint) and Notepad is launched.
int main() { std::string exe = boost::process::find_executable_in_path("notepad"); std::vector<std::string> args; boost::process::context ctx; ctx.setup = &setup; boost::process::create_child(exe, args, ctx); }