Skip to content
/ reproc Public
forked from DaanDeMeyer/reproc

Cross-platform (C99/C++11) process library

License

Notifications You must be signed in to change notification settings

cnglish/reproc

 
 

Repository files navigation

reproc

What is reproc?

reproc (Redirected Process) is a cross-platform C/C++ library that simplifies starting, stopping and communicating with external programs. The main use case is executing command line applications directly from C or C++ code and retrieving their output.

reproc consists out of two libraries: reproc and reproc++. reproc is a C99 library that contains the actual code for working with external programs. reproc++ depends on reproc and adapts its API to an idiomatic C++11 API. It also adds a few extras that simplify working with external programs from C++.

Features

  • Start any program directly from C or C++ code.
  • Communicate with a program via its standard streams.
  • Wait for a program to exit or forcefully stop it yourself. When forcefully stopping a process you can either allow the process to clean up its resources or stop it immediately.
  • The core library (reproc) is written in C99. An optional C++11 wrapper library (reproc++) with extra features is available for use in C++ applications.
  • Multiple installation methods. Either build reproc as part of your project or use a system installed version of reproc.

Questions

If you have any questions after reading the readme and documentation you can either make an issue or ask questions directly in the reproc gitter channel.

Installation

There are multiple ways to get reproc into your project. One way is to build reproc as part of your project using CMake. To do this, we first have to get the reproc source code into the project. This can be done using any of the following options:

After including reproc's source code in your project, it can be built from the root CMakeLists.txt file as follows:

add_subdirectory(<path-to-reproc>) # For example: add_subdirectory(external/reproc)

