Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

User Guide

Header files
Creating child processes
Configuring runtime contexts
Communicating with child processes
Asynchronous I/O
Waiting for a process to exit
Terminating a process
Creating new stream behaviors
POSIX extensions
Windows extensions

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] Note

pipe is a typedef for boost::asio::posix::stream_descriptor on POSIX and boost::asio::windows::stream_handle on Windows systems.

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] Caution

On POSIX systems you must use the macros defined in sys/wait.h to interpret exit codes.

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] Note

The type of the process ID is boost::process::pid_type. It is a typedef for pid_t on POSIX and DWORD on Windows platforms.

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] Caution

When you use context::setup in a multi-threaded application you must not call any function which is not async-signal-safe. Calling non-async-signal-safe functions between fork() and execve() is undefined behavior in multi-threaded applications. If you must call non-async-signal-safe functions prior to calling execve() (because you want to change the root directory with chroot() for example) you must create a singlethreaded wrapper process which can safely call non-async-signal-safe function. For more information have a look at the man page of fork().

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); 
} 


PrevUpHomeNext