GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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.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 |
240672 |
void SyncProcessOutputBuffer::OnAlloc(size_t suggested_size, |
|
54 |
uv_buf_t* buf) const { |
||
55 |
✗✓ | 240672 |
if (used() == kBufferSize) |
56 |
*buf = uv_buf_init(nullptr, 0); |
||
57 |
else |
||
58 |
240672 |
*buf = uv_buf_init(data_ + used(), available()); |
|
59 |
240672 |
} |
|
60 |
|||
61 |
|||
62 |
229072 |
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 |
✗✓ | 229072 |
CHECK_EQ(buf->base, data_ + used()); |
65 |
229072 |
used_ += static_cast<unsigned int>(nread); |
|
66 |
229072 |
} |
|
67 |
|||
68 |
|||
69 |
23935 |
size_t SyncProcessOutputBuffer::Copy(char* dest) const { |
|
70 |
23935 |
memcpy(dest, data_, used()); |
|
71 |
23935 |
return used(); |
|
72 |
} |
||
73 |
|||
74 |
|||
75 |
469111 |
unsigned int SyncProcessOutputBuffer::available() const { |
|
76 |
469111 |
return sizeof data_ - used(); |
|
77 |
} |
||
78 |
|||
79 |
|||
80 |
1251332 |
unsigned int SyncProcessOutputBuffer::used() const { |
|
81 |
1251332 |
return used_; |
|
82 |
} |
||
83 |
|||
84 |
|||
85 |
71805 |
SyncProcessOutputBuffer* SyncProcessOutputBuffer::next() const { |
|
86 |
71805 |
return next_; |
|
87 |
} |
||
88 |
|||
89 |
|||
90 |
11702 |
void SyncProcessOutputBuffer::set_next(SyncProcessOutputBuffer* next) { |
|
91 |
11702 |
next_ = next; |
|
92 |
11702 |
} |
|
93 |
|||
94 |
|||
95 |
20982 |
SyncProcessStdioPipe::SyncProcessStdioPipe(SyncProcessRunner* process_handler, |
|
96 |
bool readable, |
||
97 |
bool writable, |
||
98 |
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 |
20982 |
lifecycle_(kUninitialized) { |
|
112 |
✓✓✗✓ ✗✓ |
20982 |
CHECK(readable || writable); |
113 |
20982 |
} |
|
114 |
|||
115 |
|||
116 |
20982 |
SyncProcessStdioPipe::~SyncProcessStdioPipe() { |
|
117 |
✓✗✗✓ ✗✓ |
20982 |
CHECK(lifecycle_ == kUninitialized || lifecycle_ == kClosed); |
118 |
|||
119 |
SyncProcessOutputBuffer* buf; |
||
120 |
SyncProcessOutputBuffer* next; |
||
121 |
|||
122 |
✓✓ | 44917 |
for (buf = first_output_buffer_; buf != nullptr; buf = next) { |
123 |
23935 |
next = buf->next(); |
|
124 |
23935 |
delete buf; |
|
125 |
} |
||
126 |
20982 |
} |
|
127 |
|||
128 |
|||
129 |
20982 |
int SyncProcessStdioPipe::Initialize(uv_loop_t* loop) { |
|
130 |
✗✓ | 20982 |
CHECK_EQ(lifecycle_, kUninitialized); |
131 |
|||
132 |
20982 |
int r = uv_pipe_init(loop, uv_pipe(), 0); |
|
133 |
✗✓ | 20982 |
if (r < 0) |
134 |
return r; |
||
135 |
|||
136 |
20982 |
uv_pipe()->data = this; |
|
137 |
|||
138 |
20982 |
lifecycle_ = kInitialized; |
|
139 |
20982 |
return 0; |
|
140 |
} |
||
141 |
|||
142 |
|||
143 |
19185 |
int SyncProcessStdioPipe::Start() { |
|
144 |
✗✓ | 19185 |
CHECK_EQ(lifecycle_, kInitialized); |
145 |
|||
146 |
// Set the busy flag already. If this function fails no recovery is |
||
147 |
// possible. |
||
148 |
19185 |
lifecycle_ = kStarted; |
|
149 |
|||
150 |
✓✓ | 19185 |
if (readable()) { |
151 |
✓✓ | 6376 |
if (input_buffer_.len > 0) { |
152 |
✗✓ | 21 |
CHECK_NOT_NULL(input_buffer_.base); |
153 |
|||
154 |
int r = uv_write(&write_req_, |
||
155 |
uv_stream(), |
||
156 |
&input_buffer_, |
||
157 |
1, |
||
158 |
21 |
WriteCallback); |
|
159 |
✗✓ | 21 |
if (r < 0) |
160 |
return r; |
||
161 |
} |
||
162 |
|||
163 |
6376 |
int r = uv_shutdown(&shutdown_req_, uv_stream(), ShutdownCallback); |
|
164 |
✗✓ | 6376 |
if (r < 0) |
165 |
return r; |
||
166 |
} |
||
167 |
|||
168 |
✓✓ | 19185 |
if (writable()) { |
169 |
12809 |
int r = uv_read_start(uv_stream(), AllocCallback, ReadCallback); |
|
170 |
✗✓ | 12809 |
if (r < 0) |
171 |
return r; |
||
172 |
} |
||
173 |
|||
174 |
19185 |
return 0; |
|
175 |
} |
||
176 |
|||
177 |
|||
178 |
20982 |
void SyncProcessStdioPipe::Close() { |
|
179 |
✓✓✗✓ ✗✓ |
20982 |
CHECK(lifecycle_ == kInitialized || lifecycle_ == kStarted); |
180 |
|||
181 |
20982 |
uv_close(uv_handle(), CloseCallback); |
|
182 |
|||
183 |
20982 |
lifecycle_ = kClosing; |
|
184 |
20982 |
} |
|
185 |
|||
186 |
|||
187 |
12809 |
Local<Object> SyncProcessStdioPipe::GetOutputAsBuffer(Environment* env) const { |
|
188 |
12809 |
size_t length = OutputLength(); |
|
189 |
25618 |
Local<Object> js_buffer = Buffer::New(env, length).ToLocalChecked(); |
|
190 |
12809 |
CopyOutput(Buffer::Data(js_buffer)); |
|
191 |
12809 |
return js_buffer; |
|
192 |
} |
||
193 |
|||
194 |
|||
195 |
40167 |
bool SyncProcessStdioPipe::readable() const { |
|
196 |
40167 |
return readable_; |
|
197 |
} |
||
198 |
|||
199 |
|||
200 |
59352 |
bool SyncProcessStdioPipe::writable() const { |
|
201 |
59352 |
return writable_; |
|
202 |
} |
||
203 |
|||
204 |
|||
205 |
20982 |
uv_stdio_flags SyncProcessStdioPipe::uv_flags() const { |
|
206 |
unsigned int flags; |
||
207 |
|||
208 |
20982 |
flags = UV_CREATE_PIPE; |
|
209 |
✓✓ | 20982 |
if (readable()) |
210 |
6975 |
flags |= UV_READABLE_PIPE; |
|
211 |
✓✓ | 20982 |
if (writable()) |
212 |
14007 |
flags |= UV_WRITABLE_PIPE; |
|
213 |
|||
214 |
20982 |
return static_cast<uv_stdio_flags>(flags); |
|
215 |
} |
||
216 |
|||
217 |
|||
218 |
103134 |
uv_pipe_t* SyncProcessStdioPipe::uv_pipe() const { |
|
219 |
✗✓ | 103134 |
CHECK_LT(lifecycle_, kClosing); |
220 |
103134 |
return &uv_pipe_; |
|
221 |
} |
||
222 |
|||
223 |
|||
224 |
40188 |
uv_stream_t* SyncProcessStdioPipe::uv_stream() const { |
|
225 |
40188 |
return reinterpret_cast<uv_stream_t*>(uv_pipe()); |
|
226 |
} |
||
227 |
|||
228 |
|||
229 |
20982 |
uv_handle_t* SyncProcessStdioPipe::uv_handle() const { |
|
230 |
20982 |
return reinterpret_cast<uv_handle_t*>(uv_pipe()); |
|
231 |
} |
||
232 |
|||
233 |
|||
234 |
12809 |
size_t SyncProcessStdioPipe::OutputLength() const { |
|
235 |
SyncProcessOutputBuffer* buf; |
||
236 |
12809 |
size_t size = 0; |
|
237 |
|||
238 |
✓✓ | 36744 |
for (buf = first_output_buffer_; buf != nullptr; buf = buf->next()) |
239 |
23935 |
size += buf->used(); |
|
240 |
|||
241 |
12809 |
return size; |
|
242 |
} |
||
243 |
|||
244 |
|||
245 |
12809 |
void SyncProcessStdioPipe::CopyOutput(char* dest) const { |
|
246 |
SyncProcessOutputBuffer* buf; |
||
247 |
12809 |
size_t offset = 0; |
|
248 |
|||
249 |
✓✓ | 36744 |
for (buf = first_output_buffer_; buf != nullptr; buf = buf->next()) |
250 |
23935 |
offset += buf->Copy(dest + offset); |
|
251 |
12809 |
} |
|
252 |
|||
253 |
|||
254 |
240672 |
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 |
✓✓ | 240672 |
if (last_output_buffer_ == nullptr) { |
261 |
// Allocate the first capture buffer. |
||
262 |
12233 |
first_output_buffer_ = new SyncProcessOutputBuffer(); |
|
263 |
12233 |
last_output_buffer_ = first_output_buffer_; |
|
264 |
|||
265 |
✓✓ | 228439 |
} else if (last_output_buffer_->available() == 0) { |
266 |
// The current capture buffer is full so get us a new one. |
||
267 |
11702 |
SyncProcessOutputBuffer* buf = new SyncProcessOutputBuffer(); |
|
268 |
11702 |
last_output_buffer_->set_next(buf); |
|
269 |
11702 |
last_output_buffer_ = buf; |
|
270 |
} |
||
271 |
|||
272 |
240672 |
last_output_buffer_->OnAlloc(suggested_size, buf); |
|
273 |
240672 |
} |
|
274 |
|||
275 |
|||
276 |
240739 |
void SyncProcessStdioPipe::OnRead(const uv_buf_t* buf, ssize_t nread) { |
|
277 |
✓✓ | 240739 |
if (nread == UV_EOF) { |
278 |
// Libuv implicitly stops reading on EOF. |
||
279 |
|||
280 |
✗✓ | 229072 |
} 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 |
229072 |
last_output_buffer_->OnRead(buf, nread); |
|
287 |
229072 |
process_handler_->IncrementBufferSizeAndCheckOverflow(nread); |
|
288 |
} |
||
289 |
240739 |
} |
|
290 |
|||
291 |
|||
292 |
21 |
void SyncProcessStdioPipe::OnWriteDone(int result) { |
|
293 |
✗✓ | 21 |
if (result < 0) |
294 |
SetError(result); |
||
295 |
21 |
} |
|
296 |
|||
297 |
|||
298 |
6376 |
void SyncProcessStdioPipe::OnShutdownDone(int result) { |
|
299 |
✗✓ | 6376 |
if (result < 0) |
300 |
SetError(result); |
||
301 |
6376 |
} |
|
302 |
|||
303 |
|||
304 |
20982 |
void SyncProcessStdioPipe::OnClose() { |
|
305 |
20982 |
lifecycle_ = kClosed; |
|
306 |
20982 |
} |
|
307 |
|||
308 |
|||
309 |
void SyncProcessStdioPipe::SetError(int error) { |
||
310 |
CHECK_NE(error, 0); |
||
311 |
process_handler_->SetPipeError(error); |
||
312 |
} |
||
313 |
|||
314 |
|||
315 |
240672 |
void SyncProcessStdioPipe::AllocCallback(uv_handle_t* handle, |
|
316 |
size_t suggested_size, |
||
317 |
uv_buf_t* buf) { |
||
318 |
SyncProcessStdioPipe* self = |
||
319 |
240672 |
reinterpret_cast<SyncProcessStdioPipe*>(handle->data); |
|
320 |
240672 |
self->OnAlloc(suggested_size, buf); |
|
321 |
240672 |
} |
|
322 |
|||
323 |
|||
324 |
240739 |
void SyncProcessStdioPipe::ReadCallback(uv_stream_t* stream, |
|
325 |
ssize_t nread, |
||
326 |
const uv_buf_t* buf) { |
||
327 |
SyncProcessStdioPipe* self = |
||
328 |
240739 |
reinterpret_cast<SyncProcessStdioPipe*>(stream->data); |
|
329 |
240739 |
self->OnRead(buf, nread); |
|
330 |
240739 |
} |
|
331 |
|||
332 |
|||
333 |
21 |
void SyncProcessStdioPipe::WriteCallback(uv_write_t* req, int result) { |
|
334 |
SyncProcessStdioPipe* self = |
||
335 |
21 |
reinterpret_cast<SyncProcessStdioPipe*>(req->handle->data); |
|
336 |
21 |
self->OnWriteDone(result); |
|
337 |
21 |
} |
|
338 |
|||
339 |
|||
340 |
6376 |
void SyncProcessStdioPipe::ShutdownCallback(uv_shutdown_t* req, int result) { |
|
341 |
SyncProcessStdioPipe* self = |
||
342 |
6376 |
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 |
✗✓ | 6376 |
if (result == UV_ENOTCONN) |
349 |
result = 0; |
||
350 |
|||
351 |
6376 |
self->OnShutdownDone(result); |
|
352 |
6376 |
} |
|
353 |
|||
354 |
|||
355 |
20982 |
void SyncProcessStdioPipe::CloseCallback(uv_handle_t* handle) { |
|
356 |
SyncProcessStdioPipe* self = |
||
357 |
20982 |
reinterpret_cast<SyncProcessStdioPipe*>(handle->data); |
|
358 |
20982 |
self->OnClose(); |
|
359 |
20982 |
} |
|
360 |
|||
361 |
|||
362 |
4342 |
void SyncProcessRunner::Initialize(Local<Object> target, |
|
363 |
Local<Value> unused, |
||
364 |
Local<Context> context, |
||
365 |
void* priv) { |
||
366 |
4342 |
Environment* env = Environment::GetCurrent(context); |
|
367 |
4342 |
env->SetMethod(target, "spawn", Spawn); |
|
368 |
4342 |
} |
|
369 |
|||
370 |
|||
371 |
7044 |
void SyncProcessRunner::Spawn(const FunctionCallbackInfo<Value>& args) { |
|
372 |
7044 |
Environment* env = Environment::GetCurrent(args); |
|
373 |
7044 |
env->PrintSyncTrace(); |
|
374 |
7044 |
SyncProcessRunner p(env); |
|
375 |
Local<Value> result; |
||
376 |
✗✓ | 21132 |
if (!p.Run(args[0]).ToLocal(&result)) return; |
377 |
✓✗ | 14088 |
args.GetReturnValue().Set(result); |
378 |
} |
||
379 |
|||
380 |
|||
381 |
7044 |
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 |
7044 |
env_(env) { |
|
414 |
7044 |
} |
|
415 |
|||
416 |
|||
417 |
14088 |
SyncProcessRunner::~SyncProcessRunner() { |
|
418 |
✗✓ | 7044 |
CHECK_EQ(lifecycle_, kHandlesClosed); |
419 |
|||
420 |
7044 |
stdio_pipes_.clear(); |
|
421 |
✓✗ | 7044 |
delete[] file_buffer_; |
422 |
✓✗ | 7044 |
delete[] args_buffer_; |
423 |
✓✓ | 7044 |
delete[] cwd_buffer_; |
424 |
✓✗ | 7044 |
delete[] env_buffer_; |
425 |
✓✗ | 7044 |
delete[] uv_stdio_containers_; |
426 |
7044 |
} |
|
427 |
|||
428 |
|||
429 |
428770 |
Environment* SyncProcessRunner::env() const { |
|
430 |
428770 |
return env_; |
|
431 |
} |
||
432 |
|||
433 |
7044 |
MaybeLocal<Object> SyncProcessRunner::Run(Local<Value> options) { |
|
434 |
7044 |
EscapableHandleScope scope(env()->isolate()); |
|
435 |
|||
436 |
✗✓ | 7044 |
CHECK_EQ(lifecycle_, kUninitialized); |
437 |
|||
438 |
7044 |
Maybe<bool> r = TryInitializeAndRunLoop(options); |
|
439 |
7044 |
CloseHandlesAndDeleteLoop(); |
|
440 |
✗✓ | 7044 |
if (r.IsNothing()) return MaybeLocal<Object>(); |
441 |
|||
442 |
7044 |
Local<Object> result = BuildResultObject(); |
|
443 |
|||
444 |
7044 |
return scope.Escape(result); |
|
445 |
} |
||
446 |
|||
447 |
7044 |
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 |
✗✓ | 7044 |
CHECK_EQ(lifecycle_, kUninitialized); |
453 |
7044 |
lifecycle_ = kInitialized; |
|
454 |
|||
455 |
7044 |
uv_loop_ = new uv_loop_t; |
|
456 |
✗✓ | 7044 |
if (uv_loop_ == nullptr) { |
457 |
SetError(UV_ENOMEM); |
||
458 |
return Just(false); |
||
459 |
} |
||
460 |
✗✓ | 7044 |
CHECK_EQ(uv_loop_init(uv_loop_), 0); |
461 |
|||
462 |
✗✓ | 14088 |
if (!ParseOptions(options).To(&r)) return Nothing<bool>(); |
463 |
✗✓ | 7044 |
if (r < 0) { |
464 |
SetError(r); |
||
465 |
return Just(false); |
||
466 |
} |
||
467 |
|||
468 |
✓✓ | 7044 |
if (timeout_ > 0) { |
469 |
7 |
r = uv_timer_init(uv_loop_, &uv_timer_); |
|
470 |
✗✓ | 7 |
if (r < 0) { |
471 |
SetError(r); |
||
472 |
return Just(false); |
||
473 |
} |
||
474 |
|||
475 |
7 |
uv_unref(reinterpret_cast<uv_handle_t*>(&uv_timer_)); |
|
476 |
|||
477 |
7 |
uv_timer_.data = this; |
|
478 |
7 |
kill_timer_initialized_ = true; |
|
479 |
|||
480 |
// Start the timer immediately. If uv_spawn fails then |
||
481 |
// CloseHandlesAndDeleteLoop() will immediately close the timer handle |
||
482 |
// which implicitly stops it, so there is no risk that the timeout callback |
||
483 |
// runs when the process didn't start. |
||
484 |
7 |
r = uv_timer_start(&uv_timer_, KillTimerCallback, timeout_, 0); |
|
485 |
✗✓ | 7 |
if (r < 0) { |
486 |
SetError(r); |
||
487 |
return Just(false); |
||
488 |
} |
||
489 |
} |
||
490 |
|||
491 |
7044 |
uv_process_options_.exit_cb = ExitCallback; |
|
492 |
7044 |
r = uv_spawn(uv_loop_, &uv_process_, &uv_process_options_); |
|
493 |
✓✓ | 7044 |
if (r < 0) { |
494 |
599 |
SetError(r); |
|
495 |
599 |
return Just(false); |
|
496 |
} |
||
497 |
6445 |
uv_process_.data = this; |
|
498 |
|||
499 |
✓✓ | 25780 |
for (const auto& pipe : stdio_pipes_) { |
500 |
✓✓ | 19335 |
if (pipe != nullptr) { |
501 |
19185 |
r = pipe->Start(); |
|
502 |
✗✓ | 19185 |
if (r < 0) { |
503 |
SetPipeError(r); |
||
504 |
return Just(false); |
||
505 |
} |
||
506 |
} |
||
507 |
} |
||
508 |
|||
509 |
6445 |
r = uv_run(uv_loop_, UV_RUN_DEFAULT); |
|
510 |
✗✓ | 6445 |
if (r < 0) |
511 |
// We can't handle uv_run failure. |
||
512 |
ABORT(); |
||
513 |
|||
514 |
// If we get here the process should have exited. |
||
515 |
✗✓ | 6445 |
CHECK_GE(exit_status_, 0); |
516 |
6445 |
return Just(true); |
|
517 |
} |
||
518 |
|||
519 |
|||
520 |
7044 |
void SyncProcessRunner::CloseHandlesAndDeleteLoop() { |
|
521 |
✗✓ | 7044 |
CHECK_LT(lifecycle_, kHandlesClosed); |
522 |
|||
523 |
✓✗ | 7044 |
if (uv_loop_ != nullptr) { |
524 |
7044 |
CloseStdioPipes(); |
|
525 |
7044 |
CloseKillTimer(); |
|
526 |
// Close the process handle when ExitCallback was not called. |
||
527 |
uv_handle_t* uv_process_handle = |
||
528 |
7044 |
reinterpret_cast<uv_handle_t*>(&uv_process_); |
|
529 |
|||
530 |
// Close the process handle if it is still open. The handle type also |
||
531 |
// needs to be checked because TryInitializeAndRunLoop() won't spawn a |
||
532 |
// process if input validation fails. |
||
533 |
✓✗✓✓ ✓✓ |
14088 |
if (uv_process_handle->type == UV_PROCESS && |
534 |
7044 |
!uv_is_closing(uv_process_handle)) |
|
535 |
599 |
uv_close(uv_process_handle, nullptr); |
|
536 |
|||
537 |
// Give closing watchers a chance to finish closing and get their close |
||
538 |
// callbacks called. |
||
539 |
7044 |
int r = uv_run(uv_loop_, UV_RUN_DEFAULT); |
|
540 |
✗✓ | 7044 |
if (r < 0) |
541 |
ABORT(); |
||
542 |
|||
543 |
7044 |
CheckedUvLoopClose(uv_loop_); |
|
544 |
7044 |
delete uv_loop_; |
|
545 |
7044 |
uv_loop_ = nullptr; |
|
546 |
|||
547 |
} else { |
||
548 |
// If the loop doesn't exist, neither should any pipes or timers. |
||
549 |
CHECK_EQ(false, stdio_pipes_initialized_); |
||
550 |
CHECK_EQ(false, kill_timer_initialized_); |
||
551 |
} |
||
552 |
|||
553 |
7044 |
lifecycle_ = kHandlesClosed; |
|
554 |
7044 |
} |
|
555 |
|||
556 |
|||
557 |
7615 |
void SyncProcessRunner::CloseStdioPipes() { |
|
558 |
✗✓ | 7615 |
CHECK_LT(lifecycle_, kHandlesClosed); |
559 |
|||
560 |
✓✓ | 7615 |
if (stdio_pipes_initialized_) { |
561 |
✗✓ | 7044 |
CHECK(!stdio_pipes_.empty()); |
562 |
✗✓ | 7044 |
CHECK_NOT_NULL(uv_loop_); |
563 |
|||
564 |
✓✓ | 28176 |
for (const auto& pipe : stdio_pipes_) { |
565 |
✓✓ | 21132 |
if (pipe) |
566 |
20982 |
pipe->Close(); |
|
567 |
} |
||
568 |
|||
569 |
7044 |
stdio_pipes_initialized_ = false; |
|
570 |
} |
||
571 |
7615 |
} |
|
572 |
|||
573 |
|||
574 |
7615 |
void SyncProcessRunner::CloseKillTimer() { |
|
575 |
✗✓ | 7615 |
CHECK_LT(lifecycle_, kHandlesClosed); |
576 |
|||
577 |
✓✓ | 7615 |
if (kill_timer_initialized_) { |
578 |
✗✓ | 7 |
CHECK_GT(timeout_, 0); |
579 |
✗✓ | 7 |
CHECK_NOT_NULL(uv_loop_); |
580 |
|||
581 |
7 |
uv_handle_t* uv_timer_handle = reinterpret_cast<uv_handle_t*>(&uv_timer_); |
|
582 |
7 |
uv_ref(uv_timer_handle); |
|
583 |
7 |
uv_close(uv_timer_handle, KillTimerCloseCallback); |
|
584 |
|||
585 |
7 |
kill_timer_initialized_ = false; |
|
586 |
} |
||
587 |
7615 |
} |
|
588 |
|||
589 |
|||
590 |
571 |
void SyncProcessRunner::Kill() { |
|
591 |
// Only attempt to kill once. |
||
592 |
✗✓ | 571 |
if (killed_) |
593 |
571 |
return; |
|
594 |
571 |
killed_ = true; |
|
595 |
|||
596 |
// We might get here even if the process we spawned has already exited. This |
||
597 |
// could happen when our child process spawned another process which |
||
598 |
// inherited (one of) the stdio pipes. In this case we won't attempt to send |
||
599 |
// a signal to the process, however we will still close our end of the stdio |
||
600 |
// pipes so this situation won't make us hang. |
||
601 |
✓✗ | 571 |
if (exit_status_ < 0) { |
602 |
571 |
int r = uv_process_kill(&uv_process_, kill_signal_); |
|
603 |
|||
604 |
// If uv_kill failed with an error that isn't ESRCH, the user probably |
||
605 |
// specified an invalid or unsupported signal. Signal this to the user as |
||
606 |
// and error and kill the process with SIGKILL instead. |
||
607 |
✗✓✗✗ |
571 |
if (r < 0 && r != UV_ESRCH) { |
608 |
SetError(r); |
||
609 |
|||
610 |
r = uv_process_kill(&uv_process_, SIGKILL); |
||
611 |
CHECK(r >= 0 || r == UV_ESRCH); |
||
612 |
} |
||
613 |
} |
||
614 |
|||
615 |
// Close all stdio pipes. |
||
616 |
571 |
CloseStdioPipes(); |
|
617 |
|||
618 |
// Stop the timeout timer immediately. |
||
619 |
571 |
CloseKillTimer(); |
|
620 |
} |
||
621 |
|||
622 |
|||
623 |
229072 |
void SyncProcessRunner::IncrementBufferSizeAndCheckOverflow(ssize_t length) { |
|
624 |
229072 |
buffered_output_size_ += length; |
|
625 |
|||
626 |
✓✗✓✓ |
229072 |
if (max_buffer_ > 0 && buffered_output_size_ > max_buffer_) { |
627 |
566 |
SetError(UV_ENOBUFS); |
|
628 |
566 |
Kill(); |
|
629 |
} |
||
630 |
229072 |
} |
|
631 |
|||
632 |
|||
633 |
6445 |
void SyncProcessRunner::OnExit(int64_t exit_status, int term_signal) { |
|
634 |
✗✓ | 6445 |
if (exit_status < 0) |
635 |
6445 |
return SetError(static_cast<int>(exit_status)); |
|
636 |
|||
637 |
6445 |
exit_status_ = exit_status; |
|
638 |
6445 |
term_signal_ = term_signal; |
|
639 |
} |
||
640 |
|||
641 |
|||
642 |
5 |
void SyncProcessRunner::OnKillTimerTimeout() { |
|
643 |
5 |
SetError(UV_ETIMEDOUT); |
|
644 |
5 |
Kill(); |
|
645 |
5 |
} |
|
646 |
|||
647 |
|||
648 |
8214 |
int SyncProcessRunner::GetError() { |
|
649 |
✓✓ | 8214 |
if (error_ != 0) |
650 |
2340 |
return error_; |
|
651 |
else |
||
652 |
5874 |
return pipe_error_; |
|
653 |
} |
||
654 |
|||
655 |
|||
656 |
1170 |
void SyncProcessRunner::SetError(int error) { |
|
657 |
✓✗ | 1170 |
if (error_ == 0) |
658 |
1170 |
error_ = error; |
|
659 |
1170 |
} |
|
660 |
|||
661 |
|||
662 |
void SyncProcessRunner::SetPipeError(int pipe_error) { |
||
663 |
if (pipe_error_ == 0) |
||
664 |
pipe_error_ = pipe_error; |
||
665 |
} |
||
666 |
|||
667 |
|||
668 |
7044 |
Local<Object> SyncProcessRunner::BuildResultObject() { |
|
669 |
7044 |
EscapableHandleScope scope(env()->isolate()); |
|
670 |
7044 |
Local<Context> context = env()->context(); |
|
671 |
|||
672 |
7044 |
Local<Object> js_result = Object::New(env()->isolate()); |
|
673 |
|||
674 |
✓✓ | 7044 |
if (GetError() != 0) { |
675 |
js_result->Set(context, env()->error_string(), |
||
676 |
4680 |
Integer::New(env()->isolate(), GetError())).Check(); |
|
677 |
} |
||
678 |
|||
679 |
✓✓ | 7044 |
if (exit_status_ >= 0) { |
680 |
✓✓ | 6445 |
if (term_signal_ > 0) { |
681 |
js_result->Set(context, env()->status_string(), |
||
682 |
2328 |
Null(env()->isolate())).Check(); |
|
683 |
} else { |
||
684 |
js_result->Set(context, env()->status_string(), |
||
685 |
Number::New(env()->isolate(), |
||
686 |
23452 |
static_cast<double>(exit_status_))).Check(); |
|
687 |
} |
||
688 |
} else { |
||
689 |
// If exit_status_ < 0 the process was never started because of some error. |
||
690 |
js_result->Set(context, env()->status_string(), |
||
691 |
2396 |
Null(env()->isolate())).Check(); |
|
692 |
} |
||
693 |
|||
694 |
✓✓ | 7044 |
if (term_signal_ > 0) |
695 |
js_result->Set(context, env()->signal_string(), |
||
696 |
String::NewFromUtf8(env()->isolate(), |
||
697 |
signo_string(term_signal_), |
||
698 |
582 |
v8::NewStringType::kNormal) |
|
699 |
2328 |
.ToLocalChecked()) |
|
700 |
1164 |
.Check(); |
|
701 |
else |
||
702 |
js_result->Set(context, env()->signal_string(), |
||
703 |
25848 |
Null(env()->isolate())).Check(); |
|
704 |
|||
705 |
✓✓ | 7044 |
if (exit_status_ >= 0) |
706 |
js_result->Set(context, env()->output_string(), |
||
707 |
25780 |
BuildOutputArray()).Check(); |
|
708 |
else |
||
709 |
js_result->Set(context, env()->output_string(), |
||
710 |
2396 |
Null(env()->isolate())).Check(); |
|
711 |
|||
712 |
js_result->Set(context, env()->pid_string(), |
||
713 |
28176 |
Number::New(env()->isolate(), uv_process_.pid)).Check(); |
|
714 |
|||
715 |
7044 |
return scope.Escape(js_result); |
|
716 |
} |
||
717 |
|||
718 |
|||
719 |
6445 |
Local<Array> SyncProcessRunner::BuildOutputArray() { |
|
720 |
✗✓ | 6445 |
CHECK_GE(lifecycle_, kInitialized); |
721 |
✗✓ | 6445 |
CHECK(!stdio_pipes_.empty()); |
722 |
|||
723 |
6445 |
EscapableHandleScope scope(env()->isolate()); |
|
724 |
6445 |
Local<Context> context = env()->context(); |
|
725 |
6445 |
Local<Array> js_output = Array::New(env()->isolate(), stdio_count_); |
|
726 |
|||
727 |
✓✓ | 25780 |
for (uint32_t i = 0; i < stdio_pipes_.size(); i++) { |
728 |
19335 |
SyncProcessStdioPipe* h = stdio_pipes_[i].get(); |
|
729 |
✓✓✓✓ ✓✓ |
19335 |
if (h != nullptr && h->writable()) |
730 |
38427 |
js_output->Set(context, i, h->GetOutputAsBuffer(env())).Check(); |
|
731 |
else |
||
732 |
19578 |
js_output->Set(context, i, Null(env()->isolate())).Check(); |
|
733 |
} |
||
734 |
|||
735 |
6445 |
return scope.Escape(js_output); |
|
736 |
} |
||
737 |
|||
738 |
7044 |
Maybe<int> SyncProcessRunner::ParseOptions(Local<Value> js_value) { |
|
739 |
7044 |
Isolate* isolate = env()->isolate(); |
|
740 |
7044 |
HandleScope scope(isolate); |
|
741 |
int r; |
||
742 |
|||
743 |
✗✓ | 7044 |
if (!js_value->IsObject()) return Just<int>(UV_EINVAL); |
744 |
|||
745 |
7044 |
Local<Context> context = env()->context(); |
|
746 |
7044 |
Local<Object> js_options = js_value.As<Object>(); |
|
747 |
|||
748 |
Local<Value> js_file = |
||
749 |
21132 |
js_options->Get(context, env()->file_string()).ToLocalChecked(); |
|
750 |
✗✓ | 14088 |
if (!CopyJsString(js_file, &file_buffer_).To(&r)) return Nothing<int>(); |
751 |
✗✓ | 7044 |
if (r < 0) return Just(r); |
752 |
7044 |
uv_process_options_.file = file_buffer_; |
|
753 |
|||
754 |
Local<Value> js_args = |
||
755 |
21132 |
js_options->Get(context, env()->args_string()).ToLocalChecked(); |
|
756 |
✗✓ | 14088 |
if (!CopyJsStringArray(js_args, &args_buffer_).To(&r)) return Nothing<int>(); |
757 |
✗✓ | 7044 |
if (r < 0) return Just(r); |
758 |
7044 |
uv_process_options_.args = reinterpret_cast<char**>(args_buffer_); |
|
759 |
|||
760 |
Local<Value> js_cwd = |
||
761 |
21132 |
js_options->Get(context, env()->cwd_string()).ToLocalChecked(); |
|
762 |
✓✓ | 7044 |
if (IsSet(js_cwd)) { |
763 |
✗✓ | 194 |
if (!CopyJsString(js_cwd, &cwd_buffer_).To(&r)) return Nothing<int>(); |
764 |
✗✓ | 97 |
if (r < 0) return Just(r); |
765 |
97 |
uv_process_options_.cwd = cwd_buffer_; |
|
766 |
} |
||
767 |
|||
768 |
Local<Value> js_env_pairs = |
||
769 |
21132 |
js_options->Get(context, env()->env_pairs_string()).ToLocalChecked(); |
|
770 |
✓✗ | 7044 |
if (IsSet(js_env_pairs)) { |
771 |
✗✓ | 14088 |
if (!CopyJsStringArray(js_env_pairs, &env_buffer_).To(&r)) |
772 |
return Nothing<int>(); |
||
773 |
✗✓ | 7044 |
if (r < 0) return Just(r); |
774 |
|||
775 |
7044 |
uv_process_options_.env = reinterpret_cast<char**>(env_buffer_); |
|
776 |
} |
||
777 |
Local<Value> js_uid = |
||
778 |
21132 |
js_options->Get(context, env()->uid_string()).ToLocalChecked(); |
|
779 |
✓✓ | 7044 |
if (IsSet(js_uid)) { |
780 |
✗✓ | 1 |
CHECK(js_uid->IsInt32()); |
781 |
2 |
const int32_t uid = js_uid.As<Int32>()->Value(); |
|
782 |
1 |
uv_process_options_.uid = static_cast<uv_uid_t>(uid); |
|
783 |
1 |
uv_process_options_.flags |= UV_PROCESS_SETUID; |
|
784 |
} |
||
785 |
|||
786 |
Local<Value> js_gid = |
||
787 |
21132 |
js_options->Get(context, env()->gid_string()).ToLocalChecked(); |
|
788 |
✓✓ | 7044 |
if (IsSet(js_gid)) { |
789 |
✗✓ | 1 |
CHECK(js_gid->IsInt32()); |
790 |
2 |
const int32_t gid = js_gid.As<Int32>()->Value(); |
|
791 |
1 |
uv_process_options_.gid = static_cast<uv_gid_t>(gid); |
|
792 |
1 |
uv_process_options_.flags |= UV_PROCESS_SETGID; |
|
793 |
} |
||
794 |
|||
795 |
Local<Value> js_detached = |
||
796 |
21132 |
js_options->Get(context, env()->detached_string()).ToLocalChecked(); |
|
797 |
✓✓ | 7044 |
if (js_detached->BooleanValue(isolate)) |
798 |
1 |
uv_process_options_.flags |= UV_PROCESS_DETACHED; |
|
799 |
|||
800 |
Local<Value> js_win_hide = |
||
801 |
21132 |
js_options->Get(context, env()->windows_hide_string()).ToLocalChecked(); |
|
802 |
✓✓ | 7044 |
if (js_win_hide->BooleanValue(isolate)) |
803 |
2 |
uv_process_options_.flags |= UV_PROCESS_WINDOWS_HIDE; |
|
804 |
|||
805 |
Local<Value> js_wva = |
||
806 |
21132 |
js_options->Get(context, env()->windows_verbatim_arguments_string()) |
|
807 |
14088 |
.ToLocalChecked(); |
|
808 |
|||
809 |
✓✓ | 7044 |
if (js_wva->BooleanValue(isolate)) |
810 |
1 |
uv_process_options_.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; |
|
811 |
|||
812 |
Local<Value> js_timeout = |
||
813 |
21132 |
js_options->Get(context, env()->timeout_string()).ToLocalChecked(); |
|
814 |
✓✓ | 7044 |
if (IsSet(js_timeout)) { |
815 |
✗✓ | 8 |
CHECK(js_timeout->IsNumber()); |
816 |
16 |
int64_t timeout = js_timeout->IntegerValue(context).FromJust(); |
|
817 |
8 |
timeout_ = static_cast<uint64_t>(timeout); |
|
818 |
} |
||
819 |
|||
820 |
Local<Value> js_max_buffer = |
||
821 |
21132 |
js_options->Get(context, env()->max_buffer_string()).ToLocalChecked(); |
|
822 |
✓✓ | 7044 |
if (IsSet(js_max_buffer)) { |
823 |
✗✓ | 7042 |
CHECK(js_max_buffer->IsNumber()); |
824 |
14084 |
max_buffer_ = js_max_buffer->NumberValue(context).FromJust(); |
|
825 |
} |
||
826 |
|||
827 |
Local<Value> js_kill_signal = |
||
828 |
21132 |
js_options->Get(context, env()->kill_signal_string()).ToLocalChecked(); |
|
829 |
✓✓ | 7044 |
if (IsSet(js_kill_signal)) { |
830 |
✗✓ | 105 |
CHECK(js_kill_signal->IsInt32()); |
831 |
210 |
kill_signal_ = js_kill_signal.As<Int32>()->Value(); |
|
832 |
} |
||
833 |
|||
834 |
Local<Value> js_stdio = |
||
835 |
21132 |
js_options->Get(context, env()->stdio_string()).ToLocalChecked(); |
|
836 |
7044 |
r = ParseStdioOptions(js_stdio); |
|
837 |
✗✓ | 7044 |
if (r < 0) return Just(r); |
838 |
|||
839 |
7044 |
return Just(0); |
|
840 |
} |
||
841 |
|||
842 |
|||
843 |
7044 |
int SyncProcessRunner::ParseStdioOptions(Local<Value> js_value) { |
|
844 |
7044 |
HandleScope scope(env()->isolate()); |
|
845 |
Local<Array> js_stdio_options; |
||
846 |
|||
847 |
✗✓ | 7044 |
if (!js_value->IsArray()) |
848 |
return UV_EINVAL; |
||
849 |
|||
850 |
7044 |
Local<Context> context = env()->context(); |
|
851 |
7044 |
js_stdio_options = js_value.As<Array>(); |
|
852 |
|||
853 |
7044 |
stdio_count_ = js_stdio_options->Length(); |
|
854 |
✓✗ | 7044 |
uv_stdio_containers_ = new uv_stdio_container_t[stdio_count_]; |
855 |
|||
856 |
7044 |
stdio_pipes_.clear(); |
|
857 |
7044 |
stdio_pipes_.resize(stdio_count_); |
|
858 |
7044 |
stdio_pipes_initialized_ = true; |
|
859 |
|||
860 |
✓✓ | 56352 |
for (uint32_t i = 0; i < stdio_count_; i++) { |
861 |
Local<Value> js_stdio_option = |
||
862 |
42264 |
js_stdio_options->Get(context, i).ToLocalChecked(); |
|
863 |
|||
864 |
✗✓ | 21132 |
if (!js_stdio_option->IsObject()) |
865 |
return UV_EINVAL; |
||
866 |
|||
867 |
21132 |
int r = ParseStdioOption(i, js_stdio_option.As<Object>()); |
|
868 |
✗✓ | 21132 |
if (r < 0) |
869 |
return r; |
||
870 |
} |
||
871 |
|||
872 |
7044 |
uv_process_options_.stdio = uv_stdio_containers_; |
|
873 |
7044 |
uv_process_options_.stdio_count = stdio_count_; |
|
874 |
|||
875 |
7044 |
return 0; |
|
876 |
} |
||
877 |
|||
878 |
|||
879 |
21132 |
int SyncProcessRunner::ParseStdioOption(int child_fd, |
|
880 |
Local<Object> js_stdio_option) { |
||
881 |
21132 |
Local<Context> context = env()->context(); |
|
882 |
Local<Value> js_type = |
||
883 |
63396 |
js_stdio_option->Get(context, env()->type_string()).ToLocalChecked(); |
|
884 |
|||
885 |
✓✓ | 42264 |
if (js_type->StrictEquals(env()->ignore_string())) { |
886 |
83 |
return AddStdioIgnore(child_fd); |
|
887 |
|||
888 |
✓✓ | 42098 |
} else if (js_type->StrictEquals(env()->pipe_string())) { |
889 |
20982 |
Isolate* isolate = env()->isolate(); |
|
890 |
20982 |
Local<String> rs = env()->readable_string(); |
|
891 |
20982 |
Local<String> ws = env()->writable_string(); |
|
892 |
|||
893 |
41964 |
bool readable = js_stdio_option->Get(context, rs) |
|
894 |
62946 |
.ToLocalChecked()->BooleanValue(isolate); |
|
895 |
bool writable = |
||
896 |
41964 |
js_stdio_option->Get(context, ws) |
|
897 |
62946 |
.ToLocalChecked()->BooleanValue(isolate); |
|
898 |
|||
899 |
20982 |
uv_buf_t buf = uv_buf_init(nullptr, 0); |
|
900 |
|||
901 |
✓✓ | 20982 |
if (readable) { |
902 |
Local<Value> input = |
||
903 |
20925 |
js_stdio_option->Get(context, env()->input_string()).ToLocalChecked(); |
|
904 |
✓✓ | 6975 |
if (Buffer::HasInstance(input)) { |
905 |
buf = uv_buf_init(Buffer::Data(input), |
||
906 |
21 |
static_cast<unsigned int>(Buffer::Length(input))); |
|
907 |
✗✓✗✗ ✗✓ |
13908 |
} else if (!input->IsUndefined() && !input->IsNull()) { |
908 |
// Strings, numbers etc. are currently unsupported. It's not possible |
||
909 |
// to create a buffer for them here because there is no way to free |
||
910 |
// them afterwards. |
||
911 |
return UV_EINVAL; |
||
912 |
} |
||
913 |
} |
||
914 |
|||
915 |
20982 |
return AddStdioPipe(child_fd, readable, writable, buf); |
|
916 |
|||
917 |
✓✓✓✗ ✓✗✓✗ |
324 |
} else if (js_type->StrictEquals(env()->inherit_string()) || |
918 |
✓✓ | 123 |
js_type->StrictEquals(env()->fd_string())) { |
919 |
201 |
int inherit_fd = js_stdio_option->Get(context, env()->fd_string()) |
|
920 |
268 |
.ToLocalChecked()->Int32Value(context).FromJust(); |
|
921 |
67 |
return AddStdioInheritFD(child_fd, inherit_fd); |
|
922 |
|||
923 |
} else { |
||
924 |
CHECK(0 && "invalid child stdio type"); |
||
925 |
return UV_EINVAL; |
||
926 |
} |
||
927 |
} |
||
928 |
|||
929 |
|||
930 |
83 |
int SyncProcessRunner::AddStdioIgnore(uint32_t child_fd) { |
|
931 |
✗✓ | 83 |
CHECK_LT(child_fd, stdio_count_); |
932 |
✗✓ | 83 |
CHECK(!stdio_pipes_[child_fd]); |
933 |
|||
934 |
83 |
uv_stdio_containers_[child_fd].flags = UV_IGNORE; |
|
935 |
|||
936 |
83 |
return 0; |
|
937 |
} |
||
938 |
|||
939 |
|||
940 |
20982 |
int SyncProcessRunner::AddStdioPipe(uint32_t child_fd, |
|
941 |
bool readable, |
||
942 |
bool writable, |
||
943 |
uv_buf_t input_buffer) { |
||
944 |
✗✓ | 20982 |
CHECK_LT(child_fd, stdio_count_); |
945 |
✗✓ | 20982 |
CHECK(!stdio_pipes_[child_fd]); |
946 |
|||
947 |
std::unique_ptr<SyncProcessStdioPipe> h( |
||
948 |
20982 |
new SyncProcessStdioPipe(this, readable, writable, input_buffer)); |
|
949 |
|||
950 |
20982 |
int r = h->Initialize(uv_loop_); |
|
951 |
✗✓ | 20982 |
if (r < 0) { |
952 |
h.reset(); |
||
953 |
return r; |
||
954 |
} |
||
955 |
|||
956 |
20982 |
uv_stdio_containers_[child_fd].flags = h->uv_flags(); |
|
957 |
20982 |
uv_stdio_containers_[child_fd].data.stream = h->uv_stream(); |
|
958 |
|||
959 |
20982 |
stdio_pipes_[child_fd] = std::move(h); |
|
960 |
|||
961 |
20982 |
return 0; |
|
962 |
} |
||
963 |
|||
964 |
|||
965 |
67 |
int SyncProcessRunner::AddStdioInheritFD(uint32_t child_fd, int inherit_fd) { |
|
966 |
✗✓ | 67 |
CHECK_LT(child_fd, stdio_count_); |
967 |
✗✓ | 67 |
CHECK(!stdio_pipes_[child_fd]); |
968 |
|||
969 |
67 |
uv_stdio_containers_[child_fd].flags = UV_INHERIT_FD; |
|
970 |
67 |
uv_stdio_containers_[child_fd].data.fd = inherit_fd; |
|
971 |
|||
972 |
67 |
return 0; |
|
973 |
} |
||
974 |
|||
975 |
|||
976 |
49308 |
bool SyncProcessRunner::IsSet(Local<Value> value) { |
|
977 |
✓✓✓✓ |
127222 |
return !value->IsUndefined() && !value->IsNull(); |
978 |
} |
||
979 |
|||
980 |
7141 |
Maybe<int> SyncProcessRunner::CopyJsString(Local<Value> js_value, |
|
981 |
const char** target) { |
||
982 |
7141 |
Isolate* isolate = env()->isolate(); |
|
983 |
Local<String> js_string; |
||
984 |
size_t size, written; |
||
985 |
char* buffer; |
||
986 |
|||
987 |
✓✗ | 14282 |
if (js_value->IsString()) |
988 |
7141 |
js_string = js_value.As<String>(); |
|
989 |
else if (!js_value->ToString(env()->isolate()->GetCurrentContext()) |
||
990 |
.ToLocal(&js_string)) |
||
991 |
return Nothing<int>(); |
||
992 |
|||
993 |
// Include space for null terminator byte. |
||
994 |
✗✓ | 14282 |
if (!StringBytes::StorageSize(isolate, js_string, UTF8).To(&size)) |
995 |
return Nothing<int>(); |
||
996 |
7141 |
size += 1; |
|
997 |
|||
998 |
7141 |
buffer = new char[size]; |
|
999 |
|||
1000 |
7141 |
written = StringBytes::Write(isolate, buffer, -1, js_string, UTF8); |
|
1001 |
7141 |
buffer[written] = '\0'; |
|
1002 |
|||
1003 |
7141 |
*target = buffer; |
|
1004 |
7141 |
return Just(0); |
|
1005 |
} |
||
1006 |
|||
1007 |
14088 |
Maybe<int> SyncProcessRunner::CopyJsStringArray(Local<Value> js_value, |
|
1008 |
char** target) { |
||
1009 |
14088 |
Isolate* isolate = env()->isolate(); |
|
1010 |
Local<Array> js_array; |
||
1011 |
uint32_t length; |
||
1012 |
size_t list_size, data_size, data_offset; |
||
1013 |
char** list; |
||
1014 |
char* buffer; |
||
1015 |
|||
1016 |
✗✓ | 14088 |
if (!js_value->IsArray()) return Just<int>(UV_EINVAL); |
1017 |
|||
1018 |
14088 |
Local<Context> context = env()->context(); |
|
1019 |
42264 |
js_array = js_value.As<Array>()->Clone().As<Array>(); |
|
1020 |
14088 |
length = js_array->Length(); |
|
1021 |
14088 |
data_size = 0; |
|
1022 |
|||
1023 |
// Index has a pointer to every string element, plus one more for a final |
||
1024 |
// null pointer. |
||
1025 |
14088 |
list_size = (length + 1) * sizeof *list; |
|
1026 |
|||
1027 |
// Convert all array elements to string. Modify the js object itself if |
||
1028 |
// needed - it's okay since we cloned the original object. Also compute the |
||
1029 |
// length of all strings, including room for a null terminator after every |
||
1030 |
// string. Align strings to cache lines. |
||
1031 |
✓✓ | 499334 |
for (uint32_t i = 0; i < length; i++) { |
1032 |
970492 |
auto value = js_array->Get(context, i).ToLocalChecked(); |
|
1033 |
|||
1034 |
✓✓ | 970492 |
if (!value->IsString()) { |
1035 |
Local<String> string; |
||
1036 |
✗✓ | 168 |
if (!value->ToString(env()->isolate()->GetCurrentContext()) |
1037 |
126 |
.ToLocal(&string)) |
|
1038 |
return Nothing<int>(); |
||
1039 |
js_array |
||
1040 |
42 |
->Set(context, |
|
1041 |
i, |
||
1042 |
84 |
value->ToString(env()->isolate()->GetCurrentContext()) |
|
1043 |
84 |
.ToLocalChecked()) |
|
1044 |
84 |
.Check(); |
|
1045 |
} |
||
1046 |
|||
1047 |
485246 |
Maybe<size_t> maybe_size = StringBytes::StorageSize(isolate, value, UTF8); |
|
1048 |
✗✓ | 485246 |
if (maybe_size.IsNothing()) return Nothing<int>(); |
1049 |
485246 |
data_size += maybe_size.FromJust() + 1; |
|
1050 |
485246 |
data_size = RoundUp(data_size, sizeof(void*)); |
|
1051 |
} |
||
1052 |
|||
1053 |
14088 |
buffer = new char[list_size + data_size]; |
|
1054 |
|||
1055 |
14088 |
list = reinterpret_cast<char**>(buffer); |
|
1056 |
14088 |
data_offset = list_size; |
|
1057 |
|||
1058 |
✓✓ | 499334 |
for (uint32_t i = 0; i < length; i++) { |
1059 |
485246 |
list[i] = buffer + data_offset; |
|
1060 |
970492 |
auto value = js_array->Get(context, i).ToLocalChecked(); |
|
1061 |
data_offset += StringBytes::Write(isolate, |
||
1062 |
buffer + data_offset, |
||
1063 |
-1, |
||
1064 |
value, |
||
1065 |
485246 |
UTF8); |
|
1066 |
485246 |
buffer[data_offset++] = '\0'; |
|
1067 |
485246 |
data_offset = RoundUp(data_offset, sizeof(void*)); |
|
1068 |
} |
||
1069 |
|||
1070 |
14088 |
list[length] = nullptr; |
|
1071 |
|||
1072 |
14088 |
*target = buffer; |
|
1073 |
14088 |
return Just(0); |
|
1074 |
} |
||
1075 |
|||
1076 |
|||
1077 |
6445 |
void SyncProcessRunner::ExitCallback(uv_process_t* handle, |
|
1078 |
int64_t exit_status, |
||
1079 |
int term_signal) { |
||
1080 |
6445 |
SyncProcessRunner* self = reinterpret_cast<SyncProcessRunner*>(handle->data); |
|
1081 |
6445 |
uv_close(reinterpret_cast<uv_handle_t*>(handle), nullptr); |
|
1082 |
6445 |
self->OnExit(exit_status, term_signal); |
|
1083 |
6445 |
} |
|
1084 |
|||
1085 |
|||
1086 |
5 |
void SyncProcessRunner::KillTimerCallback(uv_timer_t* handle) { |
|
1087 |
5 |
SyncProcessRunner* self = reinterpret_cast<SyncProcessRunner*>(handle->data); |
|
1088 |
5 |
self->OnKillTimerTimeout(); |
|
1089 |
5 |
} |
|
1090 |
|||
1091 |
|||
1092 |
7 |
void SyncProcessRunner::KillTimerCloseCallback(uv_handle_t* handle) { |
|
1093 |
// No-op. |
||
1094 |
7 |
} |
|
1095 |
|||
1096 |
} // namespace node |
||
1097 |
|||
1098 |
4955 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(spawn_sync, |
|
1099 |
node::SyncProcessRunner::Initialize) |
Generated by: GCOVR (Version 3.4) |