Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Platform-specific usage

The POSIX platform
The Windows platforms

The usage chapter focused on explaining all features provided by Boost.Process that are available under all supported platforms. However, those features may be too limited when portability across different platforms is not a determining factor; in such cases, you will want to access the full power of the underlying operating system to manage processes. Boost.Process lets you do so through specialized classes - even if they are not enough for your use case, you can always design your own.

This chapter describes all platform-specific features available in Boost.Process. Keep in mind that using them will lower your application's portability.

The POSIX platform

Interprocess communication

As we saw earlier in the usage chapter, all platforms supported by Boost.Process provide three communication channels to each process. Although these are enough in almost all use cases, some applications can take advantage of more data flows. For example, they may support multiple input streams so that external processes can feed in different types of data, or emit messages through more than two output streams to clearly separate their purpose.

The POSIX platform allows the configuration of more than three communication channels thanks to the way fork works: any file descriptor can be used to connect two different processes through an anonymous pipe. Boost.Process can take advantage of such feature and configure more than three data flows by using the specialized posix_launcher and posix_child classes, both based on the generic implementations.

Before continuing, it is interesting to remember that POSIX systems identify communication channels with plain integers because they are regular file descriptors. The three standard communication channels are typically attached to fixed file descriptors and the cstdlib standard header provides constants to refer to them; these constants shall be used instead of the integer values to achieve maximum portability and clarity.

Table1.1.Standard channels on POSIX operating systems
Channel Symbolic constant Typical value
Standard input STDIN_FILENO 0
Standard output STDOUT_FILENO 1
Standard error STDERR_FILENO 2

The POSIX context class posix_context provides two additional properties that allow the user to add and specify the behavior of non-standard file descriptors; these are input_behavior and output_behavior. The former is used to configure a child's input streams and the latter output ones.

Once the streams are configured and the child process is running, the caller accesses the child's streams as it did with the generic child. However, non-standard streams are only available through two additional methods: get_input and get_output.

These methods can be seen as general cases of those provided by the generic child. The following table illustrates the equivalences:

Table1.2.POSIX child specific methods and equivalent portable methods
Portable method Equivalent to
boost::process::child::get_stdin() boost::process::posix_child::get_input(STDIN_FILENO)
boost::process::child::get_stdout() boost::process::posix_child::get_output(STDOUT_FILENO)
boost::process::child::get_stderr() boost::process::posix_child::get_output(STDERR_FILENO)

The following example program illustrates the use of these functions. It uses the D-BUS daemon application because it allows to print useful information to two non-standard streams (3 and 4 in the code below). The example utility captures these messages and provides them to the user:

// 
// Boost.Process 
// ~~~~~~~~~~~~~ 
// 
// Copyright (c) 2006, 2007 Julio M. Merino Vidal 
// Copyright (c) 2008 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) 
// 

#include <boost/process.hpp> 
#if defined(BOOST_POSIX_API) 
#include <string> 
#include <vector> 
#include <iostream> 
#include <cstdlib> 
#include <unistd.h> 

namespace bp = ::boost::process; 

bp::posix_child start_child() 
{ 
    std::string exec = bp::find_executable_in_path("dbus-daemon"); 

    std::vector<std::string> args; 
    args.push_back("--fork"); 
    args.push_back("--session"); 
    args.push_back("--print-address=3"); 
    args.push_back("--print-pid=4"); 

    bp::posix_context ctx; 
    ctx.output_behavior.insert(bp::behavior_map::value_type(STDOUT_FILENO, bp::inherit_stream())); 
    ctx.output_behavior.insert(bp::behavior_map::value_type(STDERR_FILENO, bp::inherit_stream())); 
    ctx.output_behavior.insert(bp::behavior_map::value_type(3, bp::capture_stream())); 
    ctx.output_behavior.insert(bp::behavior_map::value_type(4, bp::capture_stream())); 

    return bp::posix_launch(exec, args, ctx); 
} 