Options can be specified before calling add_subdirectory (omit FORCE if you don't want to override an existing value in the cache):

set(REPROC++ ON CACHE BOOL "" FORCE)
add_subdirectory(<path-to-reproc>)

You can also depend on a system installed version of reproc. You can either build and install reproc to your system yourself or install reproc via a package manager. reproc is available in the following package repositories:

After installing reproc to the system your build system will have to find it. reproc provides both CMake config files and pkg-config files to simplify finding a system installed version of reproc using CMake and pkg-config respectively. Note that reproc and reproc++ are separate libraries and as a result have separate config files as well. Make sure to search for the one you need.

To find a system installation of reproc using CMake:

find_package(reproc) # Find reproc.
find_package(reproc++) # Find reproc++.

After building reproc as part of your project or finding a system installed reproc, you can link against it from within your CMakeLists.txt file as follows:

target_link_libraries(myapp reproc) # Link against reproc.
target_link_libraries(myapp reproc++) # Link against reproc++.

Dependencies

Building reproc requires CMake 3.13 or higher.

By default, reproc has a single dependency on pthreads on POSIX systems. However, the dependency is included in both reproc's CMake config and pkg-config files so it should be picked up by your build system automatically.

CMake options

reproc's build can be configured using the following CMake options:

User

  • REPROC++: Build reproc++ (default: OFF).

  • REPROC_TEST: Build tests (default: OFF).

    Run the tests by building the test target or running the tests executable from within the build directory.

  • REPROC_EXAMPLES: Build examples (default: OFF).

    The built executables will be located in the examples folder of each project subdirectory in the build directory.

Advanced

  • REPROC_INSTALL: Generate installation rules (default: ON unless BUILD_SHARED_LIBS is false and reproc is built via add_subdirectory).
  • REPROC_INSTALL_CMAKECONFIGDIR: CMake config files installation directory (default: ${CMAKE_INSTALL_LIBDIR}/cmake).
  • REPROC_INSTALL_PKGCONFIG: Install pkg-config files (default: ON)
  • REPROC_INSTALL_PKGCONFIGDIR: pkg-config files installation directory (default: ${CMAKE_INSTALL_LIBDIR}/pkgconfig).

POSIX only:

  • REPROC_MULTITHREADED: Use pthread_sigmask instead of sigprocmask (default: ON).

    sigprocmask's behaviour is only defined for single-threaded programs. When using multiple threads, pthread_sigmask should be used instead. Because we cannot determine whether reproc will be used in a multi-threaded program when building reproc, REPROC_MULTITHREADED is enabled by default to guarantee defined behaviour. Users that know for certain their program will only use a single thread can opt to disable REPROC_MULTITHREADED.

    When using reproc via CMake add_subdirectory and REPROC_MULTITHREADED is enabled, reproc will only call find_package(Threads) if the user has not called find_package(Threads) himself. The THREADS_PREFER_PTHREAD_FLAG variable influences the behaviour of find_package(Threads). if it is not defined, reproc's build enables it before calling find_package(Threads).

Developer

  • REPROC_SANITIZERS: Build with sanitizers (default: OFF).
  • REPROC_TIDY: Run clang-tidy when building (default: OFF).
  • REPROC_WARNINGS_AS_ERRORS: Add -Werror or equivalent to the compile flags and clang-tidy (default: OFF).

Documentation

Each function and class is documented extensively in its header file. Examples can be found in the examples subdirectory of reproc and reproc++.

Error handling

Most functions in reproc's API return REPROC_ERROR. The REPROC_ERROR enum represents all possible errors that can occur when calling reproc API functions. Not all errors apply to each function so the documentation of each function includes a section detailing which errors can occur. System errors are represented by REPROC_ERROR_SYSTEM. The reproc_system_error error can be used to retrieve the actual system error. To get a string representation of the error, pass the error code to reproc_strerror. If the error code passed to reproc_strerror is REPROC_ERROR_SYSTEM, reproc_strerror returns a string representation of the error returned by reproc_system_error.

reproc++'s API integrates with the C++ standard library error codes mechanism (std::error_code and std::error_condition). All functions in reproc++'s API return std::error_code values that contain the actual system error that occurred. This means reproc_system_error is not necessary in reproc++ since the returned error codes store the actual system error instead of the value of REPROC_ERROR_SYSTEM. You can test against these error codes using the std::errc error condition enum:

reproc::process;
std::error_code ec = process.start(...);

if (ec == std::errc::no_such_file_or_directory) {
  std::cerr << "Executable not found. Make sure it is available from the PATH.";
  return 1;
}

if (ec) {
  // Will print the actual system error value from errno or GetLastError() if a
  // system error occurred.
  std::cerr << ec.value() << std::endl;
  // You can also print a string representation of the error.
  std::cerr << ec.message();
  return 1;
}

If needed, you can also convert std::error_code's to exceptions using std::system_error:

reproc::process;
std::error_code ec = process.start(...);
if (ec) {
  throw std::system_error(ec, "Unable to start process");
}

Multithreading

Guidelines for using a reproc child process from multiple threads:

  • Don't wait for or stop the same child process from multiple threads at the same time.
  • Don't read from or write to the same stream of the same child process from multiple threads at the same time.

Different threads can read from or write to different streams at the same time. This is a valid approach when you want to write to stdin and read from stdout in parallel.

Look at the forward and background examples to see examples of how to work with reproc from multiple threads.

Gotchas

  • (POSIX) On POSIX a parent process is required to wait on a child process that has exited (using reproc_wait) before all resources related to that process can be released by the kernel. If the parent doesn't wait on a child process after it exits, the child process becomes a zombie process.

  • While reproc_terminate allows the child process to perform cleanup it is up to the child process to correctly clean up after itself. reproc only sends a termination signal to the child process. The child process itself is responsible for cleaning up its own child processes and other resources.

  • When using reproc_kill the child process does not receive a chance to perform cleanup which could result in resources being leaked. Chief among these leaks is that the child process will not be able to stop its own child processes. Always try to let a child process exit normally by calling reproc_terminate before calling reproc_kill.

  • (Windows) reproc_kill is not guaranteed to kill a child process immediately on Windows. For more information, read the Remarks section in the documentation of the Windows TerminateProcess function that reproc uses to kill child processes on Windows.

  • While reproc tries its very best to avoid leaking file descriptors into child processes, there are scenarios where it cannot guarantee that no file descriptors will be leaked to child processes.

  • (POSIX) Writing to a closed stdin pipe of a child process will crash the parent process with the SIGPIPE signal. To avoid this the SIGPIPE signal has to be ignored in the parent process. If the SIGPIPE signal is ignored reproc_write will return REPROC_ERROR_STREAM_CLOSED as expected when writing to a closed stdin pipe.

  • (POSIX) Ignoring the SIGCHLD signal by setting its disposition to SIG_IGN changes the behavior of the waitpid system call which will cause reproc_wait to stop working as expected. Read the Notes section of the waitpid man page for more information.

About

Cross-platform (C99/C++11) process library

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C 51.4%
  • C++ 35.9%
  • CMake 12.2%
  • Batchfile 0.5%