Boost.Process

repository·develop·Indexed 40 days ago

https://github.com/boostorg/process

A C++11 library providing a high-level interface for managing system processes, their lifecycles, and inter-process communication. It includes tools for spawning processes via process launchers, manipulating environment variables, and managing exit codes and signals. Supports Boost version 1.64 or later and provides specialized launchers for Windows and POSIX platforms.

Tokens
15.1K
Snippets
55
Records
77
Agent score
43%

What's inside Boost.Process

  1. Overview of Boost.Process

    develop
    Boost.Process is a C++11 library designed for the comfortable management of processes. It provides an interface for spawning processes, managing their lifecycles, and handling inter-process communication.
  2. Use Boost.Process V2 for process management

    develop

    Boost.Process V2 is the current default version, redesigned for simplicity and reliability. Unlike V1, which used a DSL-like constructor for partial settings, V2 uses a simplified interface where each argument addresses a single logical component (e.g., process_stdio for I/O or process_environment for environment variables).

    Key improvements in V2 include:

    • Full Asio Integration: Uses Asio pipes and file handles for subprocesses to prevent deadlocks.
    • Reliable Waiting: Uses pidfd_open on Linux and HANDLEs on Windows to implement waiting via Asio's async/cancellation model, replacing the unreliable signal-based wait_for from V1.
    • UTF-8 Support: Assumes UTF-8 everywhere, utilizing UTF-16 APIs on Windows.
    • FD Safety: Automatically closes all non-whitelisted file descriptors on POSIX systems by default.
    • Explicit Configuration: Every process requires a path and arguments, avoiding the mixture of command-style and exe-args found in V1.
    extern std::unordered_map<std::string, std::string> my_env;
    extern asio::io_context ctx;
    
    // V2 uses explicit component initializers
    process proc(ctx, "./test", {"--help"}, process_stdio{nullptr, {}, {}}, process_environment(my_env));
  3. Query and manipulate the process environment

    develop

    The boost::process::environment namespace provides tools to interact with the current process's environment variables.

    Key behaviors to note:

    • Case Sensitivity: Environment variable names are case-insensitive on Windows. The key_traits class handles this platform-specific behavior automatically.
    • Path Lists: Environment variables containing lists (like PATH) are often separated by : or ;. You can use environment::value and environment::value_view to iterate through these individual values.
    • Data Formats: An environment can be represented as a list of strings or a list of string-pairs. While flexible, it is recommended to use the provided environment types to ensure correct value comparisons.
  4. Understand the `basic_process_handle` abstraction

    develop

    A basic_process_handle is an unmanaged version of a process. Unlike managed process objects, a basic_process_handle does not automatically terminate the process when it is destroyed, nor does it automatically keep track of the exit code.

    Key behaviors to note:

    • Lifecycle: Destruction does not end the process.
    • Exit Code Discovery: The exit code might be discovered early during a call to running(). It can only be discovered if the process has already exited.
    • Platform Differences: On non-Linux POSIX systems, obtaining a file descriptor for a PID might not be possible, meaning the handle may rely on signals instead.
  5. How process cancellation works in `async_execute`

    develop

    When cancelling an asynchronous execution started via async_execute, the library signals the child process to exit. The method of exit depends on the cancellation_type used. The library selects the lowest (most conservative) cancellation type requested. Note that subprocesses may ignore signals that are not terminal.

    /* Cancellation type mappings: */
    // cancellation_type::total    -> interrupt
    // cancellation_type::partial  -> request_exit
    // cancellation_type::terminal -> terminate
  6. Access the current process environment as a range

    develop

    You can iterate over the current process's environment variables using environment::current(). This returns a current_view, which acts as a range of key_value_pair_view objects.

    Warning: Accessing the environment in this way is not thread-safe.

    Usage Pattern

    You can use structured bindings to iterate through the environment easily:

    void dump_my_env(environment::current_view env = environment::current())
    {
       for (auto  & [k, v] : env)
           std::cout << k.string() << " = "  << v.string() << std::endl;
    }
  7. Manage process lifetime and exit codes

    develop

    Lifetime Management

    • Automatic Termination: If the process handle goes out of scope, the subprocess is terminated.
    • Detaching: To prevent automatic termination when the handle is destroyed, call proc.detach(). Warning: Detaching can lead to zombie processes.

    Monitoring and Exit Codes

    • Status: Use .running() to check if the process is still active.
    • Exit Code: Once a process has completed, you can retrieve the exit code using .exit_code or by capturing the return value of .wait().
    • Native Exit Code: On POSIX systems, .native_exit_code provides additional platform-specific information beyond the standard return value from main.
  8. Use pipes for subprocess I/O

    develop

    You can use Boost.Asio pipes for I/O with a subprocess. When using them within process_stdio, the pipes are automatically connected: one end is assigned to the child process, and the other end is available to the parent process.

    • Use readable_pipe for out (stdout) and err (stderr).
    • Use writable_pipe for in (stdin).
    // Example of using pipes for I/O
    boost::asio::ip::tcp::socket socket(io_context);
    // ... (setup pipes) ...
    // Assigning pipes to process_stdio
    // readable pipes -> out/err, writable pipes -> in
  9. Set the environment for a child process with `process_environment`

    develop

    To provide a specific environment to a new child process (instead of inheriting the current process's environment), use the process_environment class. This class can be initialized with strings in the format "KEY=VALUE".

    Usage

    Pass an instance of process_environment to the process constructor.

    // Setting a single variable
    process proc{executor, find_executable("printenv"), {"foo"}, process_environment{"foo=bar"}};
    
    // Reusing an environment object for multiple processes
    auto exe = find_executable("printenv");
    process_environment env = {"FOO=BAR", "BAR=FOO"};
    
    process proc1(executor, exe, {"FOO"}, env);
    process proc2(executor, exe, {"BAR"}, env);

    Note: The process_environment initializer persists its state and can be reused, but be aware that the Operating System is allowed to modify its internal state.

    process proc{executor, find_executable("printenv"), {"foo"}, process_environment{"foo=bar"}};
  10. Redirect streams to the null device using `nullptr`

    develop

    To ignore a specific stream's output or to provide only an EOF (End Of File) as input to a subprocess, assign nullptr to the corresponding stream. This automatically redirects the stream to the system's null device (/dev/null on POSIX or NUL on Windows).

    // Example of redirecting to null
    // include::../example/stdio.cpp[tag=null]