1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
// file : butl/process -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
#ifndef BUTL_PROCESS
#define BUTL_PROCESS
#ifndef _WIN32
# include <sys/types.h> // pid_t
#endif
#include <cassert>
#include <system_error>
namespace butl
{
struct process_error: std::system_error
{
process_error (int e, bool child)
: system_error (e, std::system_category ()), child_ (child) {}
bool
child () const {return child_;}
private:
bool child_;
};
struct process
{
// Start another process using the specified command line. The default
// values to the in, out and err arguments indicate that the child
// process should inherit the parent process stdin, stdout, and stderr,
// respectively. If -1 is passed instead, then the corresponding child
// process descriptor is connected (via a pipe) to out_fd for stdin,
// in_ofd for stdout, and in_efd for stderr (see data members below).
//
// Instead of passing -1 or the default value, you can also pass your
// own descriptors. Note, however, that in this case they are not
// closed by the parent. So you should do this yourself, if required.
// For example, to redirect the child process stdout to stderr, you
// can do:
//
// process p (..., 0, 2);
//
// Throw process_error if anything goes wrong. Note that some of the
// exceptions (e.g., if exec() failed) can be thrown in the child
// version of us.
//
process (char const* const args[], int in = 0, int out = 1, int err = 2);
// The "piping" constructor, for example:
//
// process lhs (..., 0, -1); // Redirect stdout to a pipe.
// process rhs (..., lhs); // Redirect stdin to lhs's pipe.
//
// rhs.wait (); // Wait for last first.
// lhs.wait ();
//
process (char const* const args[], process& in, int out = 1, int err = 2);
// Versions of the above constructors that allow us to change the
// current working directory of the child process. NULL and empty
// cwd arguments are ignored.
//
process (const char* cwd, char const* const[], int = 0, int = 1, int = 2);
process (const char* cwd, char const* const[], process&, int = 1, int = 2);
// Wait for the process to terminate. Return true if the process
// terminated normally and with the zero exit status. Throw
// process_error if anything goes wrong. This function can be
// called multiple times with subsequent calls simply returning
// the status.
//
bool
wait ();
// Return true if the process has already terminated in which case
// the argument is set to the result of wait().
//
bool
try_wait (bool&);
~process () {if (id != 0) wait ();}
// Moveable-only type.
//
process (process&&);
process& operator= (process&&);
process (const process&) = delete;
process& operator= (const process&) = delete;
// Create an empty or "already terminated" process. That is, id is 0
// and exit status is 0.
//
process ();
public:
#ifndef _WIN32
typedef pid_t id_type;
typedef int status_type;
#else
typedef void* id_type; // Win32 HANDLE.
#endif
id_type id;
status_type status;
int out_fd; // Write to this fd to send to the new process' stdin.
int in_ofd; // Read from this fd to receive from the new process' stdout.
int in_efd; // Read from this fd to receive from the new process' stderr.
};
}
#include <butl/process.ixx>
#endif // BUTL_PROCESS
|