int main() 
{ 
    try 
    { 
        bp::posix_child c = start_child(); 

        std::string address; 
        pid_t pid; 
        c.get_output(3) >> address; 
        c.get_output(4) >> pid; 

        bp::status s = c.wait(); 
        if (s.exited()) 
        { 
            if (s.exit_status() == EXIT_SUCCESS) 
            { 
                std::cout << "D-BUS daemon's address is: " << address << std::endl; 
                std::cout << "D-BUS daemon's PID is: " << pid << std::endl; 
            } 
            else 
                std::cout << "D-BUS daemon returned error condition: " << s.exit_status() << std::endl; 
        } 
        else 
            std::cout << "D-BUS daemon terminated abnormally" << std::endl; 

        return s.exited() ? s.exit_status() : EXIT_FAILURE; 
    } 
    catch (boost::filesystem::filesystem_error &ex) 
    { 
        std::cout << ex.what() << std::endl; 
        return EXIT_FAILURE; 
    } 
} 
#endif 

Process credentials

Processes under POSIX operating systems carry several properties that describe their security credentials. All of these can be configured through the POSIX context prior startup of a new process, as seen in the following table:

Table1.3.How to retrieve process credentials on POSIX operating systems
Concept Abbreviation POSIX launcher method
Real and effective user IDs UID set_uid
Effective user ID EUID set_euid
Real and effective group IDs GID set_gid
Effective group ID EGID set_egid

Note that changing the security credentials of a process is a privileged operation generally restricted to the super user. For more information you should see your operating system's documentation on the setuid, seteuid, setgid and setegid system calls.

Root directory change

Every process in a POSIX system has a root directory, used to resolve paths aside from the current working directory. This root directory is used to restrict processes to view only a part of the global file system: the process is not allowed to see the real file system's root directory; instead it sees the specified root directory as if it really was the file system's root. See the chroot system call documentation for more details.

The specialized posix_context supports changing the root directory of a new process, always assuming that sufficient privileges are available (i.e. the caller must be the super user). This is done through the chroot property.

Process termination

The POSIX's wait family of system calls returns a lot of information about the status of a finalized process, not only the exit status code provided on a normal exit. This information includes additional termination reasons such as if the process dumped a core file, if it exited due an external signal, etc.

The information described above can be queried through the posix_status class. This is built on top of the regular status class and includes additional methods to query all additional details. It can be used anywhere a status object is created thanks to its conversion constructor.

// 
// Boost.Process 
// ~~~~~~~~~~~~~ 
// 
// Copyright (c) 2006, 2007 Julio M. Merino Vidal 
// Copyright (c) 2008 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) 
// 

#include <boost/process.hpp> 
#if defined(BOOST_POSIX_API) 
#include <string> 
#include <vector> 
#include <iostream> 
#include <cstdlib> 

namespace bp = ::boost::process; 

bp::child start_child(std::string exec) 
{ 
    std::vector<std::string> args; 

    bp::context ctx; 
    ctx.stdin_behavior = bp::inherit_stream(); 
    ctx.stdout_behavior = bp::inherit_stream(); 
    ctx.stderr_behavior = bp::inherit_stream(); 

    return bp::launch(exec, args, ctx); 
} 

int main(int argc, char *argv[]) 
{ 
    if (argc < 2) 
    { 
        std::cerr << "Please provide a program name" << std::endl; 
        return EXIT_FAILURE; 
    } 

    bp::child c = start_child(argv[1]); 

    bp::posix_status s = c.wait(); 
    if (s.exited()) 
        std::cout << "Program returned exit code " << s.exit_status() << std::endl; 
    else if (s.signaled()) 
    { 
        std::cout << "Program received signal " << s.term_signal() << std::endl; 
        if (s.dumped_core()) 
            std::cout << "Program also dumped core" << std::endl; 
    } 
    else if (s.stopped()) 
        std::cout << "Program stopped by signal " << s.stop_signal() << std::endl; 
    else 
        std::cout << "Unknown termination reason" << std::endl; 

    return s.exited() ? s.exit_status() : EXIT_FAILURE; 
} 
#endif 

The Windows platforms

Startup information

