|
| 1 | +#ifndef TINY_PROCESS_LIBRARY_HPP_ |
| 2 | +#define TINY_PROCESS_LIBRARY_HPP_ |
| 3 | +#include <functional> |
| 4 | +#include <memory> |
| 5 | +#include <mutex> |
| 6 | +#include <string> |
| 7 | +#include <thread> |
| 8 | +#include <unordered_map> |
| 9 | +#include <vector> |
| 10 | +#ifndef _WIN32 |
| 11 | +#include <sys/wait.h> |
| 12 | +#endif |
| 13 | + |
| 14 | +namespace TinyProcessLib { |
| 15 | +/// Additional parameters to Process constructors. |
| 16 | +struct Config { |
| 17 | + /// Buffer size for reading stdout and stderr. Default is 131072 (128 kB). |
| 18 | + std::size_t buffer_size = 131072; |
| 19 | + /// Set to true to inherit file descriptors from parent process. Default is false. |
| 20 | + /// On Windows: has no effect unless read_stdout==nullptr, read_stderr==nullptr and open_stdin==false. |
| 21 | + bool inherit_file_descriptors = false; |
| 22 | + |
| 23 | + /// If set, invoked when process stdout is closed. |
| 24 | + /// This call goes after last call to read_stdout(). |
| 25 | + std::function<void()> on_stdout_close = nullptr; |
| 26 | + /// If set, invoked when process stderr is closed. |
| 27 | + /// This call goes after last call to read_stderr(). |
| 28 | + std::function<void()> on_stderr_close = nullptr; |
| 29 | + |
| 30 | + /// On Windows only: controls how the process is started, mimics STARTUPINFO's wShowWindow. |
| 31 | + /// See: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/ns-processthreadsapi-startupinfoa |
| 32 | + /// and https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-showwindow |
| 33 | + enum class ShowWindow { |
| 34 | + hide = 0, |
| 35 | + show_normal = 1, |
| 36 | + show_minimized = 2, |
| 37 | + maximize = 3, |
| 38 | + show_maximized = 3, |
| 39 | + show_no_activate = 4, |
| 40 | + show = 5, |
| 41 | + minimize = 6, |
| 42 | + show_min_no_active = 7, |
| 43 | + show_na = 8, |
| 44 | + restore = 9, |
| 45 | + show_default = 10, |
| 46 | + force_minimize = 11 |
| 47 | + }; |
| 48 | + /// On Windows only: controls how the window is shown. |
| 49 | + ShowWindow show_window{ShowWindow::show_default}; |
| 50 | + |
| 51 | + /// Set to true to break out of flatpak sandbox by prepending all commands with `/usr/bin/flatpak-spawn --host` |
| 52 | + /// which will execute the command line on the host system. |
| 53 | + /// Requires the flatpak `org.freedesktop.Flatpak` portal to be opened for the current sandbox. |
| 54 | + /// See https://docs.flatpak.org/en/latest/flatpak-command-reference.html#flatpak-spawn. |
| 55 | + bool flatpak_spawn_host = false; |
| 56 | +}; |
| 57 | + |
| 58 | +/// Platform independent class for creating processes. |
| 59 | +/// Note on Windows: it seems not possible to specify which pipes to redirect. |
| 60 | +/// Thus, at the moment, if read_stdout==nullptr, read_stderr==nullptr and open_stdin==false, |
| 61 | +/// the stdout, stderr and stdin are sent to the parent process instead. |
| 62 | +class Process { |
| 63 | +public: |
| 64 | +#ifdef _WIN32 |
| 65 | + typedef unsigned long id_type; // Process id type |
| 66 | + typedef void *fd_type; // File descriptor type |
| 67 | +#ifdef UNICODE |
| 68 | + typedef std::wstring string_type; |
| 69 | +#else |
| 70 | + typedef std::string string_type; |
| 71 | +#endif |
| 72 | +#else |
| 73 | + typedef pid_t id_type; |
| 74 | + typedef int fd_type; |
| 75 | + typedef std::string string_type; |
| 76 | +#endif |
| 77 | + typedef std::unordered_map<string_type, string_type> environment_type; |
| 78 | + |
| 79 | +private: |
| 80 | + class Data { |
| 81 | + public: |
| 82 | + Data() noexcept; |
| 83 | + id_type id; |
| 84 | +#ifdef _WIN32 |
| 85 | + void *handle{nullptr}; |
| 86 | +#endif |
| 87 | + int exit_status{-1}; |
| 88 | + }; |
| 89 | + |
| 90 | +public: |
| 91 | + /// Starts a process with the environment of the calling process. |
| 92 | + Process(const std::vector<string_type> &arguments, const string_type &path = string_type(), |
| 93 | + std::function<void(const char *bytes, size_t n)> read_stdout = nullptr, |
| 94 | + std::function<void(const char *bytes, size_t n)> read_stderr = nullptr, |
| 95 | + bool open_stdin = false, |
| 96 | + const Config &config = {}) noexcept; |
| 97 | + /// Starts a process with the environment of the calling process. |
| 98 | + Process(const string_type &command, const string_type &path = string_type(), |
| 99 | + std::function<void(const char *bytes, size_t n)> read_stdout = nullptr, |
| 100 | + std::function<void(const char *bytes, size_t n)> read_stderr = nullptr, |
| 101 | + bool open_stdin = false, |
| 102 | + const Config &config = {}) noexcept; |
| 103 | + |
| 104 | + /// Starts a process with specified environment. |
| 105 | + Process(const std::vector<string_type> &arguments, |
| 106 | + const string_type &path, |
| 107 | + const environment_type &environment, |
| 108 | + std::function<void(const char *bytes, size_t n)> read_stdout = nullptr, |
| 109 | + std::function<void(const char *bytes, size_t n)> read_stderr = nullptr, |
| 110 | + bool open_stdin = false, |
| 111 | + const Config &config = {}) noexcept; |
| 112 | + /// Starts a process with specified environment. |
| 113 | + Process(const string_type &command, |
| 114 | + const string_type &path, |
| 115 | + const environment_type &environment, |
| 116 | + std::function<void(const char *bytes, size_t n)> read_stdout = nullptr, |
| 117 | + std::function<void(const char *bytes, size_t n)> read_stderr = nullptr, |
| 118 | + bool open_stdin = false, |
| 119 | + const Config &config = {}) noexcept; |
| 120 | +#ifndef _WIN32 |
| 121 | + /// Starts a process with the environment of the calling process. |
| 122 | + /// Supported on Unix-like systems only. |
| 123 | + /// Since the command line is not known to the Process object itself, |
| 124 | + /// this overload does not support the flatpak_spawn_host configuration. |
| 125 | + Process(const std::function<void()> &function, |
| 126 | + std::function<void(const char *bytes, size_t n)> read_stdout = nullptr, |
| 127 | + std::function<void(const char *bytes, size_t n)> read_stderr = nullptr, |
| 128 | + bool open_stdin = false, |
| 129 | + const Config &config = {}); |
| 130 | +#endif |
| 131 | + ~Process() noexcept; |
| 132 | + |
| 133 | + /// Get the process id of the started process. |
| 134 | + id_type get_id() const noexcept; |
| 135 | + /// Wait until process is finished, and return exit status. |
| 136 | + int get_exit_status() noexcept; |
| 137 | + /// If process is finished, returns true and sets the exit status. Returns false otherwise. |
| 138 | + bool try_get_exit_status(int &exit_status) noexcept; |
| 139 | + /// Write to stdin. |
| 140 | + bool write(const char *bytes, size_t n); |
| 141 | + /// Write to stdin. Convenience function using write(const char *, size_t). |
| 142 | + bool write(const std::string &str); |
| 143 | + /// Close stdin. If the process takes parameters from stdin, use this to notify that all parameters have been sent. |
| 144 | + void close_stdin() noexcept; |
| 145 | + |
| 146 | + /// Kill the process. force=true is only supported on Unix-like systems. |
| 147 | + void kill(bool force = false) noexcept; |
| 148 | + /// Kill a given process id. Use kill(bool force) instead if possible. force=true is only supported on Unix-like systems. |
| 149 | + static void kill(id_type id, bool force = false) noexcept; |
| 150 | +#ifndef _WIN32 |
| 151 | + /// Send the signal signum to the process. |
| 152 | + void signal(int signum) noexcept; |
| 153 | +#endif |
| 154 | + |
| 155 | +private: |
| 156 | + Data data; |
| 157 | + bool closed; |
| 158 | + std::mutex close_mutex; |
| 159 | + std::function<void(const char *bytes, size_t n)> read_stdout; |
| 160 | + std::function<void(const char *bytes, size_t n)> read_stderr; |
| 161 | +#ifndef _WIN32 |
| 162 | + std::thread stdout_stderr_thread; |
| 163 | +#else |
| 164 | + std::thread stdout_thread, stderr_thread; |
| 165 | +#endif |
| 166 | + bool open_stdin; |
| 167 | + std::mutex stdin_mutex; |
| 168 | + |
| 169 | + Config config; |
| 170 | + |
| 171 | + std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd; |
| 172 | + |
| 173 | + id_type open(const std::vector<string_type> &arguments, const string_type &path, const environment_type *environment = nullptr) noexcept; |
| 174 | + id_type open(const string_type &command, const string_type &path, const environment_type *environment = nullptr) noexcept; |
| 175 | +#ifndef _WIN32 |
| 176 | + id_type open(const std::function<void()> &function) noexcept; |
| 177 | +#endif |
| 178 | + void async_read() noexcept; |
| 179 | + void close_fds() noexcept; |
| 180 | +}; |
| 181 | + |
| 182 | +} // namespace TinyProcessLib |
| 183 | + |
| 184 | +#endif // TINY_PROCESS_LIBRARY_HPP_ |
0 commit comments