1 |
|
|
// Copyright Joyent, Inc. and other Node contributors. |
2 |
|
|
// |
3 |
|
|
// Permission is hereby granted, free of charge, to any person obtaining a |
4 |
|
|
// copy of this software and associated documentation files (the |
5 |
|
|
// "Software"), to deal in the Software without restriction, including |
6 |
|
|
// without limitation the rights to use, copy, modify, merge, publish, |
7 |
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit |
8 |
|
|
// persons to whom the Software is furnished to do so, subject to the |
9 |
|
|
// following conditions: |
10 |
|
|
// |
11 |
|
|
// The above copyright notice and this permission notice shall be included |
12 |
|
|
// in all copies or substantial portions of the Software. |
13 |
|
|
// |
14 |
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 |
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 |
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 |
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 |
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 |
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 |
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 |
|
|
|
22 |
|
|
#include "spawn_sync.h" |
23 |
|
|
#include "debug_utils-inl.h" |
24 |
|
|
#include "env-inl.h" |
25 |
|
|
#include "node_internals.h" |
26 |
|
|
#include "string_bytes.h" |
27 |
|
|
#include "util-inl.h" |
28 |
|
|
|
29 |
|
|
#include <cstring> |
30 |
|
|
|
31 |
|
|
|
32 |
|
|
namespace node { |
33 |
|
|
|
34 |
|
|
using v8::Array; |
35 |
|
|
using v8::Context; |
36 |
|
|
using v8::EscapableHandleScope; |
37 |
|
|
using v8::FunctionCallbackInfo; |
38 |
|
|
using v8::HandleScope; |
39 |
|
|
using v8::Int32; |
40 |
|
|
using v8::Integer; |
41 |
|
|
using v8::Isolate; |
42 |
|
|
using v8::Just; |
43 |
|
|
using v8::Local; |
44 |
|
|
using v8::Maybe; |
45 |
|
|
using v8::MaybeLocal; |
46 |
|
|
using v8::Nothing; |
47 |
|
|
using v8::Null; |
48 |
|
|
using v8::Number; |
49 |
|
|
using v8::Object; |
50 |
|
|
using v8::String; |
51 |
|
|
using v8::Value; |
52 |
|
|
|
53 |
|
18865 |
void SyncProcessOutputBuffer::OnAlloc(size_t suggested_size, |
54 |
|
|
uv_buf_t* buf) const { |
55 |
✗✓ |
18865 |
if (used() == kBufferSize) |
56 |
|
|
*buf = uv_buf_init(nullptr, 0); |
57 |
|
|
else |
58 |
|
18865 |
*buf = uv_buf_init(data_ + used(), available()); |
59 |
|
18865 |
} |
60 |
|
|
|
61 |
|
|
|
62 |
|
17442 |
void SyncProcessOutputBuffer::OnRead(const uv_buf_t* buf, size_t nread) { |
63 |
|
|
// If we hand out the same chunk twice, this should catch it. |
64 |
✗✓ |
17442 |
CHECK_EQ(buf->base, data_ + used()); |
65 |
|
17442 |
used_ += static_cast<unsigned int>(nread); |
66 |
|
17442 |
} |
67 |
|
|
|
68 |
|
|
|
69 |
|
1626 |
size_t SyncProcessOutputBuffer::Copy(char* dest) const { |
70 |
|
1626 |
memcpy(dest, data_, used()); |
71 |
|
1626 |
return used(); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
|
75 |
|
36290 |
unsigned int SyncProcessOutputBuffer::available() const { |
76 |
|
36290 |
return sizeof data_ - used(); |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
|
80 |
|
96340 |
unsigned int SyncProcessOutputBuffer::used() const { |
81 |
|
96340 |
return used_; |
82 |
|
|
} |
83 |
|
|
|
84 |
|
|
|
85 |
|
4878 |
SyncProcessOutputBuffer* SyncProcessOutputBuffer::next() const { |
86 |
|
4878 |
return next_; |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
|
90 |
|
186 |
void SyncProcessOutputBuffer::set_next(SyncProcessOutputBuffer* next) { |
91 |
|
186 |
next_ = next; |
92 |
|
186 |
} |
93 |
|
|
|
94 |
|
|
|
95 |
|
2612 |
SyncProcessStdioPipe::SyncProcessStdioPipe(SyncProcessRunner* process_handler, |
96 |
|
|
bool readable, |
97 |
|
|
bool writable, |
98 |
|
2612 |
uv_buf_t input_buffer) |
99 |
|
|
: process_handler_(process_handler), |
100 |
|
|
readable_(readable), |
101 |
|
|
writable_(writable), |
102 |
|
|
input_buffer_(input_buffer), |
103 |
|
|
|
104 |
|
|
first_output_buffer_(nullptr), |
105 |
|
|
last_output_buffer_(nullptr), |
106 |
|
|
|
107 |
|
|
uv_pipe_(), |
108 |
|
|
write_req_(), |
109 |
|
|
shutdown_req_(), |
110 |
|
|
|
111 |
|
2612 |
lifecycle_(kUninitialized) { |
112 |
✓✓✗✓
|
2612 |
CHECK(readable || writable); |
113 |
|
2612 |
} |
114 |
|
|
|
115 |
|
|
|
116 |
|
5224 |
SyncProcessStdioPipe::~SyncProcessStdioPipe() { |
117 |
✓✗✗✓
|
2612 |
CHECK(lifecycle_ == kUninitialized || lifecycle_ == kClosed); |
118 |
|
|
|
119 |
|
|
SyncProcessOutputBuffer* buf; |
120 |
|
|
SyncProcessOutputBuffer* next; |
121 |
|
|
|
122 |
✓✓ |
4238 |
for (buf = first_output_buffer_; buf != nullptr; buf = next) { |
123 |
|
1626 |
next = buf->next(); |
124 |
|
1626 |
delete buf; |
125 |
|
|
} |
126 |
|
2612 |
} |
127 |
|
|
|
128 |
|
|
|
129 |
|
2612 |
int SyncProcessStdioPipe::Initialize(uv_loop_t* loop) { |
130 |
✗✓ |
2612 |
CHECK_EQ(lifecycle_, kUninitialized); |
131 |
|
|
|
132 |
|
2612 |
int r = uv_pipe_init(loop, uv_pipe(), 0); |
133 |
✗✓ |
2612 |
if (r < 0) |
134 |
|
|
return r; |
135 |
|
|
|
136 |
|
2612 |
uv_pipe()->data = this; |
137 |
|
|
|
138 |
|
2612 |
lifecycle_ = kInitialized; |
139 |
|
2612 |
return 0; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
|
143 |
|
2177 |
int SyncProcessStdioPipe::Start() { |
144 |
✗✓ |
2177 |
CHECK_EQ(lifecycle_, kInitialized); |
145 |
|
|
|
146 |
|
|
// Set the busy flag already. If this function fails no recovery is |
147 |
|
|
// possible. |
148 |
|
2177 |
lifecycle_ = kStarted; |
149 |
|
|
|
150 |
✓✓ |
2177 |
if (readable()) { |
151 |
✓✓ |
719 |
if (input_buffer_.len > 0) { |
152 |
✗✓ |
24 |
CHECK_NOT_NULL(input_buffer_.base); |
153 |
|
|
|
154 |
|
24 |
int r = uv_write(&write_req_, |
155 |
|
|
uv_stream(), |
156 |
|
24 |
&input_buffer_, |
157 |
|
|
1, |
158 |
|
|
WriteCallback); |
159 |
✗✓ |
24 |
if (r < 0) |
160 |
|
|
return r; |
161 |
|
|
} |
162 |
|
|
|
163 |
|
719 |
int r = uv_shutdown(&shutdown_req_, uv_stream(), ShutdownCallback); |
164 |
✗✓ |
719 |
if (r < 0) |
165 |
|
|
return r; |
166 |
|
|
} |
167 |
|
|
|
168 |
✓✓ |
2177 |
if (writable()) { |
169 |
|
1458 |
int r = uv_read_start(uv_stream(), AllocCallback, ReadCallback); |
170 |
✗✓ |
1458 |
if (r < 0) |
171 |
|
|
return r; |
172 |
|
|
} |
173 |
|
|
|
174 |
|
2177 |
return 0; |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
|
178 |
|
2612 |
void SyncProcessStdioPipe::Close() { |
179 |
✓✓✗✓
|
2612 |
CHECK(lifecycle_ == kInitialized || lifecycle_ == kStarted); |
180 |
|
|
|
181 |
|
2612 |
uv_close(uv_handle(), CloseCallback); |
182 |
|
|
|
183 |
|
2612 |
lifecycle_ = kClosing; |
184 |
|
2612 |
} |
185 |
|
|
|
186 |
|
|
|
187 |
|
1458 |
Local<Object> SyncProcessStdioPipe::GetOutputAsBuffer(Environment* env) const { |
188 |
|
1458 |
size_t length = OutputLength(); |
189 |
|
1458 |
Local<Object> js_buffer = Buffer::New(env, length).ToLocalChecked(); |
190 |
|
1458 |
CopyOutput(Buffer::Data(js_buffer)); |
191 |
|
1458 |
return js_buffer; |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
|
195 |
|
4789 |
bool SyncProcessStdioPipe::readable() const { |
196 |
|
4789 |
return readable_; |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
|
200 |
|
6966 |
bool SyncProcessStdioPipe::writable() const { |
201 |
|
6966 |
return writable_; |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
|
205 |
|
2612 |
uv_stdio_flags SyncProcessStdioPipe::uv_flags() const { |
206 |
|
|
unsigned int flags; |
207 |
|
|
|
208 |
|
2612 |
flags = UV_CREATE_PIPE; |
209 |
✓✓ |
2612 |
if (readable()) |
210 |
|
864 |
flags |= UV_READABLE_PIPE; |
211 |
✓✓ |
2612 |
if (writable()) |
212 |
|
1748 |
flags |= UV_WRITABLE_PIPE; |
213 |
|
|
|
214 |
|
2612 |
return static_cast<uv_stdio_flags>(flags); |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
|
218 |
|
12649 |
uv_pipe_t* SyncProcessStdioPipe::uv_pipe() const { |
219 |
✗✓ |
12649 |
CHECK_LT(lifecycle_, kClosing); |
220 |
|
12649 |
return &uv_pipe_; |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
|
224 |
|
4813 |
uv_stream_t* SyncProcessStdioPipe::uv_stream() const { |
225 |
|
4813 |
return reinterpret_cast<uv_stream_t*>(uv_pipe()); |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
|
229 |
|
2612 |
uv_handle_t* SyncProcessStdioPipe::uv_handle() const { |
230 |
|
2612 |
return reinterpret_cast<uv_handle_t*>(uv_pipe()); |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
|
234 |
|
1458 |
size_t SyncProcessStdioPipe::OutputLength() const { |
235 |
|
|
SyncProcessOutputBuffer* buf; |
236 |
|
1458 |
size_t size = 0; |
237 |
|
|
|
238 |
✓✓ |
3084 |
for (buf = first_output_buffer_; buf != nullptr; buf = buf->next()) |
239 |
|
1626 |
size += buf->used(); |
240 |
|
|
|
241 |
|
1458 |
return size; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
|
245 |
|
1458 |
void SyncProcessStdioPipe::CopyOutput(char* dest) const { |
246 |
|
|
SyncProcessOutputBuffer* buf; |
247 |
|
1458 |
size_t offset = 0; |
248 |
|
|
|
249 |
✓✓ |
3084 |
for (buf = first_output_buffer_; buf != nullptr; buf = buf->next()) |
250 |
|
1626 |
offset += buf->Copy(dest + offset); |
251 |
|
1458 |
} |
252 |
|
|
|
253 |
|
|
|
254 |
|
18865 |
void SyncProcessStdioPipe::OnAlloc(size_t suggested_size, uv_buf_t* buf) { |
255 |
|
|
// This function assumes that libuv will never allocate two buffers for the |
256 |
|
|
// same stream at the same time. There's an assert in |
257 |
|
|
// SyncProcessOutputBuffer::OnRead that would fail if this assumption was |
258 |
|
|
// ever violated. |
259 |
|
|
|
260 |
✓✓ |
18865 |
if (last_output_buffer_ == nullptr) { |
261 |
|
|
// Allocate the first capture buffer. |
262 |
|
1440 |
first_output_buffer_ = new SyncProcessOutputBuffer(); |
263 |
|
1440 |
last_output_buffer_ = first_output_buffer_; |
264 |
|
|
|
265 |
✓✓ |
17425 |
} else if (last_output_buffer_->available() == 0) { |
266 |
|
|
// The current capture buffer is full so get us a new one. |
267 |
|
186 |
SyncProcessOutputBuffer* buf = new SyncProcessOutputBuffer(); |
268 |
|
186 |
last_output_buffer_->set_next(buf); |
269 |
|
186 |
last_output_buffer_ = buf; |
270 |
|
|
} |
271 |
|
|
|
272 |
|
18865 |
last_output_buffer_->OnAlloc(suggested_size, buf); |
273 |
|
18865 |
} |
274 |
|
|
|
275 |
|
|
|
276 |
|
18874 |
void SyncProcessStdioPipe::OnRead(const uv_buf_t* buf, ssize_t nread) { |
277 |
✓✓ |
18874 |
if (nread == UV_EOF) { |
278 |
|
|
// Libuv implicitly stops reading on EOF. |
279 |
|
|
|
280 |
✗✓ |
17442 |
} else if (nread < 0) { |
281 |
|
|
SetError(static_cast<int>(nread)); |
282 |
|
|
// At some point libuv should really implicitly stop reading on error. |
283 |
|
|
uv_read_stop(uv_stream()); |
284 |
|
|
|
285 |
|
|
} else { |
286 |
|
17442 |
last_output_buffer_->OnRead(buf, nread); |
287 |
|
17442 |
process_handler_->IncrementBufferSizeAndCheckOverflow(nread); |
288 |
|
|
} |
289 |
|
18874 |
} |
290 |
|
|
|
291 |
|
|
|
292 |
|
24 |
void SyncProcessStdioPipe::OnWriteDone(int result) { |
293 |
✗✓ |
24 |
if (result < 0) |
294 |
|
|
SetError(result); |
295 |
|
24 |
} |
296 |
|
|
|
297 |
|
|
|
298 |
|
719 |
void SyncProcessStdioPipe::OnShutdownDone(int result) { |
299 |
✗✓ |
719 |
if (result < 0) |
300 |
|
|
SetError(result); |
301 |
|
719 |
} |
302 |
|
|
|
303 |
|
|
|
304 |
|
2612 |
void SyncProcessStdioPipe::OnClose() { |
305 |
|
2612 |
lifecycle_ = kClosed; |
306 |
|
2612 |
} |
307 |
|
|
|
308 |
|
|
|
309 |
|
|
void SyncProcessStdioPipe::SetError(int error) { |
310 |
|
|
CHECK_NE(error, 0); |
311 |
|
|
process_handler_->SetPipeError(error); |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
|
315 |
|
18865 |
void SyncProcessStdioPipe::AllocCallback(uv_handle_t* handle, |
316 |
|
|
size_t suggested_size, |
317 |
|
|
uv_buf_t* buf) { |
318 |
|
18865 |
SyncProcessStdioPipe* self = |
319 |
|
|
reinterpret_cast<SyncProcessStdioPipe*>(handle->data); |
320 |
|
18865 |
self->OnAlloc(suggested_size, buf); |
321 |
|
18865 |
} |
322 |
|
|
|
323 |
|
|
|
324 |
|
18874 |
void SyncProcessStdioPipe::ReadCallback(uv_stream_t* stream, |
325 |
|
|
ssize_t nread, |
326 |
|
|
const uv_buf_t* buf) { |
327 |
|
18874 |
SyncProcessStdioPipe* self = |
328 |
|
|
reinterpret_cast<SyncProcessStdioPipe*>(stream->data); |
329 |
|
18874 |
self->OnRead(buf, nread); |
330 |
|
18874 |
} |
331 |
|
|
|
332 |
|
|
|
333 |
|
24 |
void SyncProcessStdioPipe::WriteCallback(uv_write_t* req, int result) { |
334 |
|
24 |
SyncProcessStdioPipe* self = |
335 |
|
24 |
reinterpret_cast<SyncProcessStdioPipe*>(req->handle->data); |
336 |
|
24 |
self->OnWriteDone(result); |
337 |
|
24 |
} |
338 |
|
|
|
339 |
|
|
|
340 |
|
719 |
void SyncProcessStdioPipe::ShutdownCallback(uv_shutdown_t* req, int result) { |
341 |
|
719 |
SyncProcessStdioPipe* self = |
342 |
|
719 |
reinterpret_cast<SyncProcessStdioPipe*>(req->handle->data); |
343 |
|
|
|
344 |
|
|
// On AIX, OS X and the BSDs, calling shutdown() on one end of a pipe |
345 |
|
|
// when the other end has closed the connection fails with ENOTCONN. |
346 |
|
|
// Libuv is not the right place to handle that because it can't tell |
347 |
|
|
// if the error is genuine but we here can. |
348 |
✗✓ |
719 |
if (result == UV_ENOTCONN) |
349 |
|
|
result = 0; |
350 |
|
|
|
351 |
|
719 |
self->OnShutdownDone(result); |
352 |
|
719 |
} |
353 |
|
|
|
354 |
|
|
|
355 |
|
2612 |
void SyncProcessStdioPipe::CloseCallback(uv_handle_t* handle) { |
356 |
|
2612 |
SyncProcessStdioPipe* self = |
357 |
|
|
reinterpret_cast<SyncProcessStdioPipe*>(handle->data); |
358 |
|
2612 |
self->OnClose(); |
359 |
|
2612 |
} |
360 |
|
|
|
361 |
|
|
|
362 |
|
4733 |
void SyncProcessRunner::Initialize(Local<Object> target, |
363 |
|
|
Local<Value> unused, |
364 |
|
|
Local<Context> context, |
365 |
|
|
void* priv) { |
366 |
|
4733 |
Environment* env = Environment::GetCurrent(context); |
367 |
|
4733 |
env->SetMethod(target, "spawn", Spawn); |
368 |
|
4733 |
} |
369 |
|
|
|
370 |
|
|
|
371 |
|
898 |
void SyncProcessRunner::Spawn(const FunctionCallbackInfo<Value>& args) { |
372 |
|
898 |
Environment* env = Environment::GetCurrent(args); |
373 |
|
898 |
env->PrintSyncTrace(); |
374 |
|
898 |
SyncProcessRunner p(env); |
375 |
|
|
Local<Value> result; |
376 |
✗✓ |
1796 |
if (!p.Run(args[0]).ToLocal(&result)) return; |
377 |
|
1796 |
args.GetReturnValue().Set(result); |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
|
381 |
|
898 |
SyncProcessRunner::SyncProcessRunner(Environment* env) |
382 |
|
|
: max_buffer_(0), |
383 |
|
|
timeout_(0), |
384 |
|
|
kill_signal_(SIGTERM), |
385 |
|
|
|
386 |
|
|
uv_loop_(nullptr), |
387 |
|
|
|
388 |
|
|
stdio_count_(0), |
389 |
|
|
uv_stdio_containers_(nullptr), |
390 |
|
|
stdio_pipes_initialized_(false), |
391 |
|
|
|
392 |
|
|
uv_process_options_(), |
393 |
|
|
file_buffer_(nullptr), |
394 |
|
|
args_buffer_(nullptr), |
395 |
|
|
env_buffer_(nullptr), |
396 |
|
|
cwd_buffer_(nullptr), |
397 |
|
|
|
398 |
|
|
uv_process_(), |
399 |
|
|
killed_(false), |
400 |
|
|
|
401 |
|
|
buffered_output_size_(0), |
402 |
|
|
exit_status_(-1), |
403 |
|
|
term_signal_(-1), |
404 |
|
|
|
405 |
|
|
uv_timer_(), |
406 |
|
|
kill_timer_initialized_(false), |
407 |
|
|
|
408 |
|
|
error_(0), |
409 |
|
|
pipe_error_(0), |
410 |
|
|
|
411 |
|
|
lifecycle_(kUninitialized), |
412 |
|
|
|
413 |
|
898 |
env_(env) { |
414 |
|
898 |
} |
415 |
|
|
|
416 |
|
|
|
417 |
|
898 |
SyncProcessRunner::~SyncProcessRunner() { |
418 |
✗✓ |
898 |
CHECK_EQ(lifecycle_, kHandlesClosed); |
419 |
|
|
|
420 |
|
898 |
stdio_pipes_.clear(); |
421 |
✓✗ |
898 |
delete[] file_buffer_; |
422 |
✓✗ |
898 |
delete[] args_buffer_; |
423 |
✓✓ |
898 |
delete[] cwd_buffer_; |
424 |
✓✗ |
898 |
delete[] env_buffer_; |
425 |
✓✗ |
898 |
delete[] uv_stdio_containers_; |
426 |
|
898 |
} |
427 |
|
|
|
428 |
|
|
|
429 |
|
54610 |
Environment* SyncProcessRunner::env() const { |
430 |
|
54610 |
return env_; |
431 |
|
|
} |
432 |
|
|
|
433 |
|
898 |
MaybeLocal<Object> SyncProcessRunner::Run(Local<Value> options) { |
434 |
|
898 |
EscapableHandleScope scope(env()->isolate()); |
435 |
|
|
|
436 |
✗✓ |
898 |
CHECK_EQ(lifecycle_, kUninitialized); |
437 |
|
|
|
438 |
|
898 |
Maybe<bool> r = TryInitializeAndRunLoop(options); |
439 |
|
898 |
CloseHandlesAndDeleteLoop(); |
440 |
✗✓ |
898 |
if (r.IsNothing()) return MaybeLocal<Object>(); |
441 |
|
|
|
442 |
|
898 |
Local<Object> result = BuildResultObject(); |
443 |
|
|
|
444 |
|
898 |
return scope.Escape(result); |
445 |
|
|
} |
446 |
|
|
|
447 |
|
898 |
Maybe<bool> SyncProcessRunner::TryInitializeAndRunLoop(Local<Value> options) { |
448 |
|
|
int r; |
449 |
|
|
|
450 |
|
|
// There is no recovery from failure inside TryInitializeAndRunLoop - the |
451 |
|
|
// only option we'd have is to close all handles and destroy the loop. |
452 |
✗✓ |
898 |
CHECK_EQ(lifecycle_, kUninitialized); |
453 |
|
898 |
lifecycle_ = kInitialized; |
454 |
|
|
|
455 |
|
898 |
uv_loop_ = new uv_loop_t; |
456 |
✗✓ |
898 |
if (uv_loop_ == nullptr) { |
457 |
|
|
SetError(UV_ENOMEM); |
458 |
|
|
return Just(false); |
459 |
|
|
} |
460 |
|
|
|
461 |
|
898 |
r = uv_loop_init(uv_loop_); |
462 |
✗✓ |
898 |
if (r < 0) { |
463 |
|
|
delete uv_loop_; |
464 |
|
|
uv_loop_ = nullptr; |
465 |
|
|
SetError(r); |
466 |
|
|
return Just(false); |
467 |
|
|
} |
468 |
|
|
|
469 |
✗✓ |
1796 |
if (!ParseOptions(options).To(&r)) return Nothing<bool>(); |
470 |
|
|
|
471 |
✗✓ |
898 |
if (r < 0) { |
472 |
|
|
SetError(r); |
473 |
|
|
return Just(false); |
474 |
|
|
} |
475 |
|
|
|
476 |
✓✓ |
898 |
if (timeout_ > 0) { |
477 |
|
7 |
r = uv_timer_init(uv_loop_, &uv_timer_); |
478 |
✗✓ |
7 |
if (r < 0) { |
479 |
|
|
SetError(r); |
480 |
|
|
return Just(false); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
7 |
uv_unref(reinterpret_cast<uv_handle_t*>(&uv_timer_)); |
484 |
|
|
|
485 |
|
7 |
uv_timer_.data = this; |
486 |
|
7 |
kill_timer_initialized_ = true; |
487 |
|
|
|
488 |
|
|
// Start the timer immediately. If uv_spawn fails then |
489 |
|
|
// CloseHandlesAndDeleteLoop() will immediately close the timer handle |
490 |
|
|
// which implicitly stops it, so there is no risk that the timeout callback |
491 |
|
|
// runs when the process didn't start. |
492 |
|
7 |
r = uv_timer_start(&uv_timer_, KillTimerCallback, timeout_, 0); |
493 |
✗✓ |
7 |
if (r < 0) { |
494 |
|
|
SetError(r); |
495 |
|
|
return Just(false); |
496 |
|
|
} |
497 |
|
|
} |
498 |
|
|
|
499 |
|
898 |
uv_process_options_.exit_cb = ExitCallback; |
500 |
|
898 |
r = uv_spawn(uv_loop_, &uv_process_, &uv_process_options_); |
501 |
✓✓ |
898 |
if (r < 0) { |
502 |
|
145 |
SetError(r); |
503 |
|
145 |
return Just(false); |
504 |
|
|
} |
505 |
|
753 |
uv_process_.data = this; |
506 |
|
|
|
507 |
✓✓ |
3012 |
for (const auto& pipe : stdio_pipes_) { |
508 |
✓✓ |
2259 |
if (pipe != nullptr) { |
509 |
|
2177 |
r = pipe->Start(); |
510 |
✗✓ |
2177 |
if (r < 0) { |
511 |
|
|
SetPipeError(r); |
512 |
|
|
return Just(false); |
513 |
|
|
} |
514 |
|
|
} |
515 |
|
|
} |
516 |
|
|
|
517 |
|
753 |
r = uv_run(uv_loop_, UV_RUN_DEFAULT); |
518 |
✗✓ |
753 |
if (r < 0) |
519 |
|
|
// We can't handle uv_run failure. |
520 |
|
|
ABORT(); |
521 |
|
|
|
522 |
|
|
// If we get here the process should have exited. |
523 |
✗✓ |
753 |
CHECK_GE(exit_status_, 0); |
524 |
|
753 |
return Just(true); |
525 |
|
|
} |
526 |
|
|
|
527 |
|
|
|
528 |
|
898 |
void SyncProcessRunner::CloseHandlesAndDeleteLoop() { |
529 |
✗✓ |
898 |
CHECK_LT(lifecycle_, kHandlesClosed); |
530 |
|
|
|
531 |
✓✗ |
898 |
if (uv_loop_ != nullptr) { |
532 |
|
898 |
CloseStdioPipes(); |
533 |
|
898 |
CloseKillTimer(); |
534 |
|
|
// Close the process handle when ExitCallback was not called. |
535 |
|
898 |
uv_handle_t* uv_process_handle = |
536 |
|
|
reinterpret_cast<uv_handle_t*>(&uv_process_); |
537 |
|
|
|
538 |
|
|
// Close the process handle if it is still open. The handle type also |
539 |
|
|
// needs to be checked because TryInitializeAndRunLoop() won't spawn a |
540 |
|
|
// process if input validation fails. |
541 |
✓✗✓✓ ✓✓ |
1796 |
if (uv_process_handle->type == UV_PROCESS && |
542 |
|
898 |
!uv_is_closing(uv_process_handle)) |
543 |
|
145 |
uv_close(uv_process_handle, nullptr); |
544 |
|
|
|
545 |
|
|
// Give closing watchers a chance to finish closing and get their close |
546 |
|
|
// callbacks called. |
547 |
|
898 |
int r = uv_run(uv_loop_, UV_RUN_DEFAULT); |
548 |
✗✓ |
898 |
if (r < 0) |
549 |
|
|
ABORT(); |
550 |
|
|
|
551 |
|
898 |
CheckedUvLoopClose(uv_loop_); |
552 |
|
898 |
delete uv_loop_; |
553 |
|
898 |
uv_loop_ = nullptr; |
554 |
|
|
|
555 |
|
|
} else { |
556 |
|
|
// If the loop doesn't exist, neither should any pipes or timers. |
557 |
|
|
CHECK_EQ(false, stdio_pipes_initialized_); |
558 |
|
|
CHECK_EQ(false, kill_timer_initialized_); |
559 |
|
|
} |
560 |
|
|
|
561 |
|
898 |
lifecycle_ = kHandlesClosed; |
562 |
|
898 |
} |
563 |
|
|
|
564 |
|
|
|
565 |
|
911 |
void SyncProcessRunner::CloseStdioPipes() { |
566 |
✗✓ |
911 |
CHECK_LT(lifecycle_, kHandlesClosed); |
567 |
|
|
|
568 |
✓✓ |
911 |
if (stdio_pipes_initialized_) { |
569 |
✗✓ |
898 |
CHECK(!stdio_pipes_.empty()); |
570 |
✗✓ |
898 |
CHECK_NOT_NULL(uv_loop_); |
571 |
|
|
|
572 |
✓✓ |
3592 |
for (const auto& pipe : stdio_pipes_) { |
573 |
✓✓ |
2694 |
if (pipe) |
574 |
|
2612 |
pipe->Close(); |
575 |
|
|
} |
576 |
|
|
|
577 |
|
898 |
stdio_pipes_initialized_ = false; |
578 |
|
|
} |
579 |
|
911 |
} |
580 |
|
|
|
581 |
|
|
|
582 |
|
911 |
void SyncProcessRunner::CloseKillTimer() { |
583 |
✗✓ |
911 |
CHECK_LT(lifecycle_, kHandlesClosed); |
584 |
|
|
|
585 |
✓✓ |
911 |
if (kill_timer_initialized_) { |
586 |
✗✓ |
7 |
CHECK_GT(timeout_, 0); |
587 |
✗✓ |
7 |
CHECK_NOT_NULL(uv_loop_); |
588 |
|
|
|
589 |
|
7 |
uv_handle_t* uv_timer_handle = reinterpret_cast<uv_handle_t*>(&uv_timer_); |
590 |
|
7 |
uv_ref(uv_timer_handle); |
591 |
|
7 |
uv_close(uv_timer_handle, KillTimerCloseCallback); |
592 |
|
|
|
593 |
|
7 |
kill_timer_initialized_ = false; |
594 |
|
|
} |
595 |
|
911 |
} |
596 |
|
|
|
597 |
|
|
|
598 |
|
13 |
void SyncProcessRunner::Kill() { |
599 |
|
|
// Only attempt to kill once. |
600 |
✗✓ |
13 |
if (killed_) |
601 |
|
|
return; |
602 |
|
13 |
killed_ = true; |
603 |
|
|
|
604 |
|
|
// We might get here even if the process we spawned has already exited. This |
605 |
|
|
// could happen when our child process spawned another process which |
606 |
|
|
// inherited (one of) the stdio pipes. In this case we won't attempt to send |
607 |
|
|
// a signal to the process, however we will still close our end of the stdio |
608 |
|
|
// pipes so this situation won't make us hang. |
609 |
✓✗ |
13 |
if (exit_status_ < 0) { |
610 |
|
13 |
int r = uv_process_kill(&uv_process_, kill_signal_); |
611 |
|
|
|
612 |
|
|
// If uv_kill failed with an error that isn't ESRCH, the user probably |
613 |
|
|
// specified an invalid or unsupported signal. Signal this to the user as |
614 |
|
|
// and error and kill the process with SIGKILL instead. |
615 |
✗✓✗✗
|
13 |
if (r < 0 && r != UV_ESRCH) { |
616 |
|
|
SetError(r); |
617 |
|
|
|
618 |
|
|
// Deliberately ignore the return value, we might not have |
619 |
|
|
// sufficient privileges to signal the child process. |
620 |
|
|
USE(uv_process_kill(&uv_process_, SIGKILL)); |
621 |
|
|
} |
622 |
|
|
} |
623 |
|
|
|
624 |
|
|
// Close all stdio pipes. |
625 |
|
13 |
CloseStdioPipes(); |
626 |
|
|
|
627 |
|
|
// Stop the timeout timer immediately. |
628 |
|
13 |
CloseKillTimer(); |
629 |
|
|
} |
630 |
|
|
|
631 |
|
|
|
632 |
|
17442 |
void SyncProcessRunner::IncrementBufferSizeAndCheckOverflow(ssize_t length) { |
633 |
|
17442 |
buffered_output_size_ += length; |
634 |
|
|
|
635 |
✓✗✓✓
|
17442 |
if (max_buffer_ > 0 && buffered_output_size_ > max_buffer_) { |
636 |
|
8 |
SetError(UV_ENOBUFS); |
637 |
|
8 |
Kill(); |
638 |
|
|
} |
639 |
|
17442 |
} |
640 |
|
|
|
641 |
|
|
|
642 |
|
753 |
void SyncProcessRunner::OnExit(int64_t exit_status, int term_signal) { |
643 |
✗✓ |
753 |
if (exit_status < 0) |
644 |
|
|
return SetError(static_cast<int>(exit_status)); |
645 |
|
|
|
646 |
|
753 |
exit_status_ = exit_status; |
647 |
|
753 |
term_signal_ = term_signal; |
648 |
|
|
} |
649 |
|
|
|
650 |
|
|
|
651 |
|
5 |
void SyncProcessRunner::OnKillTimerTimeout() { |
652 |
|
5 |
SetError(UV_ETIMEDOUT); |
653 |
|
5 |
Kill(); |
654 |
|
5 |
} |
655 |
|
|
|
656 |
|
|
|
657 |
|
1056 |
int SyncProcessRunner::GetError() { |
658 |
✓✓ |
1056 |
if (error_ != 0) |
659 |
|
316 |
return error_; |
660 |
|
|
else |
661 |
|
740 |
return pipe_error_; |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
|
665 |
|
158 |
void SyncProcessRunner::SetError(int error) { |
666 |
✓✗ |
158 |
if (error_ == 0) |
667 |
|
158 |
error_ = error; |
668 |
|
158 |
} |
669 |
|
|
|
670 |
|
|
|
671 |
|
|
void SyncProcessRunner::SetPipeError(int pipe_error) { |
672 |
|
|
if (pipe_error_ == 0) |
673 |
|
|
pipe_error_ = pipe_error; |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
|
677 |
|
898 |
Local<Object> SyncProcessRunner::BuildResultObject() { |
678 |
|
898 |
EscapableHandleScope scope(env()->isolate()); |
679 |
|
898 |
Local<Context> context = env()->context(); |
680 |
|
|
|
681 |
|
898 |
Local<Object> js_result = Object::New(env()->isolate()); |
682 |
|
|
|
683 |
✓✓ |
898 |
if (GetError() != 0) { |
684 |
|
158 |
js_result->Set(context, env()->error_string(), |
685 |
|
632 |
Integer::New(env()->isolate(), GetError())).Check(); |
686 |
|
|
} |
687 |
|
|
|
688 |
✓✓ |
898 |
if (exit_status_ >= 0) { |
689 |
✓✓ |
753 |
if (term_signal_ > 0) { |
690 |
|
32 |
js_result->Set(context, env()->status_string(), |
691 |
|
128 |
Null(env()->isolate())).Check(); |
692 |
|
|
} else { |
693 |
|
721 |
js_result->Set(context, env()->status_string(), |
694 |
|
|
Number::New(env()->isolate(), |
695 |
|
2884 |
static_cast<double>(exit_status_))).Check(); |
696 |
|
|
} |
697 |
|
|
} else { |
698 |
|
|
// If exit_status_ < 0 the process was never started because of some error. |
699 |
|
145 |
js_result->Set(context, env()->status_string(), |
700 |
|
580 |
Null(env()->isolate())).Check(); |
701 |
|
|
} |
702 |
|
|
|
703 |
✓✓ |
898 |
if (term_signal_ > 0) |
704 |
|
32 |
js_result->Set(context, env()->signal_string(), |
705 |
|
32 |
String::NewFromUtf8(env()->isolate(), |
706 |
|
32 |
signo_string(term_signal_)) |
707 |
|
128 |
.ToLocalChecked()) |
708 |
|
|
.Check(); |
709 |
|
|
else |
710 |
|
866 |
js_result->Set(context, env()->signal_string(), |
711 |
|
3464 |
Null(env()->isolate())).Check(); |
712 |
|
|
|
713 |
✓✓ |
898 |
if (exit_status_ >= 0) |
714 |
|
753 |
js_result->Set(context, env()->output_string(), |
715 |
|
3012 |
BuildOutputArray()).Check(); |
716 |
|
|
else |
717 |
|
145 |
js_result->Set(context, env()->output_string(), |
718 |
|
580 |
Null(env()->isolate())).Check(); |
719 |
|
|
|
720 |
|
898 |
js_result->Set(context, env()->pid_string(), |
721 |
|
3592 |
Number::New(env()->isolate(), uv_process_.pid)).Check(); |
722 |
|
|
|
723 |
|
898 |
return scope.Escape(js_result); |
724 |
|
|
} |
725 |
|
|
|
726 |
|
|
|
727 |
|
753 |
Local<Array> SyncProcessRunner::BuildOutputArray() { |
728 |
✗✓ |
753 |
CHECK_GE(lifecycle_, kInitialized); |
729 |
✗✓ |
753 |
CHECK(!stdio_pipes_.empty()); |
730 |
|
|
|
731 |
|
753 |
EscapableHandleScope scope(env()->isolate()); |
732 |
|
753 |
MaybeStackBuffer<Local<Value>, 8> js_output(stdio_pipes_.size()); |
733 |
|
|
|
734 |
✓✓ |
3012 |
for (uint32_t i = 0; i < stdio_pipes_.size(); i++) { |
735 |
|
2259 |
SyncProcessStdioPipe* h = stdio_pipes_[i].get(); |
736 |
✓✓✓✓ ✓✓ |
2259 |
if (h != nullptr && h->writable()) |
737 |
|
2916 |
js_output[i] = h->GetOutputAsBuffer(env()); |
738 |
|
|
else |
739 |
|
1602 |
js_output[i] = Null(env()->isolate()); |
740 |
|
|
} |
741 |
|
|
|
742 |
|
|
return scope.Escape( |
743 |
|
1506 |
Array::New(env()->isolate(), js_output.out(), js_output.length())); |
744 |
|
|
} |
745 |
|
|
|
746 |
|
898 |
Maybe<int> SyncProcessRunner::ParseOptions(Local<Value> js_value) { |
747 |
|
898 |
Isolate* isolate = env()->isolate(); |
748 |
|
1796 |
HandleScope scope(isolate); |
749 |
|
|
int r; |
750 |
|
|
|
751 |
✗✓ |
898 |
if (!js_value->IsObject()) return Just<int>(UV_EINVAL); |
752 |
|
|
|
753 |
|
898 |
Local<Context> context = env()->context(); |
754 |
|
898 |
Local<Object> js_options = js_value.As<Object>(); |
755 |
|
|
|
756 |
|
|
Local<Value> js_file = |
757 |
|
1796 |
js_options->Get(context, env()->file_string()).ToLocalChecked(); |
758 |
✗✓ |
1796 |
if (!CopyJsString(js_file, &file_buffer_).To(&r)) return Nothing<int>(); |
759 |
✗✓ |
898 |
if (r < 0) return Just(r); |
760 |
|
898 |
uv_process_options_.file = file_buffer_; |
761 |
|
|
|
762 |
|
|
Local<Value> js_args = |
763 |
|
1796 |
js_options->Get(context, env()->args_string()).ToLocalChecked(); |
764 |
✗✓ |
1796 |
if (!CopyJsStringArray(js_args, &args_buffer_).To(&r)) return Nothing<int>(); |
765 |
✗✓ |
898 |
if (r < 0) return Just(r); |
766 |
|
898 |
uv_process_options_.args = reinterpret_cast<char**>(args_buffer_); |
767 |
|
|
|
768 |
|
|
Local<Value> js_cwd = |
769 |
|
1796 |
js_options->Get(context, env()->cwd_string()).ToLocalChecked(); |
770 |
✓✓ |
898 |
if (IsSet(js_cwd)) { |
771 |
✗✓ |
186 |
if (!CopyJsString(js_cwd, &cwd_buffer_).To(&r)) return Nothing<int>(); |
772 |
✗✓ |
93 |
if (r < 0) return Just(r); |
773 |
|
93 |
uv_process_options_.cwd = cwd_buffer_; |
774 |
|
|
} |
775 |
|
|
|
776 |
|
|
Local<Value> js_env_pairs = |
777 |
|
1796 |
js_options->Get(context, env()->env_pairs_string()).ToLocalChecked(); |
778 |
✓✗ |
898 |
if (IsSet(js_env_pairs)) { |
779 |
✗✓ |
1796 |
if (!CopyJsStringArray(js_env_pairs, &env_buffer_).To(&r)) |
780 |
|
|
return Nothing<int>(); |
781 |
✗✓ |
898 |
if (r < 0) return Just(r); |
782 |
|
|
|
783 |
|
898 |
uv_process_options_.env = reinterpret_cast<char**>(env_buffer_); |
784 |
|
|
} |
785 |
|
|
Local<Value> js_uid = |
786 |
|
1796 |
js_options->Get(context, env()->uid_string()).ToLocalChecked(); |
787 |
✓✓ |
898 |
if (IsSet(js_uid)) { |
788 |
✗✓ |
1 |
CHECK(js_uid->IsInt32()); |
789 |
|
1 |
const int32_t uid = js_uid.As<Int32>()->Value(); |
790 |
|
1 |
uv_process_options_.uid = static_cast<uv_uid_t>(uid); |
791 |
|
1 |
uv_process_options_.flags |= UV_PROCESS_SETUID; |
792 |
|
|
} |
793 |
|
|
|
794 |
|
|
Local<Value> js_gid = |
795 |
|
1796 |
js_options->Get(context, env()->gid_string()).ToLocalChecked(); |
796 |
✓✓ |
898 |
if (IsSet(js_gid)) { |
797 |
✗✓ |
1 |
CHECK(js_gid->IsInt32()); |
798 |
|
1 |
const int32_t gid = js_gid.As<Int32>()->Value(); |
799 |
|
1 |
uv_process_options_.gid = static_cast<uv_gid_t>(gid); |
800 |
|
1 |
uv_process_options_.flags |= UV_PROCESS_SETGID; |
801 |
|
|
} |
802 |
|
|
|
803 |
|
|
Local<Value> js_detached = |
804 |
|
2694 |
js_options->Get(context, env()->detached_string()).ToLocalChecked(); |
805 |
✓✓ |
898 |
if (js_detached->BooleanValue(isolate)) |
806 |
|
1 |
uv_process_options_.flags |= UV_PROCESS_DETACHED; |
807 |
|
|
|
808 |
|
|
Local<Value> js_win_hide = |
809 |
|
2694 |
js_options->Get(context, env()->windows_hide_string()).ToLocalChecked(); |
810 |
✓✓ |
898 |
if (js_win_hide->BooleanValue(isolate)) |
811 |
|
2 |
uv_process_options_.flags |= UV_PROCESS_WINDOWS_HIDE; |
812 |
|
|
|
813 |
✗✓ |
898 |
if (env()->hide_console_windows()) |
814 |
|
|
uv_process_options_.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; |
815 |
|
|
|
816 |
|
|
Local<Value> js_wva = |
817 |
|
1796 |
js_options->Get(context, env()->windows_verbatim_arguments_string()) |
818 |
|
898 |
.ToLocalChecked(); |
819 |
|
|
|
820 |
✓✓ |
898 |
if (js_wva->BooleanValue(isolate)) |
821 |
|
1 |
uv_process_options_.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; |
822 |
|
|
|
823 |
|
|
Local<Value> js_timeout = |
824 |
|
1796 |
js_options->Get(context, env()->timeout_string()).ToLocalChecked(); |
825 |
✓✓ |
898 |
if (IsSet(js_timeout)) { |
826 |
✗✓ |
8 |
CHECK(js_timeout->IsNumber()); |
827 |
|
8 |
int64_t timeout = js_timeout->IntegerValue(context).FromJust(); |
828 |
|
8 |
timeout_ = static_cast<uint64_t>(timeout); |
829 |
|
|
} |
830 |
|
|
|
831 |
|
|
Local<Value> js_max_buffer = |
832 |
|
1796 |
js_options->Get(context, env()->max_buffer_string()).ToLocalChecked(); |
833 |
✓✓ |
898 |
if (IsSet(js_max_buffer)) { |
834 |
✗✓ |
896 |
CHECK(js_max_buffer->IsNumber()); |
835 |
|
1792 |
max_buffer_ = js_max_buffer->NumberValue(context).FromJust(); |
836 |
|
|
} |
837 |
|
|
|
838 |
|
|
Local<Value> js_kill_signal = |
839 |
|
1796 |
js_options->Get(context, env()->kill_signal_string()).ToLocalChecked(); |
840 |
✓✓ |
898 |
if (IsSet(js_kill_signal)) { |
841 |
✗✓ |
105 |
CHECK(js_kill_signal->IsInt32()); |
842 |
|
105 |
kill_signal_ = js_kill_signal.As<Int32>()->Value(); |
843 |
|
|
} |
844 |
|
|
|
845 |
|
|
Local<Value> js_stdio = |
846 |
|
1796 |
js_options->Get(context, env()->stdio_string()).ToLocalChecked(); |
847 |
|
898 |
r = ParseStdioOptions(js_stdio); |
848 |
✗✓ |
898 |
if (r < 0) return Just(r); |
849 |
|
|
|
850 |
|
898 |
return Just(0); |
851 |
|
|
} |
852 |
|
|
|
853 |
|
|
|
854 |
|
898 |
int SyncProcessRunner::ParseStdioOptions(Local<Value> js_value) { |
855 |
|
1796 |
HandleScope scope(env()->isolate()); |
856 |
|
|
Local<Array> js_stdio_options; |
857 |
|
|
|
858 |
✗✓ |
898 |
if (!js_value->IsArray()) |
859 |
|
|
return UV_EINVAL; |
860 |
|
|
|
861 |
|
898 |
Local<Context> context = env()->context(); |
862 |
|
898 |
js_stdio_options = js_value.As<Array>(); |
863 |
|
|
|
864 |
|
898 |
stdio_count_ = js_stdio_options->Length(); |
865 |
✓✗ |
898 |
uv_stdio_containers_ = new uv_stdio_container_t[stdio_count_]; |
866 |
|
|
|
867 |
|
898 |
stdio_pipes_.clear(); |
868 |
|
898 |
stdio_pipes_.resize(stdio_count_); |
869 |
|
898 |
stdio_pipes_initialized_ = true; |
870 |
|
|
|
871 |
✓✓ |
3592 |
for (uint32_t i = 0; i < stdio_count_; i++) { |
872 |
|
|
Local<Value> js_stdio_option = |
873 |
|
5388 |
js_stdio_options->Get(context, i).ToLocalChecked(); |
874 |
|
|
|
875 |
✗✓ |
2694 |
if (!js_stdio_option->IsObject()) |
876 |
|
|
return UV_EINVAL; |
877 |
|
|
|
878 |
|
2694 |
int r = ParseStdioOption(i, js_stdio_option.As<Object>()); |
879 |
✗✓ |
2694 |
if (r < 0) |
880 |
|
|
return r; |
881 |
|
|
} |
882 |
|
|
|
883 |
|
898 |
uv_process_options_.stdio = uv_stdio_containers_; |
884 |
|
898 |
uv_process_options_.stdio_count = stdio_count_; |
885 |
|
|
|
886 |
|
898 |
return 0; |
887 |
|
|
} |
888 |
|
|
|
889 |
|
|
|
890 |
|
2694 |
int SyncProcessRunner::ParseStdioOption(int child_fd, |
891 |
|
|
Local<Object> js_stdio_option) { |
892 |
|
2694 |
Local<Context> context = env()->context(); |
893 |
|
|
Local<Value> js_type = |
894 |
|
8082 |
js_stdio_option->Get(context, env()->type_string()).ToLocalChecked(); |
895 |
|
|
|
896 |
✓✓ |
5388 |
if (js_type->StrictEquals(env()->ignore_string())) { |
897 |
|
3 |
return AddStdioIgnore(child_fd); |
898 |
|
|
|
899 |
✓✓ |
5382 |
} else if (js_type->StrictEquals(env()->pipe_string())) { |
900 |
|
2612 |
Isolate* isolate = env()->isolate(); |
901 |
|
2612 |
Local<String> rs = env()->readable_string(); |
902 |
|
2612 |
Local<String> ws = env()->writable_string(); |
903 |
|
|
|
904 |
|
2612 |
bool readable = js_stdio_option->Get(context, rs) |
905 |
|
2612 |
.ToLocalChecked()->BooleanValue(isolate); |
906 |
|
|
bool writable = |
907 |
|
2612 |
js_stdio_option->Get(context, ws) |
908 |
|
2612 |
.ToLocalChecked()->BooleanValue(isolate); |
909 |
|
|
|
910 |
|
2612 |
uv_buf_t buf = uv_buf_init(nullptr, 0); |
911 |
|
|
|
912 |
✓✓ |
2612 |
if (readable) { |
913 |
|
|
Local<Value> input = |
914 |
|
1728 |
js_stdio_option->Get(context, env()->input_string()).ToLocalChecked(); |
915 |
✓✓ |
864 |
if (Buffer::HasInstance(input)) { |
916 |
|
|
buf = uv_buf_init(Buffer::Data(input), |
917 |
|
24 |
static_cast<unsigned int>(Buffer::Length(input))); |
918 |
✗✓✗✗ ✗✓ |
1680 |
} else if (!input->IsUndefined() && !input->IsNull()) { |
919 |
|
|
// Strings, numbers etc. are currently unsupported. It's not possible |
920 |
|
|
// to create a buffer for them here because there is no way to free |
921 |
|
|
// them afterwards. |
922 |
|
|
return UV_EINVAL; |
923 |
|
|
} |
924 |
|
|
} |
925 |
|
|
|
926 |
|
2612 |
return AddStdioPipe(child_fd, readable, writable, buf); |
927 |
|
|
|
928 |
✓✓✓✗
|
194 |
} else if (js_type->StrictEquals(env()->inherit_string()) || |
929 |
✓✗ |
151 |
js_type->StrictEquals(env()->fd_string())) { |
930 |
|
158 |
int inherit_fd = js_stdio_option->Get(context, env()->fd_string()) |
931 |
|
79 |
.ToLocalChecked()->Int32Value(context).FromJust(); |
932 |
|
79 |
return AddStdioInheritFD(child_fd, inherit_fd); |
933 |
|
|
|
934 |
|
|
} else { |
935 |
|
|
CHECK(0 && "invalid child stdio type"); |
936 |
|
|
return UV_EINVAL; |
937 |
|
|
} |
938 |
|
|
} |
939 |
|
|
|
940 |
|
|
|
941 |
|
3 |
int SyncProcessRunner::AddStdioIgnore(uint32_t child_fd) { |
942 |
✗✓ |
3 |
CHECK_LT(child_fd, stdio_count_); |
943 |
✗✓ |
3 |
CHECK(!stdio_pipes_[child_fd]); |
944 |
|
|
|
945 |
|
3 |
uv_stdio_containers_[child_fd].flags = UV_IGNORE; |
946 |
|
|
|
947 |
|
3 |
return 0; |
948 |
|
|
} |
949 |
|
|
|
950 |
|
|
|
951 |
|
2612 |
int SyncProcessRunner::AddStdioPipe(uint32_t child_fd, |
952 |
|
|
bool readable, |
953 |
|
|
bool writable, |
954 |
|
|
uv_buf_t input_buffer) { |
955 |
✗✓ |
2612 |
CHECK_LT(child_fd, stdio_count_); |
956 |
✗✓ |
2612 |
CHECK(!stdio_pipes_[child_fd]); |
957 |
|
|
|
958 |
|
|
std::unique_ptr<SyncProcessStdioPipe> h( |
959 |
|
5224 |
new SyncProcessStdioPipe(this, readable, writable, input_buffer)); |
960 |
|
|
|
961 |
|
2612 |
int r = h->Initialize(uv_loop_); |
962 |
✗✓ |
2612 |
if (r < 0) { |
963 |
|
|
h.reset(); |
964 |
|
|
return r; |
965 |
|
|
} |
966 |
|
|
|
967 |
|
2612 |
uv_stdio_containers_[child_fd].flags = h->uv_flags(); |
968 |
|
2612 |
uv_stdio_containers_[child_fd].data.stream = h->uv_stream(); |
969 |
|
|
|
970 |
|
2612 |
stdio_pipes_[child_fd] = std::move(h); |
971 |
|
|
|
972 |
|
2612 |
return 0; |
973 |
|
|
} |
974 |
|
|
|
975 |
|
|
|
976 |
|
79 |
int SyncProcessRunner::AddStdioInheritFD(uint32_t child_fd, int inherit_fd) { |
977 |
✗✓ |
79 |
CHECK_LT(child_fd, stdio_count_); |
978 |
✗✓ |
79 |
CHECK(!stdio_pipes_[child_fd]); |
979 |
|
|
|
980 |
|
79 |
uv_stdio_containers_[child_fd].flags = UV_INHERIT_FD; |
981 |
|
79 |
uv_stdio_containers_[child_fd].data.fd = inherit_fd; |
982 |
|
|
|
983 |
|
79 |
return 0; |
984 |
|
|
} |
985 |
|
|
|
986 |
|
|
|
987 |
|
6286 |
bool SyncProcessRunner::IsSet(Local<Value> value) { |
988 |
✓✓✓✓
|
16586 |
return !value->IsUndefined() && !value->IsNull(); |
989 |
|
|
} |
990 |
|
|
|
991 |
|
991 |
Maybe<int> SyncProcessRunner::CopyJsString(Local<Value> js_value, |
992 |
|
|
const char** target) { |
993 |
|
991 |
Isolate* isolate = env()->isolate(); |
994 |
|
|
Local<String> js_string; |
995 |
|
|
size_t size, written; |
996 |
|
|
char* buffer; |
997 |
|
|
|
998 |
✓✗ |
1982 |
if (js_value->IsString()) |
999 |
|
991 |
js_string = js_value.As<String>(); |
1000 |
|
|
else if (!js_value->ToString(env()->isolate()->GetCurrentContext()) |
1001 |
|
|
.ToLocal(&js_string)) |
1002 |
|
|
return Nothing<int>(); |
1003 |
|
|
|
1004 |
|
|
// Include space for null terminator byte. |
1005 |
✗✓ |
1982 |
if (!StringBytes::StorageSize(isolate, js_string, UTF8).To(&size)) |
1006 |
|
|
return Nothing<int>(); |
1007 |
|
991 |
size += 1; |
1008 |
|
|
|
1009 |
|
991 |
buffer = new char[size]; |
1010 |
|
|
|
1011 |
|
991 |
written = StringBytes::Write(isolate, buffer, -1, js_string, UTF8); |
1012 |
|
991 |
buffer[written] = '\0'; |
1013 |
|
|
|
1014 |
|
991 |
*target = buffer; |
1015 |
|
991 |
return Just(0); |
1016 |
|
|
} |
1017 |
|
|
|
1018 |
|
1796 |
Maybe<int> SyncProcessRunner::CopyJsStringArray(Local<Value> js_value, |
1019 |
|
|
char** target) { |
1020 |
|
1796 |
Isolate* isolate = env()->isolate(); |
1021 |
|
|
Local<Array> js_array; |
1022 |
|
|
uint32_t length; |
1023 |
|
|
size_t list_size, data_size, data_offset; |
1024 |
|
|
char** list; |
1025 |
|
|
char* buffer; |
1026 |
|
|
|
1027 |
✗✓ |
1796 |
if (!js_value->IsArray()) return Just<int>(UV_EINVAL); |
1028 |
|
|
|
1029 |
|
1796 |
Local<Context> context = env()->context(); |
1030 |
|
3592 |
js_array = js_value.As<Array>()->Clone().As<Array>(); |
1031 |
|
1796 |
length = js_array->Length(); |
1032 |
|
1796 |
data_size = 0; |
1033 |
|
|
|
1034 |
|
|
// Index has a pointer to every string element, plus one more for a final |
1035 |
|
|
// null pointer. |
1036 |
|
1796 |
list_size = (length + 1) * sizeof *list; |
1037 |
|
|
|
1038 |
|
|
// Convert all array elements to string. Modify the js object itself if |
1039 |
|
|
// needed - it's okay since we cloned the original object. Also compute the |
1040 |
|
|
// length of all strings, including room for a null terminator after every |
1041 |
|
|
// string. Align strings to cache lines. |
1042 |
✓✓ |
69384 |
for (uint32_t i = 0; i < length; i++) { |
1043 |
|
135176 |
auto value = js_array->Get(context, i).ToLocalChecked(); |
1044 |
|
|
|
1045 |
✓✓ |
135176 |
if (!value->IsString()) { |
1046 |
|
|
Local<String> string; |
1047 |
|
92 |
if (!value->ToString(env()->isolate()->GetCurrentContext()) |
1048 |
✗✓ |
92 |
.ToLocal(&string)) |
1049 |
|
|
return Nothing<int>(); |
1050 |
|
|
js_array |
1051 |
|
92 |
->Set(context, |
1052 |
|
|
i, |
1053 |
|
184 |
string) |
1054 |
|
|
.Check(); |
1055 |
|
|
} |
1056 |
|
|
|
1057 |
|
67588 |
Maybe<size_t> maybe_size = StringBytes::StorageSize(isolate, value, UTF8); |
1058 |
✗✓ |
67588 |
if (maybe_size.IsNothing()) return Nothing<int>(); |
1059 |
|
67588 |
data_size += maybe_size.FromJust() + 1; |
1060 |
|
67588 |
data_size = RoundUp(data_size, sizeof(void*)); |
1061 |
|
|
} |
1062 |
|
|
|
1063 |
|
1796 |
buffer = new char[list_size + data_size]; |
1064 |
|
|
|
1065 |
|
1796 |
list = reinterpret_cast<char**>(buffer); |
1066 |
|
1796 |
data_offset = list_size; |
1067 |
|
|
|
1068 |
✓✓ |
69384 |
for (uint32_t i = 0; i < length; i++) { |
1069 |
|
67588 |
list[i] = buffer + data_offset; |
1070 |
|
67588 |
auto value = js_array->Get(context, i).ToLocalChecked(); |
1071 |
|
67588 |
data_offset += StringBytes::Write(isolate, |
1072 |
|
|
buffer + data_offset, |
1073 |
|
|
-1, |
1074 |
|
|
value, |
1075 |
|
|
UTF8); |
1076 |
|
67588 |
buffer[data_offset++] = '\0'; |
1077 |
|
67588 |
data_offset = RoundUp(data_offset, sizeof(void*)); |
1078 |
|
|
} |
1079 |
|
|
|
1080 |
|
1796 |
list[length] = nullptr; |
1081 |
|
|
|
1082 |
|
1796 |
*target = buffer; |
1083 |
|
1796 |
return Just(0); |
1084 |
|
|
} |
1085 |
|
|
|
1086 |
|
|
|
1087 |
|
753 |
void SyncProcessRunner::ExitCallback(uv_process_t* handle, |
1088 |
|
|
int64_t exit_status, |
1089 |
|
|
int term_signal) { |
1090 |
|
753 |
SyncProcessRunner* self = reinterpret_cast<SyncProcessRunner*>(handle->data); |
1091 |
|
753 |
uv_close(reinterpret_cast<uv_handle_t*>(handle), nullptr); |
1092 |
|
753 |
self->OnExit(exit_status, term_signal); |
1093 |
|
753 |
} |
1094 |
|
|
|
1095 |
|
|
|
1096 |
|
5 |
void SyncProcessRunner::KillTimerCallback(uv_timer_t* handle) { |
1097 |
|
5 |
SyncProcessRunner* self = reinterpret_cast<SyncProcessRunner*>(handle->data); |
1098 |
|
5 |
self->OnKillTimerTimeout(); |
1099 |
|
5 |
} |
1100 |
|
|
|
1101 |
|
|
|
1102 |
|
7 |
void SyncProcessRunner::KillTimerCloseCallback(uv_handle_t* handle) { |
1103 |
|
|
// No-op. |
1104 |
|
7 |
} |
1105 |
|
|
|
1106 |
|
|
} // namespace node |
1107 |
|
|
|
1108 |
|
5252 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(spawn_sync, |
1109 |
|
|
node::SyncProcessRunner::Initialize) |