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
119
120
121
122
123
124
125
126
|
// 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 <cstdint> // uint32_t
#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_;
};
class process
{
public:
// 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 (handle != 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, handle is 0
// and exit status is 0.
//
process ();
public:
#ifndef _WIN32
using handle_type = pid_t;
using id_type = pid_t;
using status_type = int;
#else
using handle_type = void*; // Win32 HANDLE
using id_type = std::uint32_t; // Win32 DWORD
#endif
static id_type
current_id ();
public:
handle_type handle;
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
|