Flutter Engine
The Flutter Engine
process_macos.cc
Go to the documentation of this file.
1// Copyright (c) 2012, 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_MACOS)
7
8#include "bin/process.h"
9
10#if !DART_HOST_OS_IOS
11#include <crt_externs.h> // NOLINT
12#endif
13#include <errno.h> // NOLINT
14#include <fcntl.h> // NOLINT
15#include <mach/mach.h> // NOLINT
16#include <poll.h> // NOLINT
17#include <signal.h> // NOLINT
18#include <stdio.h> // NOLINT
19#include <stdlib.h> // NOLINT
20#include <string.h> // NOLINT
21#include <unistd.h> // NOLINT
22
23#include "bin/dartutils.h"
24#include "bin/fdutils.h"
25#include "bin/lockers.h"
26#include "bin/namespace.h"
27#include "bin/thread.h"
28#include "platform/syslog.h"
29
31#include "platform/utils.h"
32
33namespace dart {
34namespace bin {
35
36int Process::global_exit_code_ = 0;
37Mutex* Process::global_exit_code_mutex_ = nullptr;
38Process::ExitHook Process::exit_hook_ = nullptr;
39
40// Spawning new processes isn't supported on iOS.
41#if !defined(DART_HOST_OS_IOS)
42
43// ProcessInfo is used to map a process id to the file descriptor for
44// the pipe used to communicate the exit code of the process to Dart.
45// ProcessInfo objects are kept in the static singly-linked
46// ProcessInfoList.
47class ProcessInfo {
48 public:
49 ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) {}
50 ~ProcessInfo() {
51 int closed = close(fd_);
52 if (closed != 0) {
53 FATAL("Failed to close process exit code pipe");
54 }
55 }
56 pid_t pid() { return pid_; }
57 intptr_t fd() { return fd_; }
58 ProcessInfo* next() { return next_; }
59 void set_next(ProcessInfo* info) { next_ = info; }
60
61 private:
62 pid_t pid_;
63 intptr_t fd_;
64 ProcessInfo* next_;
65
66 DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
67};
68
69// Singly-linked list of ProcessInfo objects for all active processes
70// started from Dart.
71class ProcessInfoList {
72 public:
73 static void Init();
74 static void Cleanup();
75
76 static void AddProcess(pid_t pid, intptr_t fd) {
77 MutexLocker locker(mutex_);
78 ProcessInfo* info = new ProcessInfo(pid, fd);
79 info->set_next(active_processes_);
80 active_processes_ = info;
81 }
82
83 static intptr_t LookupProcessExitFd(pid_t pid) {
84 MutexLocker locker(mutex_);
85 ProcessInfo* current = active_processes_;
86 while (current != nullptr) {
87 if (current->pid() == pid) {
88 return current->fd();
89 }
90 current = current->next();
91 }
92 return 0;
93 }
94
95 static void RemoveProcess(pid_t pid) {
96 MutexLocker locker(mutex_);
97 ProcessInfo* prev = nullptr;
98 ProcessInfo* current = active_processes_;
99 while (current != nullptr) {
100 if (current->pid() == pid) {
101 if (prev == nullptr) {
102 active_processes_ = current->next();
103 } else {
104 prev->set_next(current->next());
105 }
106 delete current;
107 return;
108 }
109 prev = current;
110 current = current->next();
111 }
112 }
113
114 private:
115 // Linked list of ProcessInfo objects for all active processes
116 // started from Dart code.
117 static ProcessInfo* active_processes_;
118 // Mutex protecting all accesses to the linked list of active
119 // processes.
120 static Mutex* mutex_;
121
123 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList);
124};
125
126ProcessInfo* ProcessInfoList::active_processes_ = nullptr;
127Mutex* ProcessInfoList::mutex_ = nullptr;
128
129// The exit code handler sets up a separate thread which waits for child
130// processes to terminate. That separate thread can then get the exit code from
131// processes that have exited and communicate it to Dart through the
132// event loop.
133class ExitCodeHandler {
134 public:
135 static void Init();
136 static void Cleanup();
137
138 // Notify the ExitCodeHandler that another process exists.
139 static void ProcessStarted() {
140 // Multiple isolates could be starting processes at the same
141 // time. Make sure that only one ExitCodeHandler thread exists.
142 MonitorLocker locker(monitor_);
143 process_count_++;
144
145 monitor_->Notify();
146
147 if (running_) {
148 return;
149 }
150
151 // Start thread that handles process exits when wait returns.
152 int result =
153 Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
154 if (result != 0) {
155 FATAL("Failed to start exit code handler worker thread %d", result);
156 }
157
158 running_ = true;
159 }
160
161 static void TerminateExitCodeThread() {
162 MonitorLocker locker(monitor_);
163
164 if (!running_) {
165 return;
166 }
167
168 // Set terminate_done_ to false, so we can use it as a guard for our
169 // monitor.
170 running_ = false;
171
172 // Fork to wake up waitpid.
173 if (TEMP_FAILURE_RETRY(fork()) == 0) {
174 _Exit(0);
175 }
176
177 monitor_->Notify();
178
179 while (!terminate_done_) {
180 monitor_->Wait(Monitor::kNoTimeout);
181 }
182 }
183
184 private:
185 // Entry point for the separate exit code handler thread started by
186 // the ExitCodeHandler.
187 static void ExitCodeHandlerEntry(uword param) {
188 pid_t pid = 0;
189 int status = 0;
190 while (true) {
191 {
192 MonitorLocker locker(monitor_);
193 while (running_ && process_count_ == 0) {
194 monitor_->Wait(Monitor::kNoTimeout);
195 }
196 if (!running_) {
197 terminate_done_ = true;
198 monitor_->Notify();
199 return;
200 }
201 }
202
203 if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
204 int exit_code = 0;
205 int negative = 0;
206 if (WIFEXITED(status)) {
207 exit_code = WEXITSTATUS(status);
208 }
209 if (WIFSIGNALED(status)) {
210 exit_code = WTERMSIG(status);
211 negative = 1;
212 }
213 intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
214 if (exit_code_fd != 0) {
215 int message[2] = {exit_code, negative};
216 ssize_t result =
217 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
218 // If the process has been closed, the read end of the exit
219 // pipe has been closed. It is therefore not a problem that
220 // write fails with a broken pipe error. Other errors should
221 // not happen.
222 if ((result != -1) && (result != sizeof(message))) {
223 FATAL("Failed to write entire process exit message");
224 } else if ((result == -1) && (errno != EPIPE)) {
225 FATAL("Failed to write exit code: %d", errno);
226 }
227 ProcessInfoList::RemoveProcess(pid);
228 {
229 MonitorLocker locker(monitor_);
230 process_count_--;
231 }
232 }
233 } else if (pid < 0) {
234 FATAL("Wait for process exit failed: %d", errno);
235 }
236 }
237 }
238
239 static bool terminate_done_;
240 static int process_count_;
241 static bool running_;
242 static Monitor* monitor_;
243
245 DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler);
246};
247
248bool ExitCodeHandler::running_ = false;
249int ExitCodeHandler::process_count_ = 0;
250bool ExitCodeHandler::terminate_done_ = false;
251Monitor* ExitCodeHandler::monitor_ = nullptr;
252
253class ProcessStarter {
254 public:
255 ProcessStarter(const char* path,
256 char* arguments[],
257 intptr_t arguments_length,
258 const char* working_directory,
259 char* environment[],
260 intptr_t environment_length,
262 intptr_t* in,
263 intptr_t* out,
264 intptr_t* err,
265 intptr_t* id,
266 intptr_t* exit_event,
267 char** os_error_message)
268 : path_(path),
269 working_directory_(working_directory),
270 mode_(mode),
271 in_(in),
272 out_(out),
273 err_(err),
274 id_(id),
275 exit_event_(exit_event),
276 os_error_message_(os_error_message) {
277 read_in_[0] = -1;
278 read_in_[1] = -1;
279 read_err_[0] = -1;
280 read_err_[1] = -1;
281 write_out_[0] = -1;
282 write_out_[1] = -1;
283 exec_control_[0] = -1;
284 exec_control_[1] = -1;
285
286 program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
287 (arguments_length + 2) * sizeof(*program_arguments_)));
288 program_arguments_[0] = const_cast<char*>(path_);
289 for (int i = 0; i < arguments_length; i++) {
290 program_arguments_[i + 1] = arguments[i];
291 }
292 program_arguments_[arguments_length + 1] = nullptr;
293
294 program_environment_ = nullptr;
295 if (environment != nullptr) {
296 program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
297 (environment_length + 1) * sizeof(*program_environment_)));
298 for (int i = 0; i < environment_length; i++) {
299 program_environment_[i] = environment[i];
300 }
301 program_environment_[environment_length] = nullptr;
302 }
303 }
304
305 int Start() {
306 // Create pipes required.
307 int err = CreatePipes();
308 if (err != 0) {
309 return err;
310 }
311
312 // Fork to create the new process.
313 pid_t pid = TEMP_FAILURE_RETRY(fork());
314 if (pid < 0) {
315 // Failed to fork.
316 return CleanupAndReturnError();
317 } else if (pid == 0) {
318 // This runs in the new process.
319 NewProcess();
320 }
321
322 // This runs in the original process.
323
324 // If the child process is not started in detached mode, be sure to
325 // listen for exit-codes, now that we have a non detached child process
326 // and also Register this child process.
327 if (Process::ModeIsAttached(mode_)) {
328 ExitCodeHandler::ProcessStarted();
329 err = RegisterProcess(pid);
330 if (err != 0) {
331 return err;
332 }
333 }
334
335 // Notify child process to start. This is done to delay the call to exec
336 // until the process is registered above, and we are ready to receive the
337 // exit code.
338 char msg = '1';
339 int bytes_written =
340 FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg));
341 if (bytes_written != sizeof(msg)) {
342 return CleanupAndReturnError();
343 }
344
345 // Read the result of executing the child process.
346 close(exec_control_[1]);
347 exec_control_[1] = -1;
348 if (Process::ModeIsAttached(mode_)) {
349 err = ReadExecResult();
350 } else {
351 err = ReadDetachedExecResult(&pid);
352 }
353 close(exec_control_[0]);
354 exec_control_[0] = -1;
355
356 // Return error code if any failures.
357 if (err != 0) {
358 if (Process::ModeIsAttached(mode_)) {
359 // Since exec() failed, we're not interested in the exit code.
360 // We close the reading side of the exit code pipe here.
361 // GetProcessExitCodes will get a broken pipe error when it
362 // tries to write to the writing side of the pipe and it will
363 // ignore the error.
364 close(*exit_event_);
365 *exit_event_ = -1;
366 }
367 CloseAllPipes();
368 return err;
369 }
370
371 if (Process::ModeHasStdio(mode_)) {
372 // Connect stdio, stdout and stderr.
373 FDUtils::SetNonBlocking(read_in_[0]);
374 *in_ = read_in_[0];
375 close(read_in_[1]);
376 FDUtils::SetNonBlocking(write_out_[1]);
377 *out_ = write_out_[1];
378 close(write_out_[0]);
379 FDUtils::SetNonBlocking(read_err_[0]);
380 *err_ = read_err_[0];
381 close(read_err_[1]);
382 } else {
383 // Close all fds.
384 close(read_in_[0]);
385 close(read_in_[1]);
386 ASSERT(write_out_[0] == -1);
387 ASSERT(write_out_[1] == -1);
388 ASSERT(read_err_[0] == -1);
389 ASSERT(read_err_[1] == -1);
390 }
391 ASSERT(exec_control_[0] == -1);
392 ASSERT(exec_control_[1] == -1);
393
394 *id_ = pid;
395 return 0;
396 }
397
398 private:
399 int CreatePipes() {
400 int result;
401 result = TEMP_FAILURE_RETRY(pipe(exec_control_));
402 if (result < 0) {
403 return CleanupAndReturnError();
404 }
405 FDUtils::SetCloseOnExec(exec_control_[0]);
406 FDUtils::SetCloseOnExec(exec_control_[1]);
407
408 // For a detached process the pipe to connect stdout is still used for
409 // signaling when to do the first fork.
410 result = TEMP_FAILURE_RETRY(pipe(read_in_));
411 if (result < 0) {
412 return CleanupAndReturnError();
413 }
414 FDUtils::SetCloseOnExec(read_in_[0]);
415 FDUtils::SetCloseOnExec(read_in_[1]);
416
417 // For detached processes the pipe to connect stderr and stdin are not used.
418 if (Process::ModeHasStdio(mode_)) {
419 result = TEMP_FAILURE_RETRY(pipe(read_err_));
420 if (result < 0) {
421 return CleanupAndReturnError();
422 }
423 FDUtils::SetCloseOnExec(read_err_[0]);
424 FDUtils::SetCloseOnExec(read_err_[1]);
425
426 result = TEMP_FAILURE_RETRY(pipe(write_out_));
427 if (result < 0) {
428 return CleanupAndReturnError();
429 }
430 FDUtils::SetCloseOnExec(write_out_[0]);
431 FDUtils::SetCloseOnExec(write_out_[1]);
432 }
433
434 return 0;
435 }
436
437 void NewProcess() {
438 // Wait for parent process before setting up the child process.
439 char msg;
440 int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg));
441 if (bytes_read != sizeof(msg)) {
442 perror("Failed receiving notification message");
443 _Exit(1);
444 }
445 if (Process::ModeIsAttached(mode_)) {
446 ExecProcess();
447 } else {
448 ExecDetachedProcess();
449 }
450 }
451
452 void ExecProcess() {
453 if (mode_ == kNormal) {
454 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
455 ReportChildError();
456 }
457
458 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
459 ReportChildError();
460 }
461
462 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
463 ReportChildError();
464 }
465 } else {
466 ASSERT(mode_ == kInheritStdio);
467 }
468
469 if (working_directory_ != nullptr &&
470 TEMP_FAILURE_RETRY(chdir(working_directory_)) == -1) {
471 ReportChildError();
472 }
473
474 if (program_environment_ != nullptr) {
475 // On MacOS you have to do a bit of magic to get to the
476 // environment strings.
477 char*** environ = _NSGetEnviron();
478 *environ = program_environment_;
479 }
480
481 execvp(path_, const_cast<char* const*>(program_arguments_));
482 ReportChildError();
483 }
484
485 void ExecDetachedProcess() {
486 if (mode_ == kDetached) {
487 ASSERT(write_out_[0] == -1);
488 ASSERT(write_out_[1] == -1);
489 ASSERT(read_err_[0] == -1);
490 ASSERT(read_err_[1] == -1);
491 // For a detached process the pipe to connect stdout is only used for
492 // signaling when to do the first fork.
493 close(read_in_[0]);
494 read_in_[0] = -1;
495 close(read_in_[1]);
496 read_in_[1] = -1;
497 } else {
498 // Don't close any fds if keeping stdio open to the detached process.
499 ASSERT(mode_ == kDetachedWithStdio);
500 }
501 // Fork once more to start a new session.
502 pid_t pid = TEMP_FAILURE_RETRY(fork());
503 if (pid < 0) {
504 ReportChildError();
505 } else if (pid == 0) {
506 // Start a new session.
507 if (TEMP_FAILURE_RETRY(setsid()) == -1) {
508 ReportChildError();
509 } else {
510 // Do a final fork to not be the session leader.
511 pid = TEMP_FAILURE_RETRY(fork());
512 if (pid < 0) {
513 ReportChildError();
514 } else if (pid == 0) {
515 if (mode_ == kDetached) {
516 SetupDetached();
517 } else {
518 SetupDetachedWithStdio();
519 }
520
521 if ((working_directory_ != nullptr) &&
522 (TEMP_FAILURE_RETRY(chdir(working_directory_)) == -1)) {
523 ReportChildError();
524 }
525
526 if (program_environment_ != nullptr) {
527 // On MacOS you have to do a bit of magic to get to the
528 // environment strings.
529 char*** environ = _NSGetEnviron();
530 *environ = program_environment_;
531 }
532
533 // Report the final PID and do the exec.
534 ReportPid(getpid()); // getpid cannot fail.
535 execvp(path_, const_cast<char* const*>(program_arguments_));
536 ReportChildError();
537 } else {
538 // Exit the intermediate process. Avoid any atexit callbacks
539 // to prevent deadlocks.
540 _Exit(0);
541 }
542 }
543 } else {
544 // Exit the intermediate process. Avoid any atexit callbacks
545 // to prevent deadlocks.
546 _Exit(0);
547 }
548 }
549
550 int RegisterProcess(pid_t pid) {
551 int result;
552 int event_fds[2];
553 result = TEMP_FAILURE_RETRY(pipe(event_fds));
554 if (result < 0) {
555 return CleanupAndReturnError();
556 }
557 FDUtils::SetCloseOnExec(event_fds[0]);
558 FDUtils::SetCloseOnExec(event_fds[1]);
559
560 ProcessInfoList::AddProcess(pid, event_fds[1]);
561 *exit_event_ = event_fds[0];
562 FDUtils::SetNonBlocking(event_fds[0]);
563 return 0;
564 }
565
566 int ReadExecResult() {
567 int child_errno;
568 int bytes_read = -1;
569 // Read exec result from child. If no data is returned the exec was
570 // successful and the exec call closed the pipe. Otherwise the errno
571 // is written to the pipe.
572 bytes_read = FDUtils::ReadFromBlocking(exec_control_[0], &child_errno,
573 sizeof(child_errno));
574 if (bytes_read == sizeof(child_errno)) {
575 ReadChildError();
576 return child_errno;
577 } else if (bytes_read == -1) {
578 return errno;
579 }
580 return 0;
581 }
582
583 int ReadDetachedExecResult(pid_t* pid) {
584 int child_errno;
585 int bytes_read = -1;
586 // Read exec result from child. If only pid data is returned the exec was
587 // successful and the exec call closed the pipe. Otherwise the errno
588 // is written to the pipe as well.
589 int result[2];
590 bytes_read =
591 FDUtils::ReadFromBlocking(exec_control_[0], result, sizeof(result));
592 if (bytes_read == sizeof(int)) {
593 *pid = result[0];
594 } else if (bytes_read == 2 * sizeof(int)) {
595 *pid = result[0];
596 child_errno = result[1];
597 ReadChildError();
598 return child_errno;
599 } else if (bytes_read == -1) {
600 return errno;
601 }
602 return 0;
603 }
604
605 void SetupDetached() {
606 ASSERT(mode_ == kDetached);
607
608 // Close all open file descriptors except for exec_control_[1].
609 int max_fds = sysconf(_SC_OPEN_MAX);
610 if (max_fds == -1) {
611 max_fds = _POSIX_OPEN_MAX;
612 }
613 for (int fd = 0; fd < max_fds; fd++) {
614 if (fd != exec_control_[1]) {
615 close(fd);
616 }
617 }
618
619 // Re-open stdin, stdout and stderr and connect them to /dev/null.
620 // The loop above should already have closed all of them, so
621 // creating new file descriptors should start at STDIN_FILENO.
622 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
623 if (fd != STDIN_FILENO) {
624 ReportChildError();
625 }
626 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
627 STDOUT_FILENO) {
628 ReportChildError();
629 }
630 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
631 STDERR_FILENO) {
632 ReportChildError();
633 }
634 }
635
636 void SetupDetachedWithStdio() {
637 // Close all open file descriptors except for
638 // exec_control_[1], write_out_[0], read_in_[1] and
639 // read_err_[1].
640 int max_fds = sysconf(_SC_OPEN_MAX);
641 if (max_fds == -1) {
642 max_fds = _POSIX_OPEN_MAX;
643 }
644 for (int fd = 0; fd < max_fds; fd++) {
645 if ((fd != exec_control_[1]) && (fd != write_out_[0]) &&
646 (fd != read_in_[1]) && (fd != read_err_[1])) {
647 close(fd);
648 }
649 }
650
651 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
652 ReportChildError();
653 }
654 close(write_out_[0]);
655
656 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
657 ReportChildError();
658 }
659 close(read_in_[1]);
660
661 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
662 ReportChildError();
663 }
664 close(read_err_[1]);
665 }
666
667 int CleanupAndReturnError() {
668 int actual_errno = errno;
669 // If CleanupAndReturnError is called without an actual errno make
670 // sure to return an error anyway.
671 if (actual_errno == 0) {
672 actual_errno = EPERM;
673 }
674 SetChildOsErrorMessage();
675 CloseAllPipes();
676 return actual_errno;
677 }
678
679 void SetChildOsErrorMessage() {
680 const int kBufferSize = 1024;
681 char* error_message = DartUtils::ScopedCString(kBufferSize);
682 Utils::StrError(errno, error_message, kBufferSize);
683 *os_error_message_ = error_message;
684 }
685
686 void ReportChildError() {
687 // In the case of failure in the child process write the errno and
688 // the OS error message to the exec control pipe and exit.
689 int child_errno = errno;
690 const int kBufferSize = 1024;
691 char os_error_message[kBufferSize];
692 Utils::StrError(errno, os_error_message, kBufferSize);
693 int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno,
694 sizeof(child_errno));
695 if (bytes_written == sizeof(child_errno)) {
696 FDUtils::WriteToBlocking(exec_control_[1], os_error_message,
697 strlen(os_error_message) + 1);
698 }
699 close(exec_control_[1]);
700 // Avoid calling any atexit callbacks to prevent deadlocks.
701 _Exit(1);
702 }
703
704 void ReportPid(int pid) {
705 // In the case of starting a detached process the actual pid of that process
706 // is communicated using the exec control pipe.
707 int bytes_written =
708 FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid));
709 ASSERT(bytes_written == sizeof(int));
710 USE(bytes_written);
711 }
712
713 void ReadChildError() {
714 const int kMaxMessageSize = 256;
715 char* message = DartUtils::ScopedCString(kMaxMessageSize);
716 if (message != nullptr) {
717 FDUtils::ReadFromBlocking(exec_control_[0], message, kMaxMessageSize);
718 message[kMaxMessageSize - 1] = '\0';
719 *os_error_message_ = message;
720 } else {
721 // Could not get error message. It will be nullptr.
722 ASSERT(*os_error_message_ == nullptr);
723 }
724 }
725
726 void ClosePipe(int* fds) {
727 for (int i = 0; i < 2; i++) {
728 if (fds[i] != -1) {
729 close(fds[i]);
730 fds[i] = -1;
731 }
732 }
733 }
734
735 void CloseAllPipes() {
736 ClosePipe(exec_control_);
737 ClosePipe(read_in_);
738 ClosePipe(read_err_);
739 ClosePipe(write_out_);
740 }
741
742 int read_in_[2]; // Pipe for stdout to child process.
743 int read_err_[2]; // Pipe for stderr to child process.
744 int write_out_[2]; // Pipe for stdin to child process.
745 int exec_control_[2]; // Pipe to get the result from exec.
746
747 char** program_arguments_;
748 char** program_environment_;
749
750 const char* path_;
751 const char* working_directory_;
752 ProcessStartMode mode_;
753 intptr_t* in_;
754 intptr_t* out_;
755 intptr_t* err_;
756 intptr_t* id_;
757 intptr_t* exit_event_;
758 char** os_error_message_;
759
761 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
762};
763#endif // !defined(DART_HOST_OS_IOS)
764
765int Process::Start(Namespace* namespc,
766 const char* path,
767 char* arguments[],
768 intptr_t arguments_length,
769 const char* working_directory,
770 char* environment[],
771 intptr_t environment_length,
773 intptr_t* in,
774 intptr_t* out,
775 intptr_t* err,
776 intptr_t* id,
777 intptr_t* exit_event,
778 char** os_error_message) {
779#if defined(DART_HOST_OS_IOS)
780 return EPERM;
781#else // defined(DART_HOST_OS_IOS)
782 ProcessStarter starter(path, arguments, arguments_length, working_directory,
783 environment, environment_length, mode, in, out, err,
784 id, exit_event, os_error_message);
785 return starter.Start();
786#endif // defined(DART_HOST_OS_IOS)
787}
788
789#if !defined(DART_HOST_OS_IOS)
790static bool CloseProcessBuffers(struct pollfd* fds, int alive) {
791 int e = errno;
792 for (int i = 0; i < alive; i++) {
793 close(fds[i].fd);
794 }
795 errno = e;
796 return false;
797}
798#endif // !defined(DART_HOST_OS_IOS)
799
800bool Process::Wait(intptr_t pid,
801 intptr_t in,
802 intptr_t out,
803 intptr_t err,
804 intptr_t exit_event,
805 ProcessResult* result) {
806#if defined(DART_HOST_OS_IOS)
807 return false;
808#else // defined(DART_HOST_OS_IOS)
809 // Close input to the process right away.
810 close(in);
811
812 // There is no return from this function using Dart_PropagateError
813 // as memory used by the buffer lists is freed through their
814 // destructors.
815 BufferList out_data;
816 BufferList err_data;
817 union {
818 uint8_t bytes[8];
819 int32_t ints[2];
820 } exit_code_data;
821
822 struct pollfd fds[3];
823 fds[0].fd = out;
824 fds[1].fd = err;
825 fds[2].fd = exit_event;
826
827 for (int i = 0; i < 3; i++) {
828 fds[i].events = POLLIN;
829 }
830
831 int alive = 3;
832 while (alive > 0) {
833 // Blocking call waiting for events from the child process.
834 if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) {
835 return CloseProcessBuffers(fds, alive);
836 }
837
838 // Process incoming data.
839 for (int i = 0; i < alive; i++) {
840 intptr_t avail;
841 if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) {
842 return CloseProcessBuffers(fds, alive);
843 }
844 if ((fds[i].revents & POLLIN) != 0) {
845 avail = FDUtils::AvailableBytes(fds[i].fd);
846 // On Mac OS POLLIN can be set with zero available
847 // bytes. POLLHUP is most likely also set in this case.
848 if (avail > 0) {
849 if (fds[i].fd == out) {
850 if (!out_data.Read(out, avail)) {
851 return CloseProcessBuffers(fds, alive);
852 }
853 } else if (fds[i].fd == err) {
854 if (!err_data.Read(err, avail)) {
855 return CloseProcessBuffers(fds, alive);
856 }
857 } else if (fds[i].fd == exit_event) {
858 if (avail == 8) {
859 intptr_t b =
860 TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8));
861 if (b != 8) {
862 return CloseProcessBuffers(fds, alive);
863 }
864 }
865 } else {
866 UNREACHABLE();
867 }
868 }
869 }
870 if (((fds[i].revents & POLLHUP) != 0) ||
871 (((fds[i].revents & POLLIN) != 0) && (avail == 0))) {
872 // Remove the pollfd from the list of pollfds.
873 close(fds[i].fd);
874 alive--;
875 if (i < alive) {
876 fds[i] = fds[alive];
877 }
878 // Process the same index again.
879 i--;
880 continue;
881 }
882 }
883 }
884
885 // All handles closed and all data read.
886 result->set_stdout_data(out_data.GetData());
887 result->set_stderr_data(err_data.GetData());
888 DEBUG_ASSERT(out_data.IsEmpty());
889 DEBUG_ASSERT(err_data.IsEmpty());
890
891 // Calculate the exit code.
892 intptr_t exit_code = exit_code_data.ints[0];
893 intptr_t negative = exit_code_data.ints[1];
894 if (negative != 0) {
895 exit_code = -exit_code;
896 }
897 result->set_exit_code(exit_code);
898
899 return true;
900#endif // defined(DART_HOST_OS_IOS)
901}
902
903static int SignalMap(intptr_t id) {
904 switch (static_cast<ProcessSignals>(id)) {
905 case kSighup:
906 return SIGHUP;
907 case kSigint:
908 return SIGINT;
909 case kSigquit:
910 return SIGQUIT;
911 case kSigill:
912 return SIGILL;
913 case kSigtrap:
914 return SIGTRAP;
915 case kSigabrt:
916 return SIGABRT;
917 case kSigbus:
918 return SIGBUS;
919 case kSigfpe:
920 return SIGFPE;
921 case kSigkill:
922 return SIGKILL;
923 case kSigusr1:
924 return SIGUSR1;
925 case kSigsegv:
926 return SIGSEGV;
927 case kSigusr2:
928 return SIGUSR2;
929 case kSigpipe:
930 return SIGPIPE;
931 case kSigalrm:
932 return SIGALRM;
933 case kSigterm:
934 return SIGTERM;
935 case kSigchld:
936 return SIGCHLD;
937 case kSigcont:
938 return SIGCONT;
939 case kSigstop:
940 return SIGSTOP;
941 case kSigtstp:
942 return SIGTSTP;
943 case kSigttin:
944 return SIGTTIN;
945 case kSigttou:
946 return SIGTTOU;
947 case kSigurg:
948 return SIGURG;
949 case kSigxcpu:
950 return SIGXCPU;
951 case kSigxfsz:
952 return SIGXFSZ;
953 case kSigvtalrm:
954 return SIGVTALRM;
955 case kSigprof:
956 return SIGPROF;
957 case kSigwinch:
958 return SIGWINCH;
959 case kSigpoll:
960 return -1;
961 case kSigsys:
962 return SIGSYS;
963 }
964 return -1;
965}
966
967bool Process::Kill(intptr_t id, int signal) {
968#if defined(DART_HOST_OS_IOS)
969 return false;
970#else // defined(DART_HOST_OS_IOS)
971 return (TEMP_FAILURE_RETRY(kill(id, SignalMap(signal))) != -1);
972#endif // defined(DART_HOST_OS_IOS)
973}
974
976#if !defined(DART_HOST_OS_IOS)
977 ExitCodeHandler::TerminateExitCodeThread();
978#endif // !defined(DART_HOST_OS_IOS)
979}
980
981intptr_t Process::CurrentProcessId() {
982 return static_cast<intptr_t>(getpid());
983}
984
985int64_t Process::CurrentRSS() {
986 struct mach_task_basic_info info;
987 mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
988 kern_return_t result =
989 task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
990 reinterpret_cast<task_info_t>(&info), &infoCount);
991 if (result != KERN_SUCCESS) {
992 return -1;
993 }
994 return info.resident_size;
995}
996
997int64_t Process::MaxRSS() {
998 struct rusage usage;
999 usage.ru_maxrss = 0;
1000 int r = getrusage(RUSAGE_SELF, &usage);
1001 if (r < 0) {
1002 return -1;
1003 }
1004 return usage.ru_maxrss;
1005}
1006
1007static Mutex* signal_mutex = nullptr;
1008static SignalInfo* signal_handlers = nullptr;
1009static constexpr int kSignalsCount = 7;
1010static const int kSignals[kSignalsCount] = {
1011 SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH,
1012 SIGQUIT // Allow VMService to listen on SIGQUIT.
1013};
1014
1016 close(fd_);
1017}
1018
1019static void SignalHandler(int signal) {
1020 MutexLocker lock(signal_mutex);
1021 const SignalInfo* handler = signal_handlers;
1022 while (handler != nullptr) {
1023 if (handler->signal() == signal) {
1024 int value = 0;
1025 VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1));
1026 }
1027 handler = handler->next();
1028 }
1029}
1030
1031intptr_t Process::SetSignalHandler(intptr_t signal) {
1032 signal = SignalMap(signal);
1033 if (signal == -1) {
1034 return -1;
1035 }
1036 bool found = false;
1037 for (int i = 0; i < kSignalsCount; i++) {
1038 if (kSignals[i] == signal) {
1039 found = true;
1040 break;
1041 }
1042 }
1043 if (!found) {
1044 return -1;
1045 }
1046 int fds[2];
1047 if (NO_RETRY_EXPECTED(pipe(fds)) != 0) {
1048 return -1;
1049 }
1050 if (!FDUtils::SetCloseOnExec(fds[0]) || !FDUtils::SetCloseOnExec(fds[1]) ||
1051 !FDUtils::SetNonBlocking(fds[0])) {
1052 close(fds[0]);
1053 close(fds[1]);
1054 return -1;
1055 }
1056 ThreadSignalBlocker blocker(kSignalsCount, kSignals);
1057 MutexLocker lock(signal_mutex);
1058 SignalInfo* handler = signal_handlers;
1059 bool listen = true;
1060 sa_handler_t oldact_handler = nullptr;
1061 while (handler != nullptr) {
1062 if (handler->signal() == signal) {
1063 oldact_handler = handler->oldact();
1064 listen = false;
1065 break;
1066 }
1067 handler = handler->next();
1068 }
1069 if (listen) {
1070 struct sigaction act = {};
1071 act.sa_handler = SignalHandler;
1072 sigemptyset(&act.sa_mask);
1073 for (int i = 0; i < kSignalsCount; i++) {
1074 sigaddset(&act.sa_mask, kSignals[i]);
1075 }
1076 struct sigaction oldact = {};
1077 intptr_t status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact));
1078 if (status < 0) {
1079 close(fds[0]);
1080 close(fds[1]);
1081 return -1;
1082 }
1083 oldact_handler = oldact.sa_handler;
1084 }
1085 signal_handlers =
1086 new SignalInfo(fds[1], signal, oldact_handler, signal_handlers);
1087 return fds[0];
1088}
1089
1090void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {
1091 signal = SignalMap(signal);
1092 if (signal == -1) {
1093 return;
1094 }
1095 ThreadSignalBlocker blocker(kSignalsCount, kSignals);
1096 MutexLocker lock(signal_mutex);
1097 SignalInfo* handler = signal_handlers;
1098 sa_handler_t oldact_handler = SIG_DFL;
1099 bool any_removed = false;
1100 bool any_remaining = false;
1101 while (handler != nullptr) {
1102 bool remove = false;
1103 if (handler->signal() == signal) {
1104 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1105 if (signal_handlers == handler) {
1106 signal_handlers = handler->next();
1107 }
1108 handler->Unlink();
1109 remove = true;
1110 oldact_handler = handler->oldact();
1111 any_removed = true;
1112 } else {
1113 any_remaining = true;
1114 }
1115 }
1116 SignalInfo* next = handler->next();
1117 if (remove) {
1118 delete handler;
1119 }
1120 handler = next;
1121 }
1122 if (any_removed && !any_remaining) {
1123 struct sigaction act = {};
1124 act.sa_handler = oldact_handler;
1125 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, nullptr));
1126 }
1127}
1128
1130 ThreadSignalBlocker blocker(kSignalsCount, kSignals);
1131 MutexLocker lock(signal_mutex);
1132 SignalInfo* handler = signal_handlers;
1133 sa_handler_t oldact_handler = SIG_DFL;
1134 bool any_remaining = false;
1135 intptr_t signal = -1;
1136 while (handler != nullptr) {
1137 bool remove = false;
1138 if (handler->fd() == fd) {
1139 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1140 if (signal_handlers == handler) {
1141 signal_handlers = handler->next();
1142 }
1143 handler->Unlink();
1144 remove = true;
1145 signal = handler->signal();
1146 } else {
1147 any_remaining = true;
1148 }
1149 }
1150 SignalInfo* next = handler->next();
1151 if (remove) {
1152 delete handler;
1153 }
1154 handler = next;
1155 }
1156 if ((signal != -1) && !any_remaining) {
1157 struct sigaction act = {};
1158 act.sa_handler = oldact_handler;
1159 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, nullptr));
1160 }
1161}
1162
1163#if !defined(DART_HOST_OS_IOS)
1164void ProcessInfoList::Init() {
1165 active_processes_ = nullptr;
1166 ASSERT(ProcessInfoList::mutex_ == nullptr);
1167 ProcessInfoList::mutex_ = new Mutex();
1168}
1169
1171 ASSERT(ProcessInfoList::mutex_ != nullptr);
1172 delete ProcessInfoList::mutex_;
1173 ProcessInfoList::mutex_ = nullptr;
1174}
1175
1176void ExitCodeHandler::Init() {
1177 running_ = false;
1178 process_count_ = 0;
1179 terminate_done_ = false;
1180 ASSERT(ExitCodeHandler::monitor_ == nullptr);
1181 ExitCodeHandler::monitor_ = new Monitor();
1182}
1183
1185 ASSERT(ExitCodeHandler::monitor_ != nullptr);
1186 delete ExitCodeHandler::monitor_;
1187 ExitCodeHandler::monitor_ = nullptr;
1188}
1189#endif // !defined(DART_HOST_OS_IOS)
1190
1191void Process::Init() {
1192#if !defined(DART_HOST_OS_IOS)
1195#endif // !defined(DART_HOST_OS_IOS)
1196
1197 ASSERT(signal_mutex == nullptr);
1198 signal_mutex = new Mutex();
1199 signal_handlers = nullptr;
1200
1201 ASSERT(Process::global_exit_code_mutex_ == nullptr);
1202 Process::global_exit_code_mutex_ = new Mutex();
1203}
1204
1205void Process::Cleanup() {
1207
1208 ASSERT(signal_mutex != nullptr);
1209 delete signal_mutex;
1210 signal_mutex = nullptr;
1211
1212 ASSERT(Process::global_exit_code_mutex_ != nullptr);
1213 delete Process::global_exit_code_mutex_;
1214 Process::global_exit_code_mutex_ = nullptr;
1215
1216#if !defined(DART_HOST_OS_IOS)
1219#endif // !defined(DART_HOST_OS_IOS)
1220}
1221
1222} // namespace bin
1223} // namespace dart
1224
1225#endif // defined(DART_HOST_OS_MACOS)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
static float next(float f)
static float prev(float f)
static bool read(SkStream *stream, void *buffer, size_t amount)
static const size_t kBufferSize
Definition: SkString.cpp:27
#define UNREACHABLE()
Definition: assert.h:248
#define DEBUG_ASSERT(cond)
Definition: assert.h:321
static char * StrError(int err, char *buffer, size_t bufsize)
Definition: utils_android.h:40
static char * ScopedCString(intptr_t length)
Definition: dartutils.h:224
static intptr_t AvailableBytes(intptr_t fd)
static bool SetCloseOnExec(intptr_t fd)
static bool SetNonBlocking(intptr_t fd)
static ssize_t ReadFromBlocking(int fd, void *buffer, size_t count)
static ssize_t WriteToBlocking(int fd, const void *buffer, size_t count)
static constexpr int64_t kNoTimeout
Definition: thread.h:79
static bool ModeIsAttached(ProcessStartMode mode)
Definition: process.cc:62
static void ClearSignalHandler(intptr_t signal, Dart_Port port)
static void ClearSignalHandlerByFd(intptr_t fd, Dart_Port port)
static void Cleanup()
static bool ModeHasStdio(ProcessStartMode mode)
Definition: process.cc:66
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 void ClearAllSignalHandlers()
Definition: process.cc:70
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)
#define ILLEGAL_PORT
Definition: dart_api.h:1535
int64_t Dart_Port
Definition: dart_api.h:1525
#define ASSERT(E)
static bool b
#define FATAL(error)
uint8_t value
GAsyncResult * result
void Init()
Win32Message message
void(* sa_handler_t)(int)
Definition: process.h:173
ProcessStartMode
Definition: process.h:81
@ kDetached
Definition: process.h:84
@ kInheritStdio
Definition: process.h:83
@ kNormal
Definition: process.h:82
@ kDetachedWithStdio
Definition: process.h:85
ProcessSignals
Definition: process.h:47
@ kSigtrap
Definition: process.h:52
@ kSigtstp
Definition: process.h:66
@ kSigstop
Definition: process.h:65
@ kSigbus
Definition: process.h:54
@ kSighup
Definition: process.h:48
@ kSigpoll
Definition: process.h:75
@ kSigttin
Definition: process.h:67
@ kSigxcpu
Definition: process.h:70
@ kSigkill
Definition: process.h:56
@ kSigquit
Definition: process.h:50
@ kSigusr1
Definition: process.h:57
@ kSigvtalrm
Definition: process.h:72
@ kSigabrt
Definition: process.h:53
@ kSigcont
Definition: process.h:64
@ kSigterm
Definition: process.h:62
@ kSigusr2
Definition: process.h:59
@ kSigfpe
Definition: process.h:55
@ kSigxfsz
Definition: process.h:71
@ kSigchld
Definition: process.h:63
@ kSigsegv
Definition: process.h:58
@ kSigill
Definition: process.h:51
@ kSigwinch
Definition: process.h:74
@ kSigprof
Definition: process.h:73
@ kSigttou
Definition: process.h:68
@ kSigalrm
Definition: process.h:61
@ kSigint
Definition: process.h:49
@ kSigsys
Definition: process.h:76
@ kSigurg
Definition: process.h:69
@ kSigpipe
Definition: process.h:60
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
static void USE(T &&)
Definition: globals.h:618
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
static void SignalHandler(int signal)
Definition: backtrace.cc:101
def remove(*paths)
#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)
#define VOID_TEMP_FAILURE_RETRY(expression)
#define VOID_NO_RETRY_EXPECTED(expression)
#define TEMP_FAILURE_RETRY(expression)
static void usage(char *argv0)
void write(SkWStream *wStream, const T &text)
Definition: skqp.cpp:188
const uintptr_t id