1// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/globals.h"
6#if defined(HOST_OS_FUCHSIA)
7
8#include "bin/process.h"
9
10#include <errno.h>
11#include <fcntl.h>
12#include <lib/fdio/io.h>
13#include <lib/fdio/namespace.h>
14#include <lib/fdio/spawn.h>
15#include <poll.h>
16#include <pthread.h>
17#include <stdbool.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22#include <zircon/process.h>
23#include <zircon/processargs.h>
24#include <zircon/status.h>
25#include <zircon/syscalls.h>
26#include <zircon/syscalls/object.h>
27#include <zircon/types.h>
28
29#include "bin/dartutils.h"
30#include "bin/eventhandler.h"
31#include "bin/fdutils.h"
32#include "bin/file.h"
33#include "bin/lockers.h"
34#include "bin/namespace.h"
35#include "bin/namespace_fuchsia.h"
36#include "platform/signal_blocker.h"
37#include "platform/syslog.h"
38#include "platform/utils.h"
39
40// #define PROCESS_LOGGING 1
41#if defined(PROCESS_LOGGING)
42#define LOG_ERR(msg, ...) Syslog::PrintErr("Dart Process: " msg, ##__VA_ARGS__)
43#define LOG_INFO(msg, ...) Syslog::Print("Dart Process: " msg, ##__VA_ARGS__)
44#else
45#define LOG_ERR(msg, ...)
46#define LOG_INFO(msg, ...)
47#endif // defined(PROCESS_LOGGING)
48
49namespace dart {
50namespace bin {
51
52int Process::global_exit_code_ = 0;
53Mutex* Process::global_exit_code_mutex_ = nullptr;
54Process::ExitHook Process::exit_hook_ = NULL;
55
56// ProcessInfo is used to map a process id to the file descriptor for
57// the pipe used to communicate the exit code of the process to Dart.
58// ProcessInfo objects are kept in the static singly-linked
59// ProcessInfoList.
60class ProcessInfo {
61 public:
62 ProcessInfo(zx_handle_t process, intptr_t fd)
63 : process_(process), exit_pipe_fd_(fd) {}
64 ~ProcessInfo() {
65 int closed = NO_RETRY_EXPECTED(close(exit_pipe_fd_));
66 if (closed != 0) {
67 LOG_ERR("Failed to close process exit code pipe");
68 }
69 zx_handle_close(process_);
70 }
71 zx_handle_t process() const { return process_; }
72 intptr_t exit_pipe_fd() const { return exit_pipe_fd_; }
73 ProcessInfo* next() const { return next_; }
74 void set_next(ProcessInfo* info) { next_ = info; }
75
76 private:
77 zx_handle_t process_;
78 intptr_t exit_pipe_fd_;
79 ProcessInfo* next_;
80
81 DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
82};
83
84// Singly-linked list of ProcessInfo objects for all active processes
85// started from Dart.
86class ProcessInfoList {
87 public:
88 static void Init();
89 static void Cleanup();
90
91 static void AddProcess(zx_handle_t process, intptr_t fd) {
92 MutexLocker locker(mutex_);
93 ProcessInfo* info = new ProcessInfo(process, fd);
94 info->set_next(active_processes_);
95 active_processes_ = info;
96 }
97
98 static intptr_t LookupProcessExitFd(zx_handle_t process) {
99 MutexLocker locker(mutex_);
100 ProcessInfo* current = active_processes_;
101 while (current != NULL) {
102 if (current->process() == process) {
103 return current->exit_pipe_fd();
104 }
105 current = current->next();
106 }
107 return 0;
108 }
109
110 static bool Exists(zx_handle_t process) {
111 return LookupProcessExitFd(process) != 0;
112 }
113
114 static void RemoveProcess(zx_handle_t process) {
115 MutexLocker locker(mutex_);
116 ProcessInfo* prev = NULL;
117 ProcessInfo* current = active_processes_;
118 while (current != NULL) {
119 if (current->process() == process) {
120 if (prev == NULL) {
121 active_processes_ = current->next();
122 } else {
123 prev->set_next(current->next());
124 }
125 delete current;
126 return;
127 }
128 prev = current;
129 current = current->next();
130 }
131 }
132
133 private:
134 // Linked list of ProcessInfo objects for all active processes
135 // started from Dart code.
136 static ProcessInfo* active_processes_;
137 // Mutex protecting all accesses to the linked list of active
138 // processes.
139 static Mutex* mutex_;
140
141 DISALLOW_ALLOCATION();
142 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList);
143};
144
145ProcessInfo* ProcessInfoList::active_processes_ = NULL;
146Mutex* ProcessInfoList::mutex_ = nullptr;
147
148// The exit code handler sets up a separate thread which waits for child
149// processes to terminate. That separate thread can then get the exit code from
150// processes that have exited and communicate it to Dart through the
151// event loop.
152class ExitCodeHandler {
153 public:
154 static void Init();
155 static void Cleanup();
156
157 // Notify the ExitCodeHandler that another process exists.
158 static void Start() {
159 // Multiple isolates could be starting processes at the same
160 // time. Make sure that only one ExitCodeHandler thread exists.
161 MonitorLocker locker(monitor_);
162 if (running_) {
163 return;
164 }
165 LOG_INFO("ExitCodeHandler Starting\n");
166
167 zx_status_t status = zx_port_create(0, &port_);
168 if (status != ZX_OK) {
169 FATAL1("ExitCodeHandler: zx_port_create failed: %s\n",
170 zx_status_get_string(status));
171 return;
172 }
173
174 // Start thread that handles process exits when wait returns.
175 intptr_t result =
176 Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
177 if (result != 0) {
178 FATAL1("Failed to start exit code handler worker thread %ld", result);
179 }
180
181 running_ = true;
182 }
183
184 static zx_status_t Add(zx_handle_t process) {
185 MonitorLocker locker(monitor_);
186 LOG_INFO("ExitCodeHandler Adding Process: %u\n", process);
187 return zx_object_wait_async(process, port_, static_cast<uint64_t>(process),
188 ZX_TASK_TERMINATED, ZX_WAIT_ASYNC_ONCE);
189 }
190
191 static void Terminate() {
192 MonitorLocker locker(monitor_);
193 if (!running_) {
194 return;
195 }
196 running_ = false;
197
198 LOG_INFO("ExitCodeHandler Terminating\n");
199 SendShutdownMessage();
200
201 while (!terminate_done_) {
202 monitor_->Wait(Monitor::kNoTimeout);
203 }
204 zx_handle_close(port_);
205 LOG_INFO("ExitCodeHandler Terminated\n");
206 }
207
208 private:
209 static const uint64_t kShutdownPacketKey = 1;
210
211 static void SendShutdownMessage() {
212 zx_port_packet_t pkt;
213 pkt.key = kShutdownPacketKey;
214 zx_status_t status = zx_port_queue(port_, &pkt);
215 if (status != ZX_OK) {
216 Syslog::PrintErr("ExitCodeHandler: zx_port_queue failed: %s\n",
217 zx_status_get_string(status));
218 }
219 }
220
221 // Entry point for the separate exit code handler thread started by
222 // the ExitCodeHandler.
223 static void ExitCodeHandlerEntry(uword param) {
224 LOG_INFO("ExitCodeHandler Entering ExitCodeHandler thread\n");
225
226 zx_port_packet_t pkt;
227 while (true) {
228 zx_status_t status = zx_port_wait(port_, ZX_TIME_INFINITE, &pkt);
229 if (status != ZX_OK) {
230 FATAL1("ExitCodeHandler: zx_port_wait failed: %s\n",
231 zx_status_get_string(status));
232 }
233 if (pkt.type == ZX_PKT_TYPE_USER) {
234 ASSERT(pkt.key == kShutdownPacketKey);
235 break;
236 }
237 zx_handle_t process = static_cast<zx_handle_t>(pkt.key);
238 zx_signals_t observed = pkt.signal.observed;
239 if ((observed & ZX_TASK_TERMINATED) == ZX_SIGNAL_NONE) {
240 LOG_ERR("ExitCodeHandler: Unexpected signals, process %u: %ux\n",
241 process, observed);
242 }
243 SendProcessStatus(process);
244 }
245
246 LOG_INFO("ExitCodeHandler thread shutting down\n");
247 terminate_done_ = true;
248 monitor_->Notify();
249 }
250
251 static void SendProcessStatus(zx_handle_t process) {
252 LOG_INFO("ExitCodeHandler thread getting process status: %u\n", process);
253 int return_code = -1;
254 zx_info_process_t proc_info;
255 zx_status_t status = zx_object_get_info(
256 process, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL);
257 if (status != ZX_OK) {
258 Syslog::PrintErr("ExitCodeHandler: zx_object_get_info failed: %s\n",
259 zx_status_get_string(status));
260 } else {
261 return_code = proc_info.return_code;
262 }
263 zx_handle_close(process);
264 LOG_INFO("ExitCodeHandler thread process %u exited with %d\n", process,
265 return_code);
266
267 const intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(process);
268 LOG_INFO("ExitCodeHandler thread sending %u code %d on fd %ld\n", process,
269 return_code, exit_code_fd);
270 if (exit_code_fd != 0) {
271 int exit_message[2];
272 exit_message[0] = abs(return_code);
273 exit_message[1] = return_code >= 0 ? 0 : 1;
274 intptr_t result = FDUtils::WriteToBlocking(exit_code_fd, &exit_message,
275 sizeof(exit_message));
276 ASSERT((result == -1) || (result == sizeof(exit_code_fd)));
277 if ((result == -1) && (errno != EPIPE)) {
278 int err = errno;
279 Syslog::PrintErr("Failed to write exit code for process %d: errno=%d\n",
280 process, err);
281 }
282 LOG_INFO("ExitCodeHandler thread wrote %ld bytes to fd %ld\n", result,
283 exit_code_fd);
284 LOG_INFO("ExitCodeHandler thread removing process %u from list\n",
285 process);
286 ProcessInfoList::RemoveProcess(process);
287 } else {
288 LOG_ERR("ExitCodeHandler: Process %u not found\n", process);
289 }
290 }
291
292 static zx_handle_t port_;
293
294 // Protected by monitor_.
295 static bool terminate_done_;
296 static bool running_;
297 static Monitor* monitor_;
298
299 DISALLOW_ALLOCATION();
300 DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler);
301};
302
303zx_handle_t ExitCodeHandler::port_ = ZX_HANDLE_INVALID;
304bool ExitCodeHandler::running_ = false;
305bool ExitCodeHandler::terminate_done_ = false;
306Monitor* ExitCodeHandler::monitor_ = nullptr;
307
308void Process::TerminateExitCodeHandler() {
309 ExitCodeHandler::Terminate();
310}
311
312intptr_t Process::CurrentProcessId() {
313 return static_cast<intptr_t>(getpid());
314}
315
316int64_t Process::CurrentRSS() {
317 zx_info_task_stats_t task_stats;
318 zx_handle_t process = zx_process_self();
319 zx_status_t status = zx_object_get_info(
320 process, ZX_INFO_TASK_STATS, &task_stats, sizeof(task_stats), NULL, NULL);
321 if (status != ZX_OK) {
322 // TODO(zra): Translate this to a Unix errno.
323 errno = status;
324 return -1;
325 }
326 return task_stats.mem_private_bytes + task_stats.mem_shared_bytes;
327}
328
329int64_t Process::MaxRSS() {
330 // There is currently no way to get the high watermark value on Fuchsia, so
331 // just return the current RSS value.
332 return CurrentRSS();
333}
334
335class IOHandleScope {
336 public:
337 explicit IOHandleScope(IOHandle* io_handle) : io_handle_(io_handle) {}
338 ~IOHandleScope() {
339 io_handle_->Close();
340 io_handle_->Release();
341 }
342
343 private:
344 IOHandle* io_handle_;
345
346 DISALLOW_ALLOCATION();
347 DISALLOW_COPY_AND_ASSIGN(IOHandleScope);
348};
349
350bool Process::Wait(intptr_t pid,
351 intptr_t in,
352 intptr_t out,
353 intptr_t err,
354 intptr_t exit_event,
355 ProcessResult* result) {
356 IOHandle* out_iohandle = reinterpret_cast<IOHandle*>(out);
357 IOHandle* err_iohandle = reinterpret_cast<IOHandle*>(err);
358 IOHandle* exit_iohandle = reinterpret_cast<IOHandle*>(exit_event);
359
360 // There is no return from this function using Dart_PropagateError
361 // as memory used by the buffer lists is freed through their
362 // destructors.
363 BufferList out_data;
364 BufferList err_data;
365 union {
366 uint8_t bytes[8];
367 int32_t ints[2];
368 } exit_code_data;
369
370 // Create a port, which is like an epoll() fd on Linux.
371 zx_handle_t port;
372 zx_status_t status = zx_port_create(0, &port);
373 if (status != ZX_OK) {
374 Syslog::PrintErr("Process::Wait: zx_port_create failed: %s\n",
375 zx_status_get_string(status));
376 return false;
377 }
378
379 IOHandle* out_tmp = out_iohandle;
380 IOHandle* err_tmp = err_iohandle;
381 IOHandle* exit_tmp = exit_iohandle;
382 const uint64_t out_key = reinterpret_cast<uint64_t>(out_tmp);
383 const uint64_t err_key = reinterpret_cast<uint64_t>(err_tmp);
384 const uint64_t exit_key = reinterpret_cast<uint64_t>(exit_tmp);
385 const uint32_t events = POLLRDHUP | POLLIN;
386 if (!out_tmp->AsyncWait(port, events, out_key)) {
387 return false;
388 }
389 if (!err_tmp->AsyncWait(port, events, err_key)) {
390 return false;
391 }
392 if (!exit_tmp->AsyncWait(port, events, exit_key)) {
393 return false;
394 }
395 while ((out_tmp != NULL) || (err_tmp != NULL) || (exit_tmp != NULL)) {
396 zx_port_packet_t pkt;
397 status = zx_port_wait(port, ZX_TIME_INFINITE, &pkt);
398 if (status != ZX_OK) {
399 Syslog::PrintErr("Process::Wait: zx_port_wait failed: %s\n",
400 zx_status_get_string(status));
401 return false;
402 }
403 IOHandle* event_handle = reinterpret_cast<IOHandle*>(pkt.key);
404 const intptr_t event_mask = event_handle->WaitEnd(pkt.signal.observed);
405 if (event_handle == out_tmp) {
406 if ((event_mask & POLLIN) != 0) {
407 const intptr_t avail = FDUtils::AvailableBytes(out_tmp->fd());
408 if (!out_data.Read(out_tmp->fd(), avail)) {
409 return false;
410 }
411 }
412 if ((event_mask & POLLRDHUP) != 0) {
413 out_tmp->CancelWait(port, out_key);
414 out_tmp = NULL;
415 }
416 } else if (event_handle == err_tmp) {
417 if ((event_mask & POLLIN) != 0) {
418 const intptr_t avail = FDUtils::AvailableBytes(err_tmp->fd());
419 if (!err_data.Read(err_tmp->fd(), avail)) {
420 return false;
421 }
422 }
423 if ((event_mask & POLLRDHUP) != 0) {
424 err_tmp->CancelWait(port, err_key);
425 err_tmp = NULL;
426 }
427 } else if (event_handle == exit_tmp) {
428 if ((event_mask & POLLIN) != 0) {
429 const intptr_t avail = FDUtils::AvailableBytes(exit_tmp->fd());
430 if (avail == 8) {
431 intptr_t b =
432 NO_RETRY_EXPECTED(read(exit_tmp->fd(), exit_code_data.bytes, 8));
433 if (b != 8) {
434 return false;
435 }
436 }
437 }
438 if ((event_mask & POLLRDHUP) != 0) {
439 exit_tmp->CancelWait(port, exit_key);
440 exit_tmp = NULL;
441 }
442 } else {
443 Syslog::PrintErr("Process::Wait: Unexpected wait key: %p\n",
444 event_handle);
445 }
446 if (out_tmp != NULL) {
447 if (!out_tmp->AsyncWait(port, events, out_key)) {
448 return false;
449 }
450 }
451 if (err_tmp != NULL) {
452 if (!err_tmp->AsyncWait(port, events, err_key)) {
453 return false;
454 }
455 }
456 if (exit_tmp != NULL) {
457 if (!exit_tmp->AsyncWait(port, events, exit_key)) {
458 return false;
459 }
460 }
461 }
462
463 // All handles closed and all data read.
464 result->set_stdout_data(out_data.GetData());
465 result->set_stderr_data(err_data.GetData());
466 DEBUG_ASSERT(out_data.IsEmpty());
467 DEBUG_ASSERT(err_data.IsEmpty());
468
469 // Calculate the exit code.
470 intptr_t exit_code = exit_code_data.ints[0];
471 intptr_t negative = exit_code_data.ints[1];
472 if (negative != 0) {
473 exit_code = -exit_code;
474 }
475 result->set_exit_code(exit_code);
476
477 // Close the process handle.
478 zx_handle_t process = static_cast<zx_handle_t>(pid);
479 zx_handle_close(process);
480 return true;
481}
482
483bool Process::Kill(intptr_t id, int signal) {
484 LOG_INFO("Sending signal %d to process with id %ld\n", signal, id);
485 // zx_task_kill is definitely going to kill the process.
486 if ((signal != SIGTERM) && (signal != SIGKILL)) {
487 LOG_ERR("Signal %d not supported\n", signal);
488 errno = ENOSYS;
489 return false;
490 }
491 // We can only use zx_task_kill if we know id is a process handle, and we only
492 // know that for sure if it's in our list.
493 zx_handle_t process = static_cast<zx_handle_t>(id);
494 if (!ProcessInfoList::Exists(process)) {
495 LOG_ERR("Process %ld wasn't in the ProcessInfoList\n", id);
496 errno = ESRCH; // No such process.
497 return false;
498 }
499 zx_status_t status = zx_task_kill(process);
500 if (status != ZX_OK) {
501 LOG_ERR("zx_task_kill failed: %s\n", zx_status_get_string(status));
502 errno = EPERM; // TODO(zra): Figure out what it really should be.
503 return false;
504 }
505 LOG_INFO("Signal %d sent successfully to process %ld\n", signal, id);
506 return true;
507}
508
509class ProcessStarter {
510 public:
511 ProcessStarter(Namespace* namespc,
512 const char* path,
513 char* arguments[],
514 intptr_t arguments_length,
515 const char* working_directory,
516 char* environment[],
517 intptr_t environment_length,
518 ProcessStartMode mode,
519 intptr_t* in,
520 intptr_t* out,
521 intptr_t* err,
522 intptr_t* id,
523 intptr_t* exit_event,
524 char** os_error_message)
525 : namespc_(namespc),
526 path_(path),
527 working_directory_(working_directory),
528 mode_(mode),
529 in_(in),
530 out_(out),
531 err_(err),
532 id_(id),
533 exit_event_(exit_event),
534 os_error_message_(os_error_message) {
535 LOG_INFO("ProcessStarter: ctor %s with %ld args, mode = %d\n", path,
536 arguments_length, mode);
537
538 read_in_ = -1;
539 read_err_ = -1;
540 write_out_ = -1;
541
542 program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
543 (arguments_length + 2) * sizeof(*program_arguments_)));
544 program_arguments_[0] = const_cast<char*>(path_);
545 for (int i = 0; i < arguments_length; i++) {
546 program_arguments_[i + 1] = arguments[i];
547 }
548 program_arguments_[arguments_length + 1] = NULL;
549
550 program_environment_ = NULL;
551 if (environment != NULL) {
552 program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
553 (environment_length + 1) * sizeof(*program_environment_)));
554 for (int i = 0; i < environment_length; i++) {
555 program_environment_[i] = environment[i];
556 }
557 program_environment_[environment_length] = NULL;
558 }
559 }
560
561 ~ProcessStarter() {
562 if (read_in_ != -1) {
563 close(read_in_);
564 }
565 if (read_err_ != -1) {
566 close(read_err_);
567 }
568 if (write_out_ != -1) {
569 close(write_out_);
570 }
571 }
572
573 int Start() {
574 LOG_INFO("ProcessStarter: Start()\n");
575 int exit_pipe_fds[2];
576 intptr_t result = NO_RETRY_EXPECTED(pipe(exit_pipe_fds));
577 if (result != 0) {
578 *os_error_message_ = DartUtils::ScopedCopyCString(
579 "Failed to create exit code pipe for process start.");
580 return result;
581 }
582 LOG_INFO("ProcessStarter: Start() set up exit_pipe_fds (%d, %d)\n",
583 exit_pipe_fds[0], exit_pipe_fds[1]);
584
585 NamespaceScope ns(namespc_, path_);
586 const int pathfd =
587 TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), O_RDONLY));
588 zx_handle_t vmo = ZX_HANDLE_INVALID;
589 zx_status_t status = fdio_get_vmo_clone(pathfd, &vmo);
590 close(pathfd);
591 if (status != ZX_OK) {
592 close(exit_pipe_fds[0]);
593 close(exit_pipe_fds[1]);
594 *os_error_message_ = DartUtils::ScopedCopyCString(
595 "Failed to load executable for process start.");
596 return status;
597 }
598
599 // After reading the binary into a VMO, we need to mark it as executable,
600 // since the VMO returned by fdio_get_vmo_clone should be read-only.
601 status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);
602 if (status != ZX_OK) {
603 close(exit_pipe_fds[0]);
604 close(exit_pipe_fds[1]);
605 *os_error_message_ = DartUtils::ScopedCopyCString(
606 "Failed to mark binary as executable for process start.");
607 return status;
608 }
609
610 fdio_spawn_action_t* actions;
611 const intptr_t actions_count = BuildSpawnActions(
612 namespc_->namespc()->fdio_ns(), &actions);
613 if (actions_count < 0) {
614 zx_handle_close(vmo);
615 close(exit_pipe_fds[0]);
616 close(exit_pipe_fds[1]);
617 *os_error_message_ = DartUtils::ScopedCopyCString(
618 "Failed to build spawn actions array.");
619 return ZX_ERR_IO;
620 }
621
622 // TODO(zra): Use the supplied working directory when fdio_spawn_vmo adds an
623 // API to set it.
624
625 LOG_INFO("ProcessStarter: Start() Calling fdio_spawn_vmo\n");
626 zx_handle_t process = ZX_HANDLE_INVALID;
627 char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
628 uint32_t flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_DEFAULT_LDSVC;
629 status = fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, vmo, program_arguments_,
630 program_environment_, actions_count, actions,
631 &process, err_msg);
632 // Handles are consumed by fdio_spawn_vmo even if it fails.
633 delete[] actions;
634 if (status != ZX_OK) {
635 LOG_ERR("ProcessStarter: Start() fdio_spawn_vmo failed\n");
636 close(exit_pipe_fds[0]);
637 close(exit_pipe_fds[1]);
638 ReportStartError(err_msg);
639 return status;
640 }
641
642 LOG_INFO("ProcessStarter: Start() adding %u to list with exit_pipe %d\n",
643 process, exit_pipe_fds[1]);
644 ProcessInfoList::AddProcess(process, exit_pipe_fds[1]);
645 ExitCodeHandler::Start();
646 status = ExitCodeHandler::Add(process);
647 if (status != ZX_OK) {
648 LOG_ERR("ProcessStarter: ExitCodeHandler: Add failed: %s\n",
649 zx_status_get_string(status));
650 close(exit_pipe_fds[0]);
651 close(exit_pipe_fds[1]);
652 zx_task_kill(process);
653 ProcessInfoList::RemoveProcess(process);
654 ReportStartError(zx_status_get_string(status));
655 return status;
656 }
657
658 // The IOHandles allocated below are returned to Dart code. The Dart code
659 // calls into the runtime again to allocate a C++ Socket object, which
660 // becomes the native field of a Dart _NativeSocket object. The C++ Socket
661 // object and the EventHandler manage the lifetime of these IOHandles.
662 *id_ = process;
663 FDUtils::SetNonBlocking(read_in_);
664 *in_ = reinterpret_cast<intptr_t>(new IOHandle(read_in_));
665 read_in_ = -1;
666 FDUtils::SetNonBlocking(read_err_);
667 *err_ = reinterpret_cast<intptr_t>(new IOHandle(read_err_));
668 read_err_ = -1;
669 FDUtils::SetNonBlocking(write_out_);
670 *out_ = reinterpret_cast<intptr_t>(new IOHandle(write_out_));
671 write_out_ = -1;
672 FDUtils::SetNonBlocking(exit_pipe_fds[0]);
673 *exit_event_ = reinterpret_cast<intptr_t>(new IOHandle(exit_pipe_fds[0]));
674 return 0;
675 }
676
677 private:
678 void ReportStartError(const char* errormsg) {
679 const intptr_t kMaxMessageSize = 256;
680 char* message = DartUtils::ScopedCString(kMaxMessageSize);
681 snprintf(message, kMaxMessageSize, "Process start failed: %s\n", errormsg);
682 *os_error_message_ = message;
683 }
684
685 zx_status_t AddPipe(int target_fd, int* local_fd,
686 fdio_spawn_action_t* action) {
687 zx_status_t status = fdio_pipe_half(local_fd, &action->h.handle);
688 if (status != ZX_OK) return status;
689 action->action = FDIO_SPAWN_ACTION_ADD_HANDLE;
690 action->h.id = PA_HND(PA_HND_TYPE(PA_FD), target_fd);
691 return ZX_OK;
692 }
693
694 // Fills in 'actions_out' and returns action count.
695 intptr_t BuildSpawnActions(fdio_ns_t* ns, fdio_spawn_action_t** actions_out) {
696 const intptr_t fixed_actions_cnt = 4;
697 intptr_t ns_cnt = 0;
698 zx_status_t status;
699
700 // First, figure out how many namespace actions are needed.
701 fdio_flat_namespace_t* flat_ns = nullptr;
702 if (ns != nullptr) {
703 status = fdio_ns_export(ns, &flat_ns);
704 if (status != ZX_OK) {
705 LOG_ERR("ProcessStarter: BuildSpawnActions: fdio_ns_export: %s\n",
706 zx_status_get_string(status));
707 return -1;
708 }
709 ns_cnt = flat_ns->count;
710 }
711
712 // Allocate the actions array.
713 const intptr_t actions_cnt = ns_cnt + fixed_actions_cnt;
714 fdio_spawn_action_t* actions = new fdio_spawn_action_t[actions_cnt];
715
716 // Fill in the entries for passing stdin/out/err handles, and the program
717 // name.
718 status = AddPipe(0, &write_out_, &actions[0]);
719 if (status != ZX_OK) {
720 LOG_ERR("ProcessStarter: BuildSpawnActions: stdout AddPipe failed: %s\n",
721 zx_status_get_string(status));
722 if (flat_ns != nullptr) {
723 fdio_ns_free_flat_ns(flat_ns);
724 }
725 return -1;
726 }
727 status = AddPipe(1, &read_in_, &actions[1]);
728 if (status != ZX_OK) {
729 LOG_ERR("ProcessStarter: BuildSpawnActions: stdin AddPipe failed: %s\n",
730 zx_status_get_string(status));
731 if (flat_ns != nullptr) {
732 fdio_ns_free_flat_ns(flat_ns);
733 }
734 return -1;
735 }
736 status = AddPipe(2, &read_err_, &actions[2]);
737 if (status != ZX_OK) {
738 LOG_ERR("ProcessStarter: BuildSpawnActions: stderr AddPipe failed: %s\n",
739 zx_status_get_string(status));
740 if (flat_ns != nullptr) {
741 fdio_ns_free_flat_ns(flat_ns);
742 }
743 return -1;
744 }
745 actions[3] = {
746 .action = FDIO_SPAWN_ACTION_SET_NAME,
747 .name = {
748 .data = program_arguments_[0],
749 },
750 };
751
752 // Then fill in the namespace actions.
753 if (ns != nullptr) {
754 for (size_t i = 0; i < flat_ns->count; i++) {
755 actions[fixed_actions_cnt + i] = {
756 .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
757 .ns = {
758 .prefix = flat_ns->path[i],
759 .handle = flat_ns->handle[i],
760 },
761 };
762 }
763 free(flat_ns);
764 flat_ns = nullptr;
765 }
766
767 *actions_out = actions;
768 return actions_cnt;
769 }
770
771 int read_in_; // Pipe for stdout to child process.
772 int read_err_; // Pipe for stderr to child process.
773 int write_out_; // Pipe for stdin to child process.
774
775 char** program_arguments_;
776 char** program_environment_;
777
778 Namespace* namespc_;
779 const char* path_;
780 const char* working_directory_;
781 ProcessStartMode mode_;
782 intptr_t* in_;
783 intptr_t* out_;
784 intptr_t* err_;
785 intptr_t* id_;
786 intptr_t* exit_event_;
787 char** os_error_message_;
788
789 DISALLOW_ALLOCATION();
790 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
791};
792
793int Process::Start(Namespace* namespc,
794 const char* path,
795 char* arguments[],
796 intptr_t arguments_length,
797 const char* working_directory,
798 char* environment[],
799 intptr_t environment_length,
800 ProcessStartMode mode,
801 intptr_t* in,
802 intptr_t* out,
803 intptr_t* err,
804 intptr_t* id,
805 intptr_t* exit_event,
806 char** os_error_message) {
807 if (mode != kNormal) {
808 *os_error_message = DartUtils::ScopedCopyCString(
809 "Only ProcessStartMode.NORMAL is supported on this platform");
810 return -1;
811 }
812 ProcessStarter starter(namespc, path, arguments, arguments_length,
813 working_directory, environment, environment_length,
814 mode, in, out, err, id, exit_event, os_error_message);
815 return starter.Start();
816}
817
818intptr_t Process::SetSignalHandler(intptr_t signal) {
819 errno = ENOSYS;
820 return -1;
821}
822
823void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {}
824
825void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {}
826
827void ProcessInfoList::Init() {
828 ASSERT(ProcessInfoList::mutex_ == nullptr);
829 ProcessInfoList::mutex_ = new Mutex();
830}
831
832void ProcessInfoList::Cleanup() {
833 ASSERT(ProcessInfoList::mutex_ != nullptr);
834 delete ProcessInfoList::mutex_;
835 ProcessInfoList::mutex_ = nullptr;
836}
837
838void ExitCodeHandler::Init() {
839 ASSERT(ExitCodeHandler::monitor_ == nullptr);
840 ExitCodeHandler::monitor_ = new Monitor();
841}
842
843void ExitCodeHandler::Cleanup() {
844 ASSERT(ExitCodeHandler::monitor_ != nullptr);
845 delete ExitCodeHandler::monitor_;
846 ExitCodeHandler::monitor_ = nullptr;
847}
848
849void Process::Init() {
850 ExitCodeHandler::Init();
851 ProcessInfoList::Init();
852
853 ASSERT(Process::global_exit_code_mutex_ == nullptr);
854 Process::global_exit_code_mutex_ = new Mutex();
855}
856
857void Process::Cleanup() {
858 ASSERT(Process::global_exit_code_mutex_ != nullptr);
859 delete Process::global_exit_code_mutex_;
860 Process::global_exit_code_mutex_ = nullptr;
861
862 ProcessInfoList::Cleanup();
863 ExitCodeHandler::Cleanup();
864}
865
866} // namespace bin
867} // namespace dart
868
869#endif // defined(HOST_OS_FUCHSIA)
870