subprocess.h
Go to the documentation of this file.
1 // ===========================================================================
2 // Copyright (c) 2011-2012 University of Pennsylvania
3 // Copyright (c) 2013-2016 Andreas Schuh
4 // All rights reserved.
5 //
6 // See COPYING file for license information or visit
7 // https://cmake-basis.github.io/download.html#license
8 // ===========================================================================
9 
10 /**
11  * @file subprocess.h
12  * @brief Module used to execute subprocesses.
13  */
14 
15 #pragma once
16 #ifndef _BASIS_SUBPROCESS_H
17 #define _BASIS_SUBPROCESS_H
18 
19 
20 #include <basis/config.h> // platform macros - must be first
21 
22 #include <vector>
23 #include <string>
24 
25 #if WINDOWS
26 # include <windows.h>
27 #else
28 # include <sys/types.h>
29 # include <unistd.h>
30 #endif
31 
32 
33 namespace basis {
34 
35 
36 /**
37  * @class Subprocess
38  * @brief Platform-independent interface to create and control a subprocess.
39  */
41 {
42  // -----------------------------------------------------------------------
43  // types
44 public:
45 
46  typedef std::vector<std::string> CommandLine;
47  typedef std::vector<std::string> Environment;
48 
49 private:
50 
51  /// Type used for file handles of pipes between processes.
52 #if WINDOWS
53  typedef HANDLE PipeHandle;
54 #else
55  typedef int PipeHandle;
56 #endif
57 
58  /// Information structure required by system to identify subprocess.
59 #if WINDOWS
60  typedef PROCESS_INFORMATION Information;
61 #else
62  struct Information
63  {
64  pid_t pid;
65  };
66 #endif
67 
68  // -----------------------------------------------------------------------
69  // constants
70 public:
71 
72  /**
73  * @brief Modes of redirection for standard input/output buffers.
74  */
76  {
77  RM_NONE, ///< Do not redirect the input/output.
78  RM_PIPE, ///< Use a pipe to redirect the input/output from/to the parent.
79  RM_STDOUT ///< Redirect stderr to stdout.
80  };
81 
82  // -----------------------------------------------------------------------
83  // construction / destruction
84 public:
85 
86  /**
87  * @brief Default constructor.
88  */
89  Subprocess();
90 
91  /**
92  * @brief Terminate running subprocess and close all related handles.
93  */
94  ~Subprocess();
95 
96  // -----------------------------------------------------------------------
97  // helpers
98 public:
99 
100  /**
101  * @brief Split double quoted string into arguments.
102  *
103  * @param [in] cmd Double quoted string. Use '\' to escape double quotes
104  * within arguments.
105  *
106  * @returns Argument vector.
107  */
108  static CommandLine split(const std::string& cmd);
109 
110  /**
111  * @brief Convert argument vector to double quoted string.
112  *
113  * @param [in] args Argument vector.
114  *
115  * @returns Double quoted string.
116  */
117  static std::string tostring(const CommandLine& args);
118 
119  // -----------------------------------------------------------------------
120  // process control
121 public:
122 
123  /**
124  * @brief Open new subprocess.
125  *
126  * This method creates the subprocess and returns immediately. In order to
127  * wait for the suprocess to finish, the wait() method has to be called
128  * explicitly.
129  *
130  * @param [in] args Command-line of subprocess. The first argument has to
131  * be the name/path of the command to be executed.
132  * @param [in] rm_in Mode used for redirection of stdin of subprocess.
133  * Can be either RM_NONE or RM_PIPE.
134  * @param [in] rm_out Mode used for redirection of stdout of subprocess.
135  * Can be either RM_NONE or RM_PIPE.
136  * @param [in] rm_err Mode used for redirection of stderr of subprocess.
137  * Can be either RM_NONE, RM_PIPE, or RM_STDOUT.
138  * @param [in] env Environment for the subprocess. If NULL is given, the
139  * environment of the parent process is used.
140  *
141  * @returns Whether the subprocess was created successfully.
142  */
143  bool popen(const CommandLine& args,
144  // attention: stdin, stdout, and stderr are macros defined by windows.h
145  const RedirectMode rm_in = RM_NONE,
146  const RedirectMode rm_out = RM_NONE,
147  const RedirectMode rm_err = RM_NONE,
148  const Environment* env = NULL);
149 
150  /**
151  * @brief Open new subprocess.
152  *
153  * This method creates the subprocess and returns immediately. In order to
154  * wait for the suprocess to finish, the wait() method has to be called
155  * explicitly.
156  *
157  * @param [in] cmd Command-line given as double quoted string. Arguments
158  * containing whitespaces have to be quoted using double
159  * quotes. Use a backslash (\) to escape double quotes
160  * inside an argument as well as to escape a backslash
161  * itself (required if backslash at end of double quoted
162  * argument, e.g., "this argument \\").
163  * @param [in] rm_in Mode used for redirection of stdin of subprocess.
164  * Can be either RM_NONE or RM_PIPE.
165  * @param [in] rm_out Mode used for redirection of stdout of subprocess.
166  * Can be either RM_NONE or RM_PIPE.
167  * @param [in] rm_err Mode used for redirection of stderr of subprocess.
168  * Can be either RM_NONE, RM_PIPE, or RM_STDOUT.
169  * @param [in] env Environment for the subprocess. If NULL is given, the
170  * environment of the parent process is used.
171  */
172  bool popen(const std::string& cmd,
173  // attention: stdin, stdout, and stderr are macros defined by windows.h
174  const RedirectMode rm_in = RM_NONE,
175  const RedirectMode rm_out = RM_NONE,
176  const RedirectMode rm_err = RM_NONE,
177  const Environment* env = NULL)
178  {
179  return popen(split(cmd), rm_in, rm_out, rm_err, env);
180  }
181 
182  /**
183  * @brief Check if subprocess terminated and update return code.
184  *
185  * This method returns immediately and does not wait for the subprocess to
186  * actually being terminated. For that purpuse, use wait() instead.
187  *
188  * @returns Whether the subprocess terminated.
189  */
190  bool poll() const;
191 
192  /**
193  * @brief Wait for subprocess to terminate.
194  *
195  * This method also sets the exit code as returned by GetExitCode().
196  */
197  bool wait();
198 
199  /**
200  * @brief Send signal to subprocess.
201  *
202  * On Windows, SIGTERM is an alias for terminate() and SIGKILL an alias for
203  * kill() which in turn is nothing else but a termination of the subprocess.
204  * All other signals are only sent to POSIX processes.
205  */
206  bool send_signal(int signal);
207 
208  /**
209  * @brief Terminate subprocess.
210  *
211  * - On POSIX, the SIGTERM signal is send.
212  * - On Windows, TerminateProcess() is invoked to terminate the subprocess.
213  */
214  bool terminate();
215 
216  /**
217  * @brief Kill subprocess.
218  *
219  * - On POSIX, the SIGKILL signal is send.
220  * - On Windows, TerminateProcess() is invoked to terminate the subprocess.
221  */
222  bool kill();
223 
224  /**
225  * @returns Wether the subprocess has been signaled, i.e., terminated abnormally.
226  */
227  bool signaled() const;
228 
229  /**
230  * @returns ID of subprocess.
231  */
232  int pid() const;
233 
234  /**
235  * @returns Exit code of subprocess. Only valid if terminated() is true.
236  */
237  int returncode() const;
238 
239  // -----------------------------------------------------------------------
240  // inter-process communication
241 public:
242 
243  /**
244  * @brief Communicate with subprocess.
245  *
246  * @note This method closes the pipes to the subprocess after all data
247  * has been sent and received and returns after the subprocess
248  * terminated. Therefore, the exit code is set upon return.
249  *
250  * @param [in] in Data send to subprocess via pipe to stdin.
251  * If no pipe was setup during subprocess creation,
252  * this function does nothing and returns false.
253  * @param [in] out Data read from stdout of subprocess. Can be an empty
254  * string if no pipe was created for stdout.
255  * @param [in] err Data read from stderr of subprocess. Can be an empty
256  * string if no pipe was created for stderr.
257  *
258  * @returns Whether the communication with the subprocess was successful.
259  */
260  bool communicate(std::istream& in, std::ostream& out, std::ostream& err);
261 
262  /**
263  * @brief Communicate with subprocess.
264  *
265  * @note This method closes the pipes to the subprocess after all data
266  * has been received and returns after the subprocess terminated.
267  * Therefore, the exit code is set upon return.
268  *
269  * @param [in] out Data read from stdout of subprocess. Can be an empty
270  * string if no pipe was created for stdout.
271  * @param [in] err Data read from stderr of subprocess. Can be an empty
272  * string if no pipe was created for stderr.
273  *
274  * @returns Whether the communication with the subprocess was successful.
275  */
276  bool communicate(std::ostream& out, std::ostream& err);
277 
278  /**
279  * @brief Communicate with subprocess.
280  *
281  * @note This method closes the pipes to the subprocess after all data
282  * has been received and returns after the subprocess terminated.
283  * Therefore, the exit code is set upon return.
284  *
285  * @param [in] out Data read from stdout of subprocess. Can be an empty
286  * string if no pipe was created for stdout.
287  *
288  * @returns Whether the communication with the subprocess was successful.
289  */
290  bool communicate(std::ostream& out);
291 
292  /**
293  * @brief Write data to stdin of subprocess.
294  *
295  * @param [in] buf Bytes to write.
296  * @param [in] nbuf Number of bytes from @p buf to write.
297  *
298  * @returns Number of bytes written or -1 on error.
299  */
300  int write(const void* buf, size_t nbuf);
301 
302  /**
303  * @brief Read data from stdout or stderr of subprocess.
304  *
305  * @param [out] buf Allocated buffer to store read data to.
306  * @param [in] nbuf Number of bytes to read from subprocess.
307  * @param [in] err If true and the redirection mode of stderr is RM_PIPE,
308  * the data is read from stderr of the subprocess.
309  * Otherwise, the data is read from stdout.
310  *
311  * @returns Number of bytes read or -1 on error.
312  */
313  int read(void* buf, size_t nbuf, bool err = false);
314 
315  // -----------------------------------------------------------------------
316  // process execution
317 public:
318 
319  /**
320  * @brief Execute command as subprocess.
321  *
322  * This function is implemented in the same manner as system() on Unix.
323  * It simply creates a Subprocess instance, executes the subprocess and
324  * waits for its termination.
325  *
326  * Example:
327  * @code
328  * Subprocess::CommandLine cmd;
329  * cmd.push_back("ls");
330  * cmd.push_back("some directory");
331  * int status = Subprocess::call(cmd);
332  * @endcode
333  *
334  * @returns Exit code of subprocess or -1 on error.
335  */
336  static int call(const CommandLine& cmd);
337 
338  /**
339  * @brief Execute command as subprocess.
340  *
341  * This function is implemented in the same manner as system() on Unix.
342  * It simply creates a Subprocess instance, executes the subprocess and
343  * waits for its termination.
344  *
345  * Example:
346  * @code
347  * int status = Subprocess::call("ls \"some directory\"");
348  * @endcode
349  *
350  * @param [in] cmd Command-line given as single string. Quote arguments
351  * containing whitespace characters using ".
352  *
353  * @returns Exit code of subprocess or -1 on error.
354  */
355  static int call(const std::string& cmd);
356 
357  // -----------------------------------------------------------------------
358  // unsupported operations
359 private:
360 
361  /**
362  * @brief Copy constructor.
363  *
364  * @note Intentionally not implemented.
365  */
366  Subprocess(const Subprocess&);
367 
368  /**
369  * @brief Assignment operator.
370  *
371  * @note Intentionally not implemented.
372  */
373  void operator=(const Subprocess&);
374 
375  // -----------------------------------------------------------------------
376  // members
377 private:
378 
379  Information _info; ///< Subprocess information.
380  PipeHandle _stdin; ///< Used to write data to stdin of subprocess.
381  PipeHandle _stdout; ///< Used to read data from stdout of subprocess.
382  PipeHandle _stderr; ///< Used to read data from stderr of subprocess.
383  mutable int _status; ///< Status of subprocess.
384 
385 }; // class Subprocess
386 
387 
388 } // namespace basis
389 
390 
391 #endif // _BASIS_SUBPROCESS_H
RedirectMode
Modes of redirection for standard input/output buffers.
Definition: subprocess.h:75
std::vector< std::string > CommandLine
Definition: subprocess.h:46
bool kill()
Kill subprocess.
Definition: subprocess.cxx:565
bool popen(const CommandLine &args, const RedirectMode rm_in=RM_NONE, const RedirectMode rm_out=RM_NONE, const RedirectMode rm_err=RM_NONE, const Environment *env=NULL)
Open new subprocess.
Definition: subprocess.cxx:218
bool signaled() const
Definition: subprocess.cxx:575
int write(const void *buf, size_t nbuf)
Write data to stdin of subprocess.
Definition: subprocess.cxx:738
System related macro definitions.
Do not redirect the input/output.
Definition: subprocess.h:77
bool send_signal(int signal)
Send signal to subprocess.
Definition: subprocess.cxx:529
Redirect stderr to stdout.
Definition: subprocess.h:79
Definition: basis.h:34
static int call(const CommandLine &cmd)
Execute command as subprocess.
Definition: subprocess.cxx:771
int read(void *buf, size_t nbuf, bool err=false)
Read data from stdout or stderr of subprocess.
Definition: subprocess.cxx:751
bool wait()
Wait for subprocess to terminate.
Definition: subprocess.cxx:504
std::vector< std::string > Environment
Definition: subprocess.h:47
Platform-independent interface to create and control a subprocess.
Definition: subprocess.h:40
bool terminate()
Terminate subprocess.
Definition: subprocess.cxx:541
bool popen(const std::string &cmd, const RedirectMode rm_in=RM_NONE, const RedirectMode rm_out=RM_NONE, const RedirectMode rm_err=RM_NONE, const Environment *env=NULL)
Open new subprocess.
Definition: subprocess.h:172
Subprocess()
Default constructor.
Definition: subprocess.cxx:177
int returncode() const
Definition: subprocess.cxx:620
static std::string tostring(const CommandLine &args)
Convert argument vector to double quoted string.
Definition: subprocess.cxx:141
int pid() const
Definition: subprocess.cxx:610
Use a pipe to redirect the input/output from/to the parent.
Definition: subprocess.h:78
static CommandLine split(const std::string &cmd)
Split double quoted string into arguments.
Definition: subprocess.cxx:71
~Subprocess()
Terminate running subprocess and close all related handles.
Definition: subprocess.cxx:194
bool communicate(std::istream &in, std::ostream &out, std::ostream &err)
Communicate with subprocess.
Definition: subprocess.cxx:634
bool poll() const
Check if subprocess terminated and update return code.
Definition: subprocess.cxx:466