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