The Windows CreateProcess system call receives a STARTUPINFO object that contains multiple details on how to configure the new process. Among these are the handles for the three standard communication channels (internally set up by the library), hints to set up the application's main window, etc.

The Windows-specific context win32_context provides mechanisms to provide some of this platform-specific information to the new process. The property startupinfo can receive a pointer to an already initialized STARTUPINFO object that is later passed to the CreateProcess call. If no such object is provided, the context behaves as context.

The example below demonstrates this feature. It relies on features provided by Windows operating systems to start a GUI process with hints on how to create the main window. The example passes the suggested window position as well as size and then waits until the new process terminates.

// 
// Boost.Process 
// ~~~~~~~~~~~~~ 
// 
// Copyright (c) 2006, 2007 Julio M. Merino Vidal 
// Copyright (c) 2008 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) 
// 

#include <boost/process.hpp> 
#if defined(BOOST_WINDOWS_API) 
#include <string> 
#include <vector> 
#include <iostream> 
#include <windows.h> 

namespace bp = ::boost::process; 

bp::win32_child start_child() 
{ 
    std::string exec = bp::find_executable_in_path("notepad"); 

    std::vector<std::string> args; 

    STARTUPINFO si; 
    ::ZeroMemory(&si, sizeof(si)); 
    si.cb = sizeof(si); 
    si.dwFlags |= STARTF_USEPOSITION | STARTF_USESIZE; 
    si.dwX = 0; 
    si.dwY = 0; 
    si.dwXSize = 640; 
    si.dwYSize = 480; 

    bp::win32_context ctx; 
    ctx.startupinfo = &si; 

    return bp::win32_launch(exec, args, ctx); 
} 

int main() 
{ 
    try 
    { 
        bp::win32_child c = start_child(); 
    } 
    catch (boost::filesystem::filesystem_error &ex) 
    { 
        std::cout << ex.what() << std::endl; 
    } 
} 
#endif 

Process information

The Windows CreateProcess system call starts a new process and returns a handle and an identifier for both the application's process and its main thread. Due to portability restrictions, the generic child implementation does not allow access to this information but, fortunately, the Windows-speficic child does. The class win32_child provides access to the information returned by the CreateProcess system call as described below:

Table1.4.How to retrieve process information on Windows
PROCESS_INFORMATION field Windows child method
hProcess get_handle
dwProcessId get_id
hThread get_primary_thread_handle
dwThreadId get_primary_thread_id

Windows child objects can only be constructed by using the Windows-specific context win32_context even if the user does not need any of the extra features provided by that class.

The following example demonstrates how a program can retrieve all the information returned by Windows's CreateProcess system call; that is: the process' and primary thread's identifier and handle. It relies on the Windows-specific context and child classes to be able to access this information:

// 
// Boost.Process 
// ~~~~~~~~~~~~~ 
// 
// Copyright (c) 2006, 2007 Julio M. Merino Vidal 
// Copyright (c) 2008 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) 
// 

#include <boost/process.hpp> 
#if defined(BOOST_WINDOWS_API) 
#include <string> 
#include <vector> 
#include <iostream> 
#include <windows.h> 

namespace bp = ::boost::process; 

bp::win32_child start_child() 
{ 
    std::string exec = bp::find_executable_in_path("notepad"); 

    std::vector<std::string> args; 

    bp::win32_context ctx; 

    return bp::win32_launch(exec, args, ctx); 
} 

int main() 
{ 
    try 
    { 
        bp::win32_child c = start_child(); 

        std::cout << "Process handle            : 0x" 
            << c.get_handle() << std::endl; 
        std::cout << "Process identifier        : " 
            << c.get_id() << std::endl; 
        std::cout << "Primary thread handle     : 0x" 
            << c.get_primary_thread_handle() << std::endl; 
        std::cout << "Primary thread identifier : " 
            << c.get_primary_thread_id() << std::endl; 
    } 
    catch (boost::filesystem::filesystem_error &ex) 
    { 
        std::cout << ex.what() << std::endl; 
    } 
} 
#endif 

PrevUpHomeNext