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