Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
process_linux.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_LINUX) || defined(DART_HOST_OS_ANDROID)
7
8#include "bin/process.h"
9
10#include <errno.h> // NOLINT
11#include <fcntl.h> // NOLINT
12#include <poll.h> // NOLINT
13#include <stdio.h> // NOLINT
14#include <stdlib.h> // NOLINT
15#include <string.h> // NOLINT
16#include <sys/resource.h> // NOLINT
17#include <sys/wait.h> // NOLINT
18#include <unistd.h> // NOLINT
19
20#include "bin/dartutils.h"
21#include "bin/directory.h"
22#include "bin/fdutils.h"
23#include "bin/file.h"
24#include "bin/lockers.h"
26#include "bin/thread.h"
27#include "platform/syslog.h"
28
30#include "platform/utils.h"
31
32extern char** environ;
33
34namespace dart {
35namespace bin {
36
37int Process::global_exit_code_ = 0;
38Mutex* Process::global_exit_code_mutex_ = nullptr;
39Process::ExitHook Process::exit_hook_ = nullptr;
40
41// ProcessInfo is used to map a process id to the file descriptor for
42// the pipe used to communicate the exit code of the process to Dart.
43// ProcessInfo objects are kept in the static singly-linked
44// ProcessInfoList.
45class ProcessInfo {
46 public:
47 ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) {}
48 ~ProcessInfo() {
49 int closed = close(fd_);
50 if (closed != 0) {
51 FATAL("Failed to close process exit code pipe");
52 }
53 }
54 pid_t pid() { return pid_; }
55 intptr_t fd() { return fd_; }
56 ProcessInfo* next() { return next_; }
57 void set_next(ProcessInfo* info) { next_ = info; }
58
59 private:
60 pid_t pid_;
61 intptr_t fd_;
62 ProcessInfo* next_;
63
64 DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
65};
66
67// Singly-linked list of ProcessInfo objects for all active processes
68// started from Dart.
69class ProcessInfoList {
70 public:
71 static void Init();
72 static void Cleanup();
73
74 static void AddProcess(pid_t pid, intptr_t fd) {
75 MutexLocker locker(mutex_);
76 ProcessInfo* info = new ProcessInfo(pid, fd);
77 info->set_next(active_processes_);
78 active_processes_ = info;
79 }
80
81 static intptr_t LookupProcessExitFd(pid_t pid) {
82 MutexLocker locker(mutex_);
83 ProcessInfo* current = active_processes_;
84 while (current != nullptr) {
85 if (current->pid() == pid) {
86 return current->fd();
87 }
88 current = current->next();
89 }
90 return 0;
91 }
92
93 static void RemoveProcess(pid_t pid) {
94 MutexLocker locker(mutex_);
95 ProcessInfo* prev = nullptr;
96 ProcessInfo* current = active_processes_;
97 while (current != nullptr) {
98 if (current->pid() == pid) {
99 if (prev == nullptr) {
100 active_processes_ = current->next();
101 } else {
102 prev->set_next(current->next());
103 }
104 delete current;
105 return;
106 }
107 prev = current;
108 current = current->next();
109 }
110 }
111
112 private:
113 // Linked list of ProcessInfo objects for all active processes
114 // started from Dart code.
115 static ProcessInfo* active_processes_;
116 // Mutex protecting all accesses to the linked list of active
117 // processes.
118 static Mutex* mutex_;
119
121 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList);
122};
123
124ProcessInfo* ProcessInfoList::active_processes_ = nullptr;
125Mutex* ProcessInfoList::mutex_ = nullptr;
126
127// The exit code handler sets up a separate thread which waits for child
128// processes to terminate. That separate thread can then get the exit code from
129// processes that have exited and communicate it to Dart through the
130// event loop.
131class ExitCodeHandler {
132 public:
133 static void Init();
134 static void Cleanup();
135
136 // Notify the ExitCodeHandler that another process exists.
137 static void ProcessStarted() {
138 // Multiple isolates could be starting processes at the same
139 // time. Make sure that only one ExitCodeHandler thread exists.
140 MonitorLocker locker(monitor_);
141 process_count_++;
142
143 monitor_->Notify();
144
145 if (running_) {
146 return;
147 }
148
149 // Start thread that handles process exits when wait returns.
150 int result =
151 Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
152 if (result != 0) {
153 FATAL("Failed to start exit code handler worker thread %d", result);
154 }
155
156 running_ = true;
157 }
158
159 static void TerminateExitCodeThread() {
160 MonitorLocker locker(monitor_);
161
162 if (!running_) {
163 return;
164 }
165
166 // Set terminate_done_ to false, so we can use it as a guard for our
167 // monitor.
168 running_ = false;
169
170 // Wake up the [ExitCodeHandler] thread which is blocked on `wait()` (see
171 // [ExitCodeHandlerEntry]).
172 if (TEMP_FAILURE_RETRY(fork()) == 0) {
173 // Avoid calling any atexit callbacks to prevent deadlocks.
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(Namespace* namespc,
256 const char* path,
257 char* arguments[],
258 intptr_t arguments_length,
259 const char* working_directory,
260 char* environment[],
261 intptr_t environment_length,
262 ProcessStartMode mode,
263 intptr_t* in,
264 intptr_t* out,
265 intptr_t* err,
266 intptr_t* id,
267 intptr_t* exit_event,
268 char** os_error_message)
269 : namespc_(namespc),
270 path_(path),
271 working_directory_(working_directory),
272 mode_(mode),
273 in_(in),
274 out_(out),
275 err_(err),
276 id_(id),
277 exit_event_(exit_event),
278 os_error_message_(os_error_message) {
279 read_in_[0] = -1;
280 read_in_[1] = -1;
281 read_err_[0] = -1;
282 read_err_[1] = -1;
283 write_out_[0] = -1;
284 write_out_[1] = -1;
285 exec_control_[0] = -1;
286 exec_control_[1] = -1;
287
288 program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
289 (arguments_length + 2) * sizeof(*program_arguments_)));
290 program_arguments_[0] = const_cast<char*>(path_);
291 for (int i = 0; i < arguments_length; i++) {
292 program_arguments_[i + 1] = arguments[i];
293 }
294 program_arguments_[arguments_length + 1] = nullptr;
295
296 program_environment_ = nullptr;
297 if (environment != nullptr) {
298 program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
299 (environment_length + 1) * sizeof(*program_environment_)));
300 for (int i = 0; i < environment_length; i++) {
301 program_environment_[i] = environment[i];
302 }
303 program_environment_[environment_length] = nullptr;
304 }
305 }
306
307 int Start() {
308 // Create pipes required.
309 int err = CreatePipes();
310 if (err != 0) {
311 return err;
312 }
313
314 // Fork to create the new process.
315 pid_t pid = TEMP_FAILURE_RETRY(fork());
316 if (pid < 0) {
317 // Failed to fork.
318 return CleanupAndReturnError();
319 } else if (pid == 0) {
320 // This runs in the new process.
321 NewProcess();
322 }
323
324 // This runs in the original process.
325
326 // If the child process is not started in detached mode, be sure to
327 // listen for exit-codes, now that we have a non detached child process
328 // and also Register this child process.
329 if (Process::ModeIsAttached(mode_)) {
330 ExitCodeHandler::ProcessStarted();
331 err = RegisterProcess(pid);
332 if (err != 0) {
333 return err;
334 }
335 }
336
337 // Notify child process to start. This is done to delay the call to exec
338 // until the process is registered above, and we are ready to receive the
339 // exit code.
340 char msg = '1';
341 int bytes_written =
342 FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg));
343 if (bytes_written != sizeof(msg)) {
344 return CleanupAndReturnError();
345 }
346
347 // Read the result of executing the child process.
348 close(exec_control_[1]);
349 exec_control_[1] = -1;
350 if (Process::ModeIsAttached(mode_)) {
351 err = ReadExecResult();
352 } else {
353 err = ReadDetachedExecResult(&pid);
354 }
355 close(exec_control_[0]);
356 exec_control_[0] = -1;
357
358 // Return error code if any failures.
359 if (err != 0) {
360 if (Process::ModeIsAttached(mode_)) {
361 // Since exec() failed, we're not interested in the exit code.
362 // We close the reading side of the exit code pipe here.
363 // GetProcessExitCodes will get a broken pipe error when it
364 // tries to write to the writing side of the pipe and it will
365 // ignore the error.
366 close(*exit_event_);
367 *exit_event_ = -1;
368 }
369 CloseAllPipes();
370 return err;
371 }
372
373 if (Process::ModeHasStdio(mode_)) {
374 // Connect stdio, stdout and stderr.
375 FDUtils::SetNonBlocking(read_in_[0]);
376 *in_ = read_in_[0];
377 close(read_in_[1]);
378 FDUtils::SetNonBlocking(write_out_[1]);
379 *out_ = write_out_[1];
380 close(write_out_[0]);
381 FDUtils::SetNonBlocking(read_err_[0]);
382 *err_ = read_err_[0];
383 close(read_err_[1]);
384 } else {
385 // Close all fds.
386 close(read_in_[0]);
387 close(read_in_[1]);
388 ASSERT(write_out_[0] == -1);
389 ASSERT(write_out_[1] == -1);
390 ASSERT(read_err_[0] == -1);
391 ASSERT(read_err_[1] == -1);
392 }
393 ASSERT(exec_control_[0] == -1);
394 ASSERT(exec_control_[1] == -1);
395
396 *id_ = pid;
397 return 0;
398 }
399
400 private:
401 static constexpr int kErrorBufferSize = 1024;
402
403 int CreatePipes() {
404 int result;
405 result = TEMP_FAILURE_RETRY(pipe2(exec_control_, O_CLOEXEC));
406 if (result < 0) {
407 return CleanupAndReturnError();
408 }
409
410 // For a detached process the pipe to connect stdout is still used for
411 // signaling when to do the first fork.
412 result = TEMP_FAILURE_RETRY(pipe2(read_in_, O_CLOEXEC));
413 if (result < 0) {
414 return CleanupAndReturnError();
415 }
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(pipe2(read_err_, O_CLOEXEC));
420 if (result < 0) {
421 return CleanupAndReturnError();
422 }
423
424 result = TEMP_FAILURE_RETRY(pipe2(write_out_, O_CLOEXEC));
425 if (result < 0) {
426 return CleanupAndReturnError();
427 }
428 }
429
430 return 0;
431 }
432
433 void NewProcess() {
434 // Wait for parent process before setting up the child process.
435 char msg;
436 int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg));
437 if (bytes_read != sizeof(msg)) {
438 perror("Failed receiving notification message");
439 _exit(1);
440 }
441 if (Process::ModeIsAttached(mode_)) {
442 ExecProcess();
443 } else {
444 ExecDetachedProcess();
445 }
446 }
447
448 // Tries to find path_ relative to the current namespace unless it should be
449 // searched in the PATH.
450 // The path that should be passed to exec is returned in realpath.
451 // Returns true on success, and false if there was an error that should
452 // be reported to the parent.
453 bool FindPathInNamespace(char* realpath, intptr_t realpath_size) {
454 // Perform a PATH search if there's no slash in the path.
455 if (Namespace::IsDefault(namespc_) || strchr(path_, '/') == nullptr) {
456 // TODO(zra): If there is a non-default namespace, the entries in PATH
457 // should be treated as relative to the namespace.
458 strncpy(realpath, path_, realpath_size);
459 realpath[realpath_size - 1] = '\0';
460 return true;
461 }
462 NamespaceScope ns(namespc_, path_);
463 const int fd =
464 TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC));
465 if (fd == -1) {
466 return false;
467 }
468 char procpath[PATH_MAX];
469 snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd);
470 const intptr_t length =
471 TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size));
472 if (length < 0) {
473 FDUtils::SaveErrorAndClose(fd);
474 return false;
475 }
476 realpath[length] = '\0';
477 FDUtils::SaveErrorAndClose(fd);
478 return true;
479 }
480
481 void ExecProcess() {
482 if (mode_ == kNormal) {
483 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
484 ReportChildError();
485 }
486
487 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
488 ReportChildError();
489 }
490
491 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
492 ReportChildError();
493 }
494 } else {
495 ASSERT(mode_ == kInheritStdio);
496 }
497
498 if (working_directory_ != nullptr &&
499 !Directory::SetCurrent(namespc_, working_directory_)) {
500 ReportChildError();
501 }
502
503 if (program_environment_ != nullptr) {
504 environ = program_environment_;
505 }
506
507 char realpath[PATH_MAX];
508 if (!FindPathInNamespace(realpath, PATH_MAX)) {
509 ReportChildError();
510 }
511 // TODO(dart:io) Test for the existence of execveat, and use it instead.
512 execvp(realpath, const_cast<char* const*>(program_arguments_));
513 ReportChildError();
514 }
515
516 void ExecDetachedProcess() {
517 if (mode_ == kDetached) {
518 ASSERT(write_out_[0] == -1);
519 ASSERT(write_out_[1] == -1);
520 ASSERT(read_err_[0] == -1);
521 ASSERT(read_err_[1] == -1);
522 // For a detached process the pipe to connect stdout is only used for
523 // signaling when to do the first fork.
524 close(read_in_[0]);
525 read_in_[0] = -1;
526 close(read_in_[1]);
527 read_in_[1] = -1;
528 } else {
529 // Don't close any fds if keeping stdio open to the detached process.
530 ASSERT(mode_ == kDetachedWithStdio);
531 }
532 // Fork once more to start a new session.
533 pid_t pid = TEMP_FAILURE_RETRY(fork());
534 if (pid < 0) {
535 ReportChildError();
536 } else if (pid == 0) {
537 // Start a new session.
538 if (TEMP_FAILURE_RETRY(setsid()) == -1) {
539 ReportChildError();
540 } else {
541 // Do a final fork to not be the session leader.
542 pid = TEMP_FAILURE_RETRY(fork());
543 if (pid < 0) {
544 ReportChildError();
545 } else if (pid == 0) {
546 if (mode_ == kDetached) {
547 SetupDetached();
548 } else {
549 SetupDetachedWithStdio();
550 }
551
552 if ((working_directory_ != nullptr) &&
553 !Directory::SetCurrent(namespc_, working_directory_)) {
554 ReportChildError();
555 }
556 if (program_environment_ != nullptr) {
557 environ = program_environment_;
558 }
559
560 // Report the final PID and do the exec.
561 ReportPid(getpid()); // getpid cannot fail.
562 char realpath[PATH_MAX];
563 if (!FindPathInNamespace(realpath, PATH_MAX)) {
564 ReportChildError();
565 }
566 // TODO(dart:io) Test for the existence of execveat, and use it
567 // instead.
568 execvp(realpath, const_cast<char* const*>(program_arguments_));
569 ReportChildError();
570 } else {
571 // Exit the intermediate process. Avoid calling any atexit callbacks
572 // to avoid potential issues (e.g. deadlocks).
573 _exit(0);
574 }
575 }
576 } else {
577 // Exit the intermediate process. Avoid calling any atexit callbacks
578 // to avoid potential issues (e.g. deadlocks).
579 _exit(0);
580 }
581 }
582
583 int RegisterProcess(pid_t pid) {
584 int result;
585 int event_fds[2];
586 result = TEMP_FAILURE_RETRY(pipe2(event_fds, O_CLOEXEC));
587 if (result < 0) {
588 return CleanupAndReturnError();
589 }
590
591 ProcessInfoList::AddProcess(pid, event_fds[1]);
592 *exit_event_ = event_fds[0];
593 FDUtils::SetNonBlocking(event_fds[0]);
594 return 0;
595 }
596
597 int ReadExecResult() {
598 int child_errno;
599 int bytes_read = -1;
600 // Read exec result from child. If no data is returned the exec was
601 // successful and the exec call closed the pipe. Otherwise the errno
602 // is written to the pipe.
603 bytes_read = FDUtils::ReadFromBlocking(exec_control_[0], &child_errno,
604 sizeof(child_errno));
605 if (bytes_read == sizeof(child_errno)) {
606 ReadChildError();
607 return child_errno;
608 } else if (bytes_read == -1) {
609 return errno;
610 }
611 return 0;
612 }
613
614 int ReadDetachedExecResult(pid_t* pid) {
615 int child_errno;
616 int bytes_read = -1;
617 // Read exec result from child. If only pid data is returned the exec was
618 // successful and the exec call closed the pipe. Otherwise the errno
619 // is written to the pipe as well.
620 int result[2];
621 bytes_read =
622 FDUtils::ReadFromBlocking(exec_control_[0], result, sizeof(result));
623 if (bytes_read == sizeof(int)) {
624 *pid = result[0];
625 } else if (bytes_read == 2 * sizeof(int)) {
626 *pid = result[0];
627 child_errno = result[1];
628 ReadChildError();
629 return child_errno;
630 } else if (bytes_read == -1) {
631 return errno;
632 }
633 return 0;
634 }
635
636 void SetupDetached() {
637 ASSERT(mode_ == kDetached);
638
639 // Close all open file descriptors except for exec_control_[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]) {
646 close(fd);
647 }
648 }
649
650 // Re-open stdin, stdout and stderr and connect them to /dev/null.
651 // The loop above should already have closed all of them, so
652 // creating new file descriptors should start at STDIN_FILENO.
653 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
654 if (fd != STDIN_FILENO) {
655 ReportChildError();
656 }
657 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
658 STDOUT_FILENO) {
659 ReportChildError();
660 }
661 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
662 STDERR_FILENO) {
663 ReportChildError();
664 }
665 }
666
667 void SetupDetachedWithStdio() {
668 // Close all open file descriptors except for
669 // exec_control_[1], write_out_[0], read_in_[1] and
670 // read_err_[1].
671 int max_fds = sysconf(_SC_OPEN_MAX);
672 if (max_fds == -1) {
673 max_fds = _POSIX_OPEN_MAX;
674 }
675 for (int fd = 0; fd < max_fds; fd++) {
676 if ((fd != exec_control_[1]) && (fd != write_out_[0]) &&
677 (fd != read_in_[1]) && (fd != read_err_[1])) {
678 close(fd);
679 }
680 }
681
682 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
683 ReportChildError();
684 }
685 close(write_out_[0]);
686
687 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
688 ReportChildError();
689 }
690 close(read_in_[1]);
691
692 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
693 ReportChildError();
694 }
695 close(read_err_[1]);
696 }
697
698 int CleanupAndReturnError() {
699 int actual_errno = errno;
700 // If CleanupAndReturnError is called without an actual errno make
701 // sure to return an error anyway.
702 if (actual_errno == 0) {
703 actual_errno = EPERM;
704 }
705 SetChildOsErrorMessage();
706 CloseAllPipes();
707 return actual_errno;
708 }
709
710 void SetChildOsErrorMessage() {
711 char* error_message = DartUtils::ScopedCString(kErrorBufferSize);
712 Utils::StrError(errno, error_message, kErrorBufferSize);
713 *os_error_message_ = error_message;
714 }
715
716 void ReportChildError() {
717 // In the case of failure in the child process write the errno and
718 // the OS error message to the exec control pipe and exit.
719 int child_errno = errno;
720 char error_buf[kErrorBufferSize];
721 char* os_error_message =
722 Utils::StrError(errno, error_buf, kErrorBufferSize);
723 int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno,
724 sizeof(child_errno));
725 if (bytes_written == sizeof(child_errno)) {
726 FDUtils::WriteToBlocking(exec_control_[1], os_error_message,
727 strlen(os_error_message) + 1);
728 }
729 close(exec_control_[1]);
730
731 // We avoid running through registered atexit() handlers because that is
732 // unnecessary work. It can also cause deadlocks on exit in the forked
733 // process.
734 _exit(1);
735 }
736
737 void ReportPid(int pid) {
738 // In the case of starting a detached process the actual pid of that process
739 // is communicated using the exec control pipe.
740 int bytes_written =
741 FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid));
742 ASSERT(bytes_written == sizeof(int));
743 USE(bytes_written);
744 }
745
746 void ReadChildError() {
747 char* message = DartUtils::ScopedCString(kErrorBufferSize);
748 if (message != nullptr) {
749 FDUtils::ReadFromBlocking(exec_control_[0], message, kErrorBufferSize);
750 message[kErrorBufferSize - 1] = '\0';
751 *os_error_message_ = message;
752 } else {
753 // Could not get error message. It will be nullptr.
754 ASSERT(*os_error_message_ == nullptr);
755 }
756 }
757
758 void ClosePipe(int* fds) {
759 for (int i = 0; i < 2; i++) {
760 if (fds[i] != -1) {
761 close(fds[i]);
762 fds[i] = -1;
763 }
764 }
765 }
766
767 void CloseAllPipes() {
768 ClosePipe(exec_control_);
769 ClosePipe(read_in_);
770 ClosePipe(read_err_);
771 ClosePipe(write_out_);
772 }
773
774 int read_in_[2]; // Pipe for stdout to child process.
775 int read_err_[2]; // Pipe for stderr to child process.
776 int write_out_[2]; // Pipe for stdin to child process.
777 int exec_control_[2]; // Pipe to get the result from exec.
778
779 char** program_arguments_;
780 char** program_environment_;
781
782 Namespace* namespc_;
783 const char* path_;
784 const char* working_directory_;
785 ProcessStartMode mode_;
786 intptr_t* in_;
787 intptr_t* out_;
788 intptr_t* err_;
789 intptr_t* id_;
790 intptr_t* exit_event_;
791 char** os_error_message_;
792
794 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
795};
796
797int Process::Start(Namespace* namespc,
798 const char* path,
799 char* arguments[],
800 intptr_t arguments_length,
801 const char* working_directory,
802 char* environment[],
803 intptr_t environment_length,
804 ProcessStartMode mode,
805 intptr_t* in,
806 intptr_t* out,
807 intptr_t* err,
808 intptr_t* id,
809 intptr_t* exit_event,
810 char** os_error_message) {
811 ProcessStarter starter(namespc, path, arguments, arguments_length,
812 working_directory, environment, environment_length,
813 mode, in, out, err, id, exit_event, os_error_message);
814 return starter.Start();
815}
816
817static bool CloseProcessBuffers(struct pollfd* fds, int alive) {
818 int e = errno;
819 for (int i = 0; i < alive; i++) {
820 close(fds[i].fd);
821 }
822 errno = e;
823 return false;
824}
825
826bool Process::Wait(intptr_t pid,
827 intptr_t in,
828 intptr_t out,
829 intptr_t err,
830 intptr_t exit_event,
831 ProcessResult* result) {
832 // Close input to the process right away.
833 close(in);
834
835 // There is no return from this function using Dart_PropagateError
836 // as memory used by the buffer lists is freed through their
837 // destructors.
838 BufferList out_data;
839 BufferList err_data;
840 union {
841 uint8_t bytes[8];
842 int32_t ints[2];
843 } exit_code_data;
844
845 struct pollfd fds[3];
846 fds[0].fd = out;
847 fds[1].fd = err;
848 fds[2].fd = exit_event;
849
850 for (int i = 0; i < 3; i++) {
851 fds[i].events = POLLIN;
852 }
853
854 int alive = 3;
855 while (alive > 0) {
856 // Blocking call waiting for events from the child process.
857 if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) {
858 return CloseProcessBuffers(fds, alive);
859 }
860
861 // Process incoming data.
862 for (int i = 0; i < alive; i++) {
863 if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) {
864 return CloseProcessBuffers(fds, alive);
865 }
866 if ((fds[i].revents & POLLIN) != 0) {
867 intptr_t avail = FDUtils::AvailableBytes(fds[i].fd);
868 if (fds[i].fd == out) {
869 if (!out_data.Read(out, avail)) {
870 return CloseProcessBuffers(fds, alive);
871 }
872 } else if (fds[i].fd == err) {
873 if (!err_data.Read(err, avail)) {
874 return CloseProcessBuffers(fds, alive);
875 }
876 } else if (fds[i].fd == exit_event) {
877 if (avail == 8) {
878 intptr_t b =
879 TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8));
880 if (b != 8) {
881 return CloseProcessBuffers(fds, alive);
882 }
883 }
884 } else {
885 UNREACHABLE();
886 }
887 }
888 if ((fds[i].revents & POLLHUP) != 0) {
889 // Remove the pollfd from the list of pollfds.
890 close(fds[i].fd);
891 alive--;
892 if (i < alive) {
893 fds[i] = fds[alive];
894 }
895 // Process the same index again.
896 i--;
897 continue;
898 }
899 }
900 }
901
902 // All handles closed and all data read.
903 result->set_stdout_data(out_data.GetData());
904 result->set_stderr_data(err_data.GetData());
905 DEBUG_ASSERT(out_data.IsEmpty());
906 DEBUG_ASSERT(err_data.IsEmpty());
907
908 // Calculate the exit code.
909 intptr_t exit_code = exit_code_data.ints[0];
910 intptr_t negative = exit_code_data.ints[1];
911 if (negative != 0) {
912 exit_code = -exit_code;
913 }
914 result->set_exit_code(exit_code);
915
916 return true;
917}
918
919bool Process::Kill(intptr_t id, int signal) {
920 return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1);
921}
922
924 ExitCodeHandler::TerminateExitCodeThread();
925}
926
927intptr_t Process::CurrentProcessId() {
928 return static_cast<intptr_t>(getpid());
929}
930
931static void SaveErrorAndClose(FILE* file) {
932 int actual_errno = errno;
933 fclose(file);
934 errno = actual_errno;
935}
936
937int64_t Process::CurrentRSS() {
938 // The second value in /proc/self/statm is the current RSS in pages.
939 // It is not possible to use getrusage() because the interested fields are not
940 // implemented by the linux kernel.
941 FILE* statm = fopen("/proc/self/statm", "r");
942 if (statm == nullptr) {
943 return -1;
944 }
945 int64_t current_rss_pages = 0;
946 int matches = fscanf(statm, "%*s%" Pd64 "", &current_rss_pages);
947 if (matches != 1) {
948 SaveErrorAndClose(statm);
949 return -1;
950 }
951 fclose(statm);
952 return current_rss_pages * getpagesize();
953}
954
955int64_t Process::MaxRSS() {
956 struct rusage usage;
957 usage.ru_maxrss = 0;
958 int r = getrusage(RUSAGE_SELF, &usage);
959 if (r < 0) {
960 return -1;
961 }
962 return usage.ru_maxrss * KB;
963}
964
965static Mutex* signal_mutex = nullptr;
966static SignalInfo* signal_handlers = nullptr;
967static constexpr int kSignalsCount = 7;
968static const int kSignals[kSignalsCount] = {
969 SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH,
970 SIGQUIT // Allow VMService to listen on SIGQUIT.
971};
972
974 close(fd_);
975}
976
977static void SignalHandler(int signal) {
978 MutexLocker lock(signal_mutex);
979 const SignalInfo* handler = signal_handlers;
980 while (handler != nullptr) {
981 if (handler->signal() == signal) {
982 int value = 0;
983 VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1));
984 }
985 handler = handler->next();
986 }
987}
988
989intptr_t Process::SetSignalHandler(intptr_t signal) {
990 bool found = false;
991 for (int i = 0; i < kSignalsCount; i++) {
992 if (kSignals[i] == signal) {
993 found = true;
994 break;
995 }
996 }
997 if (!found) {
998 return -1;
999 }
1000 int fds[2];
1001 if (NO_RETRY_EXPECTED(pipe2(fds, O_CLOEXEC)) != 0) {
1002 return -1;
1003 }
1004 ThreadSignalBlocker blocker(kSignalsCount, kSignals);
1005 MutexLocker lock(signal_mutex);
1006 SignalInfo* handler = signal_handlers;
1007 bool listen = true;
1008 sa_handler_t oldact_handler = nullptr;
1009 while (handler != nullptr) {
1010 if (handler->signal() == signal) {
1011 oldact_handler = handler->oldact();
1012 listen = false;
1013 break;
1014 }
1015 handler = handler->next();
1016 }
1017 if (listen) {
1018 struct sigaction act = {};
1019 act.sa_handler = SignalHandler;
1020 sigemptyset(&act.sa_mask);
1021 for (int i = 0; i < kSignalsCount; i++) {
1022 sigaddset(&act.sa_mask, kSignals[i]);
1023 }
1024 struct sigaction oldact = {};
1025 int status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact));
1026 if (status < 0) {
1027 int err = errno;
1028 close(fds[0]);
1029 close(fds[1]);
1030 errno = err;
1031 return -1;
1032 }
1033 oldact_handler = oldact.sa_handler;
1034 }
1035 signal_handlers =
1036 new SignalInfo(fds[1], signal, oldact_handler, signal_handlers);
1037 return fds[0];
1038}
1039
1040void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {
1041 ThreadSignalBlocker blocker(kSignalsCount, kSignals);
1042 MutexLocker lock(signal_mutex);
1043 SignalInfo* handler = signal_handlers;
1044 sa_handler_t oldact_handler = SIG_DFL;
1045 bool any_removed = false;
1046 bool any_remaining = false;
1047 while (handler != nullptr) {
1048 bool remove = false;
1049 if (handler->signal() == signal) {
1050 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1051 if (signal_handlers == handler) {
1052 signal_handlers = handler->next();
1053 }
1054 handler->Unlink();
1055 remove = true;
1056 oldact_handler = handler->oldact();
1057 any_removed = true;
1058 } else {
1059 any_remaining = true;
1060 }
1061 }
1062 SignalInfo* next = handler->next();
1063 if (remove) {
1064 delete handler;
1065 }
1066 handler = next;
1067 }
1068 if (any_removed && !any_remaining) {
1069 struct sigaction act = {};
1070 act.sa_handler = oldact_handler;
1071 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, nullptr));
1072 }
1073}
1074
1075void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {
1076 ThreadSignalBlocker blocker(kSignalsCount, kSignals);
1077 MutexLocker lock(signal_mutex);
1078 SignalInfo* handler = signal_handlers;
1079 sa_handler_t oldact_handler = SIG_DFL;
1080 bool any_remaining = false;
1081 intptr_t signal = -1;
1082 while (handler != nullptr) {
1083 bool remove = false;
1084 if (handler->fd() == fd) {
1085 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1086 if (signal_handlers == handler) {
1087 signal_handlers = handler->next();
1088 }
1089 handler->Unlink();
1090 remove = true;
1091 signal = handler->signal();
1092 } else {
1093 any_remaining = true;
1094 }
1095 }
1096 SignalInfo* next = handler->next();
1097 if (remove) {
1098 delete handler;
1099 }
1100 handler = next;
1101 }
1102 if ((signal != -1) && !any_remaining) {
1103 struct sigaction act = {};
1104 act.sa_handler = oldact_handler;
1105 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, nullptr));
1106 }
1107}
1108
1109void ProcessInfoList::Init() {
1110 active_processes_ = nullptr;
1111 ASSERT(ProcessInfoList::mutex_ == nullptr);
1112 ProcessInfoList::mutex_ = new Mutex();
1113}
1114
1115void ProcessInfoList::Cleanup() {
1116 ASSERT(ProcessInfoList::mutex_ != nullptr);
1117 delete ProcessInfoList::mutex_;
1118 ProcessInfoList::mutex_ = nullptr;
1119}
1120
1121void ExitCodeHandler::Init() {
1122 running_ = false;
1123 process_count_ = 0;
1124 terminate_done_ = false;
1125 ASSERT(ExitCodeHandler::monitor_ == nullptr);
1126 ExitCodeHandler::monitor_ = new Monitor();
1127}
1128
1129void ExitCodeHandler::Cleanup() {
1130 ASSERT(ExitCodeHandler::monitor_ != nullptr);
1131 delete ExitCodeHandler::monitor_;
1132 ExitCodeHandler::monitor_ = nullptr;
1133}
1134
1135void Process::Init() {
1136 ExitCodeHandler::Init();
1137 ProcessInfoList::Init();
1138
1139 ASSERT(signal_mutex == nullptr);
1140 signal_mutex = new Mutex();
1141 signal_handlers = nullptr;
1142
1143 ASSERT(Process::global_exit_code_mutex_ == nullptr);
1144 Process::global_exit_code_mutex_ = new Mutex();
1145}
1146
1147void Process::Cleanup() {
1149
1150 ASSERT(signal_mutex != nullptr);
1151 delete signal_mutex;
1152 signal_mutex = nullptr;
1153
1154 ASSERT(Process::global_exit_code_mutex_ != nullptr);
1155 delete Process::global_exit_code_mutex_;
1156 Process::global_exit_code_mutex_ = nullptr;
1157
1158 ProcessInfoList::Cleanup();
1159 ExitCodeHandler::Cleanup();
1160}
1161
1162} // namespace bin
1163} // namespace dart
1164
1165#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
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)
#define UNREACHABLE()
Definition assert.h:248
#define DEBUG_ASSERT(cond)
Definition assert.h:321
static intptr_t AvailableBytes(intptr_t fd)
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 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)
#define ILLEGAL_PORT
Definition dart_api.h:1530
int64_t Dart_Port
Definition dart_api.h:1524
DART_EXPORT uint8_t * Dart_ScopeAllocate(intptr_t size)
@ kNormal
Default priority level.
Definition embedder.h:260
#define ASSERT(E)
static bool b
#define FATAL(error)
uint8_t value
GAsyncResult * result
void Init()
size_t length
Win32Message message
void(* sa_handler_t)(int)
Definition process.h:173
ProcessStartMode
Definition process.h:81
static dart::SimpleHashMap * environment
constexpr intptr_t KB
Definition globals.h:528
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
#define Pd64
Definition globals.h:416
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName)
Definition globals.h:593
#define PATH_MAX
Definition globals.h:708
#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