-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgramExtra.cpp
241 lines (189 loc) · 7.16 KB
/
ProgramExtra.cpp
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
//===- ProgramExtra.cpp - Program extra utilities definitions -------------===//
//
// Copyright 2024 Ehud Katz <ehudkatz@gmail.com>
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#include "ProgramExtra.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Config/config.h"
#include <unistd.h>
using namespace llvm;
using namespace llvm::sys;
namespace {
struct Pipe : public std::array<int, 2> {
enum { ReadId = 0, WriteId = 1 };
Pipe() { fill(-1); }
operator bool() const { return front() != -1; }
int getRead() const { return at(ReadId); }
int getWrite() const { return at(WriteId); }
void closeRead() {
if (at(ReadId) != -1)
fs::closeFile(at(ReadId));
}
void closeWrite() {
if (at(WriteId) != -1)
fs::closeFile(at(WriteId));
}
void close() {
closeRead();
closeWrite();
}
Error open() {
if (::pipe(data()) == -1) {
std::error_code ec(errno, std::generic_category());
return make_error<StringError>(
"Could not create in pipe: " + ec.message(), ec);
}
return Error::success();
}
};
using PipeArray = std::array<Pipe, 3>;
} // anonymous namespace
// We need to override the `Execute`, `RedirectIO` and `RedirectIO_PS` to extend
// their functionality to support redirecting the pipe FDs.
bool Execute(ProcessInfo &pi, StringRef program, ArrayRef<StringRef> args,
std::optional<ArrayRef<StringRef>> env,
ArrayRef<std::optional<StringRef>> redirects, unsigned memoryLimit,
std::string *errMsg, BitVector *affinityMask);
#define ExecuteProcessInfo ExecuteImpl(PipeArray &pipes, ProcessInfo
#define ExecutePI Execute(PI
#define Execute(PI, Program, Args, Env, Redirects, MemoryLimit, ErrMsg, \
AffinityMask) \
Execute##PI, Program, Args, Env,Redirects, MemoryLimit, ErrMsg, AffinityMask)
static bool RedirectIO(PipeArray &pipes, std::optional<StringRef> path, int fd,
std::string *errMsg);
#define RedirectIOstd RedirectIO_NoPipe(std
#define RedirectIORedirects RedirectIO(pipes, Redirects
#define RedirectIO(Path, FD, ErrMsg) RedirectIO##Path, FD, ErrMsg)
#ifdef HAVE_POSIX_SPAWN
#include <spawn.h>
static bool RedirectIO_PS(PipeArray &pipes, const std::string *path, int fd,
std::string *errMsg,
posix_spawn_file_actions_t *fileActions);
#define RedirectIO_PSconst RedirectIO_PS_NoPipe(const
#define RedirectIO_PSRedirectsStr RedirectIO_PS(pipes, RedirectsStr
#define RedirectIO_PS(Path, FD, ErrMsg, FileActions) \
RedirectIO_PS##Path, FD, ErrMsg, FileActions)
#endif
#include "lib/Support/Program.cpp"
#undef Execute
#undef RedirectIO
#ifdef HAVE_POSIX_SPAWN
#undef RedirectIO_PS
#endif
bool Execute(ProcessInfo &pi, StringRef program, ArrayRef<StringRef> args,
std::optional<ArrayRef<StringRef>> env,
ArrayRef<std::optional<StringRef>> redirects, unsigned memoryLimit,
std::string *errMsg, BitVector *affinityMask) {
PipeArray pipes;
return ExecuteImpl(pipes, pi, program, args, env, redirects, memoryLimit,
errMsg, affinityMask);
}
static bool RedirectIO(PipeArray &pipes, std::optional<StringRef> path, int fd,
std::string *errMsg) {
auto &pipe = pipes[fd];
if (!pipe)
return RedirectIO_NoPipe(path, fd, errMsg);
unsigned dupFd, unusedFd;
if (fd == STDIN_FILENO) {
dupFd = Pipe::ReadId;
unusedFd = Pipe::WriteId;
} else {
dupFd = Pipe::WriteId;
unusedFd = Pipe::ReadId;
}
// Close the unused endpoint.
close(pipe[unusedFd]);
// Bind `fd` to the requested end of the pipe.
bool failed = dup2(pipe[dupFd], fd) == -1;
if (failed)
MakeErrMsg(errMsg, "Cannot dup2");
// Close the original descriptor.
close(pipe[dupFd]);
return failed;
}
#ifdef HAVE_POSIX_SPAWN
static bool RedirectIO_PS(PipeArray &pipes, const std::string *path, int fd,
std::string *errMsg,
posix_spawn_file_actions_t *fileActions) {
auto &pipe = pipes[fd];
if (!pipe)
return RedirectIO_PS_NoPipe(path, fd, errMsg, fileActions);
unsigned dupFd, unusedFd;
if (fd == STDIN_FILENO) {
dupFd = Pipe::ReadId;
unusedFd = Pipe::WriteId;
} else {
dupFd = Pipe::WriteId;
unusedFd = Pipe::ReadId;
}
// Close the unused endpoint.
posix_spawn_file_actions_addclose(fileActions, pipe[unusedFd]);
// Bind `fd` to the requested end of the pipe.
int err = posix_spawn_file_actions_adddup2(fileActions, pipe[dupFd], fd);
if (err)
MakeErrMsg(errMsg, "Cannot posix_spawn_file_actions_adddup2", err);
// Close the original descriptor.
posix_spawn_file_actions_addclose(fileActions, pipe[dupFd]);
return err != 0;
}
#endif
Expected<std::unique_ptr<MemoryBuffer>> llvm::executeAndWaitWithPipe(
StringRef program, ArrayRef<StringRef> args,
std::optional<ArrayRef<StringRef>> env, std::optional<StringRef> input,
std::unique_ptr<MemoryBuffer> *errBuf, unsigned secondsToWait,
unsigned memoryLimit, bool *executionFailed,
std::optional<ProcessStatistics> *procStat, BitVector *affinityMask) {
PipeArray pipes;
auto autoClose = make_scope_exit([&pipes] {
for (Pipe &pipe : pipes)
pipe.close();
});
// If `errBuf` is valid, then we expect to return a separate buffer for
// stderr, otherwise, it is redirected to stdout, and we do not need to open a
// pipe for the stderr (the 3rd pipe).
size_t numPipes = errBuf ? 3 : 2;
for (size_t i = 0; i < numPipes; ++i)
if (Error err = pipes[i].open())
return std::move(err);
ProcessInfo pi;
std::optional<StringRef> redirects[] = {
StringRef("|0"),
StringRef("|1"),
StringRef(errBuf ? "|2" : "|1"),
};
std::string errMsg;
bool failed = !ExecuteImpl(pipes, pi, program, args, env, redirects,
memoryLimit, &errMsg, affinityMask);
if (executionFailed)
*executionFailed = failed;
if (failed)
return make_error<StringError>(
errMsg, std::make_error_code(std::errc::invalid_argument));
pipes[STDIN_FILENO].closeRead();
pipes[STDOUT_FILENO].closeWrite();
pipes[STDERR_FILENO].closeWrite();
if (input)
raw_fd_ostream(pipes[STDIN_FILENO].getWrite(), false) << *input;
pipes[STDIN_FILENO].closeWrite();
// Two output buffers: first for stdout, second for stderr.
std::unique_ptr<MemoryBuffer> outputBufs[2];
for (size_t i = 1; i < numPipes; ++i) {
ErrorOr<std::unique_ptr<MemoryBuffer>> memBufOrErr =
MemoryBuffer::getOpenFile(pipes[i].getRead(), {}, -1);
if (std::error_code ec = memBufOrErr.getError())
return make_error<StringError>("Could not open pipe: " + ec.message(),
ec);
outputBufs[i - 1] = std::move(*memBufOrErr);
}
if (errBuf)
*errBuf = std::move(outputBufs[1]);
pi =
Wait(pi, secondsToWait == 0 ? std::nullopt : std::optional(secondsToWait),
&errMsg, procStat);
if (pi.ReturnCode < 0)
return make_error<StringError>(
errMsg, std::make_error_code(std::errc::invalid_argument));
return std::move(outputBufs[0]);
}