forked from facebook/watchman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ChildProcess.h
181 lines (148 loc) · 5.53 KB
/
ChildProcess.h
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/* Copyright 2017-present Facebook, Inc.
* Licensed under the Apache License, Version 2.0 */
#pragma once
#include "watchman_system.h"
#include <spawn.h>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include "Future.h"
#include "Pipe.h"
#include "thirdparty/jansson/jansson.h"
#include "watchman_string.h"
namespace watchman {
class ChildProcess {
public:
struct Deleter {
void operator()(char** vec) const {
free((void*)vec);
}
};
class Environment {
public:
// Constructs an environment from the current process environment
Environment();
Environment(const Environment&) = default;
/* implicit */ Environment(
const std::unordered_map<w_string, w_string>& map);
Environment& operator=(const Environment&) = default;
// Returns the environment as an environ compatible array
std::unique_ptr<char*, Deleter> asEnviron(size_t* env_size = nullptr) const;
// Set a value in the environment
void set(const w_string& key, const w_string& value);
void set(
std::initializer_list<std::pair<w_string_piece, w_string_piece>> pairs);
void set(const w_string& key, bool bval);
// Remove a value from the environment
void unset(const w_string& key);
private:
std::unordered_map<w_string, w_string> map_;
};
class Options {
public:
Options();
// Not copyable
Options(const Options&) = delete;
Options(Options&&) = default;
Options& operator=(const Options&) = delete;
Options& operator=(Options&&) = default;
#ifdef POSIX_SPAWN_SETSIGMASK
void setSigMask(const sigset_t& mask);
#endif
// Adds flags to the set of flags maintainted in the spawn attributes.
// This is logically equivalent to calling setflags(getflags()|flags)
void setFlags(short flags);
Environment& environment();
// Arranges to duplicate an fd from the parent as targetFd in
// the child process.
void dup2(int sourceFd, int targetFd);
void dup2(const FileDescriptor& fd, int targetFd);
// Arranges to create a pipe for communicating between the
// parent and child process and setting it as targetFd in
// the child.
void pipe(int targetFd, bool childRead);
// Set up stdin with a pipe
void pipeStdin();
// Set up stdout with a pipe
void pipeStdout();
// Set up stderr with a pipe
void pipeStderr();
// Set up stdin with a null device
void nullStdin();
// Arrange to open(2) a file for the child process and make
// it available as targetFd
void open(int targetFd, const char* path, int flags, int mode);
// Arrange to set the cwd for the child process
void chdir(w_string_piece path);
private:
struct Inner {
// There is no defined way to copy or move either of
// these things, so we separate them out into a container
// that we can point to and move the pointer.
posix_spawn_file_actions_t actions;
posix_spawnattr_t attr;
Inner();
~Inner();
};
std::unique_ptr<Inner> inner_;
Environment env_;
std::unordered_map<int, std::unique_ptr<Pipe>> pipes_;
std::string cwd_;
friend class ChildProcess;
};
ChildProcess(std::vector<w_string_piece> args, Options&& options);
ChildProcess(const json_ref& args, Options&& options);
~ChildProcess();
// Check to see if the process has terminated.
// Does not block. Returns true if the process has
// terminated, false otherwise.
bool terminated();
// Wait for the process to terminate and return its
// exit status. If the process has already terminated,
// immediately returns its exit status.
int wait();
// Disassociate from the running process.
// We will no longer be able to wait for it to complete.
// This causes minor leakage of resources.
void disown();
// This mutex is present to avoid fighting over the cwd when multiple
// process might need to chdir concurrently
static std::unique_lock<std::mutex> lockCwdMutex();
// Terminates the process
void kill(
#ifndef _WIN32
int signo = SIGTERM
#endif
);
// The pipeWriteCallback is called by communicate when it is safe to write
// data to the pipe. The callback should then attempt to write to it.
// The callback must return true when it has nothing more
// to write to the input of the child. This will cause the
// pipe to be closed.
// Note that the pipe may be non-blocking, and you must not loop attempting
// to write data to the pipe - the caller will arrange to call you again
// if you return false (e.g. after a partial write).
using pipeWriteCallback = std::function<bool(FileDescriptor&)>;
/** ChildProcess::communicate() performs a read/write operation.
* The provided pipeWriteCallback allows sending data to the input stream.
* communicate() will return with the pair of output and error streams once
* they have been completely consumed. */
std::pair<w_string, w_string> communicate(
pipeWriteCallback writeCallback = [](FileDescriptor&) {
// If not provided by the caller, we're just going to close the input
// stream
return true;
});
// these are public for the sake of testing. You should use the
// communicate() method instead of calling these directly.
std::pair<w_string, w_string> pollingCommunicate(pipeWriteCallback writable);
std::pair<w_string, w_string> threadedCommunicate(pipeWriteCallback writable);
private:
pid_t pid_;
bool waited_{false};
int status_;
std::unordered_map<int, std::unique_ptr<Pipe>> pipes_;
Future<w_string> readPipe(int fd);
};
}