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   /// On Windows only: controls how the process is started, mimics STARTUPINFO's wShowWindow.
24   /// See: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/ns-processthreadsapi-startupinfoa
25   /// and https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-showwindow
26   enum class ShowWindow {
27     hide = 0,
28     show_normal = 1,
29     show_minimized = 2,
30     maximize = 3,
31     show_maximized = 3,
32     show_no_activate = 4,
33     show = 5,
34     minimize = 6,
35     show_min_no_active = 7,
36     show_na = 8,
37     restore = 9,
38     show_default = 10,
39     force_minimize = 11
40   };
41   /// On Windows only: controls how the window is shown.
42   ShowWindow show_window{ShowWindow::show_default};
43 };
44 
45 /// Platform independent class for creating processes.
46 /// Note on Windows: it seems not possible to specify which pipes to redirect.
47 /// Thus, at the moment, if read_stdout==nullptr, read_stderr==nullptr and open_stdin==false,
48 /// the stdout, stderr and stdin are sent to the parent process instead.
49 class Process {
50 public:
51 #ifdef _WIN32
52   typedef unsigned long id_type; // Process id type
53   typedef void *fd_type;         // File descriptor type
54 #ifdef UNICODE
55   typedef std::wstring string_type;
56 #else
57   typedef std::string string_type;
58 #endif
59 #else
60   typedef pid_t id_type;
61   typedef int fd_type;
62   typedef std::string string_type;
63 #endif
64   typedef std::unordered_map<string_type, string_type> environment_type;
65 
66 private:
67   class Data {
68   public:
69     Data() noexcept;
70     id_type id;
71 #ifdef _WIN32
72     void *handle{nullptr};
73 #endif
74     int exit_status{-1};
75   };
76 
77 public:
78   /// Starts a process with the environment of the calling process.
79   Process(const std::vector<string_type> &arguments, const string_type &path = string_type(),
80           std::function<void(const char *bytes, size_t n)> read_stdout = nullptr,
81           std::function<void(const char *bytes, size_t n)> read_stderr = nullptr,
82           bool open_stdin = false,
83           const Config &config = {}) noexcept;
84   /// Starts a process with the environment of the calling process.
85   Process(const string_type &command, const string_type &path = string_type(),
86           std::function<void(const char *bytes, size_t n)> read_stdout = nullptr,
87           std::function<void(const char *bytes, size_t n)> read_stderr = nullptr,
88           bool open_stdin = false,
89           const Config &config = {}) noexcept;
90 
91   /// Starts a process with specified environment.
92   Process(const std::vector<string_type> &arguments,
93           const string_type &path,
94           const environment_type &environment,
95           std::function<void(const char *bytes, size_t n)> read_stdout = nullptr,
96           std::function<void(const char *bytes, size_t n)> read_stderr = nullptr,
97           bool open_stdin = false,
98           const Config &config = {}) noexcept;
99   /// Starts a process with specified environment.
100   Process(const string_type &command,
101           const string_type &path,
102           const environment_type &environment,
103           std::function<void(const char *bytes, size_t n)> read_stdout = nullptr,
104           std::function<void(const char *bytes, size_t n)> read_stderr = nullptr,
105           bool open_stdin = false,
106           const Config &config = {}) noexcept; /// Starts a process with specified environment.
107 #ifndef _WIN32
108   /// Starts a process with the environment of the calling process.
109   /// Supported on Unix-like systems only.
110   Process(const std::function<void()> &function,
111           std::function<void(const char *bytes, size_t n)> read_stdout = nullptr,
112           std::function<void(const char *bytes, size_t n)> read_stderr = nullptr,
113           bool open_stdin = false,
114           const Config &config = {}) noexcept;
115 #endif
116   ~Process() noexcept;
117 
118   /// Get the process id of the started process.
119   id_type get_id() const noexcept;
120   /// Wait until process is finished, and return exit status.
121   int get_exit_status() noexcept;
122   /// If process is finished, returns true and sets the exit status. Returns false otherwise.
123   bool try_get_exit_status(int &exit_status) noexcept;
124   /// Write to stdin.
125   bool write(const char *bytes, size_t n);
126   /// Write to stdin. Convenience function using write(const char *, size_t).
127   bool write(const std::string &str);
128   /// Close stdin. If the process takes parameters from stdin, use this to notify that all parameters have been sent.
129   void close_stdin() noexcept;
130 
131   /// Kill the process. force=true is only supported on Unix-like systems.
132   void kill(bool force = false) noexcept;
133   /// Kill a given process id. Use kill(bool force) instead if possible. force=true is only supported on Unix-like systems.
134   static void kill(id_type id, bool force = false) noexcept;
135 #ifndef _WIN32
136   /// Send the signal signum to the process.
137   void signal(int signum) noexcept;
138 #endif
139 
140 private:
141   Data data;
142   bool closed;
143   std::mutex close_mutex;
144   std::function<void(const char *bytes, size_t n)> read_stdout;
145   std::function<void(const char *bytes, size_t n)> read_stderr;
146 #ifndef _WIN32
147   std::thread stdout_stderr_thread;
148 #else
149   std::thread stdout_thread, stderr_thread;
150 #endif
151   bool open_stdin;
152   std::mutex stdin_mutex;
153 
154   Config config;
155 
156   std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd;
157 
158   id_type open(const std::vector<string_type> &arguments, const string_type &path, const environment_type *environment = nullptr) noexcept;
159   id_type open(const string_type &command, const string_type &path, const environment_type *environment = nullptr) noexcept;
160 #ifndef _WIN32
161   id_type open(const std::function<void()> &function) noexcept;
162 #endif
163   void async_read() noexcept;
164   void close_fds() noexcept;
165 };
166 
167 } // namespace TinyProcessLib
168 
169 #endif // TINY_PROCESS_LIBRARY_HPP_
170