blob: 0578d0c7973d00b68334bdb1dd3409506260860c [file]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(DART_HOST_OS_MACOS)
#include "bin/process.h"
#include <errno.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <poll.h>
#include <signal.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/event.h>
#include <unistd.h>
#include "bin/dartutils.h"
#include "bin/fdutils.h"
#include "bin/lockers.h"
#include "bin/namespace.h"
#include "bin/thread.h"
#include "platform/syslog.h"
#include "platform/signal_blocker.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
int Process::global_exit_code_ = 0;
Mutex* Process::global_exit_code_mutex_ = nullptr;
Process::ExitHook Process::exit_hook_ = nullptr;
// Spawning new processes isn't supported on iOS.
#if !defined(DART_HOST_OS_IOS)
// The exit code handler sets up a separate thread which waits for child
// processes to terminate. That separate thread can then get the exit code from
// processes that have exited and communicate it to Dart through the
// event loop.
class ExitCodeHandler {
public:
static void Init();
static void Cleanup();
static void EnsureStarted() {
MonitorLocker locker(monitor_);
if (kqueue_fd_ != -1) {
return;
}
kqueue_fd_ = NO_RETRY_EXPECTED(kqueue());
if (kqueue_fd_ == -1) {
FATAL("Failed creating kqueue");
}
if (!FDUtils::SetCloseOnExec(kqueue_fd_)) {
FATAL("Failed to set kqueue fd close on exec\n");
}
// Start thread that handles process exits when wait returns.
Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
}
static void TerminateExitCodeThread() {
MonitorLocker locker(monitor_);
if (kqueue_fd_ == -1) {
return;
}
struct kevent event;
EV_SET(&event, kShutdown, EVFILT_USER, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_TRIGGER, 0, 0);
int status = kevent(kqueue_fd_, &event, 1, nullptr, 0, nullptr);
if (status == -1) {
FATAL("kevent failed");
}
while (kqueue_fd_ != -1) {
locker.Wait();
}
}
static void AddProcess(pid_t pid, int exit_pipe) {
ASSERT(pid != kShutdown);
struct kevent event;
EV_SET(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0,
reinterpret_cast<void*>(exit_pipe));
int status = kevent(kqueue_fd_, &event, 1, nullptr, 0, nullptr);
if (status == -1) {
if (errno == ESRCH) {
// Process already exited.
EV_SET(&event, pid, EVFILT_USER, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_TRIGGER, 0, reinterpret_cast<void*>(exit_pipe));
status = kevent(kqueue_fd_, &event, 1, nullptr, 0, nullptr);
if (status == -1) {
FATAL("kevent failed");
}
} else {
FATAL("kevent failed");
}
}
}
private:
// Entry point for the separate exit code handler thread started by
// the ExitCodeHandler.
static void ExitCodeHandlerEntry(uword param) {
while (true) {
struct kevent event;
int r = kevent(kqueue_fd_, nullptr, 0, &event, 1, nullptr);
if ((r == -1) && (errno != EINTR)) {
FATAL("kevent failed");
}
if (r == 0) continue;
ASSERT(event.filter == EVFILT_PROC || event.filter == EVFILT_USER);
int exit_pipe = reinterpret_cast<intptr_t>(event.udata);
pid_t pid = event.ident;
if (pid == kShutdown) break;
int status;
pid_t wait_result;
do {
wait_result = waitpid(pid, &status, 0);
} while (wait_result == -1 && errno == EINTR);
ASSERT(wait_result == pid);
int exit_code = 0;
int negative = 0;
if (WIFEXITED(status)) {
exit_code = WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
exit_code = WTERMSIG(status);
negative = 1;
}
int message[2] = {exit_code, negative};
ssize_t result =
FDUtils::WriteToBlocking(exit_pipe, &message, sizeof(message));
// If the process has been closed, the read end of the exit
// pipe has been closed. It is therefore not a problem that
// write fails with a broken pipe error. Other errors should
// not happen.
if ((result != -1) && (result != sizeof(message))) {
FATAL("Failed to write entire process exit message");
} else if ((result == -1) && (errno != EPIPE)) {
FATAL("Failed to write exit code: %d", errno);
}
close(exit_pipe);
}
MonitorLocker locker(monitor_);
close(kqueue_fd_);
kqueue_fd_ = -1;
locker.NotifyAll();
}
static Monitor* monitor_;
static int kqueue_fd_;
static constexpr pid_t kShutdown = 0;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler);
};
Monitor* ExitCodeHandler::monitor_ = nullptr;
int ExitCodeHandler::kqueue_fd_ = -1;
class ProcessStarter {
public:
ProcessStarter(const char* path,
const 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_event,
char** os_error_message)
: path_(path),
working_directory_(working_directory),
mode_(mode),
in_(in),
out_(out),
err_(err),
id_(id),
exit_event_(exit_event),
os_error_message_(os_error_message) {
stdout_pipe_[0] = -1;
stdout_pipe_[1] = -1;
stderr_pipe_[0] = -1;
stderr_pipe_[1] = -1;
stdin_pipe_[0] = -1;
stdin_pipe_[1] = -1;
exit_pipe_[0] = -1;
exit_pipe_[1] = -1;
program_arguments_ = reinterpret_cast<const char**>(Dart_ScopeAllocate(
(arguments_length + 2) * sizeof(*program_arguments_)));
program_arguments_[0] = const_cast<char*>(path_);
for (int i = 0; i < arguments_length; i++) {
program_arguments_[i + 1] = arguments[i];
}
program_arguments_[arguments_length + 1] = nullptr;
program_environment_ = nullptr;
if (environment != nullptr) {
program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate(
(environment_length + 1) * sizeof(*program_environment_)));
for (int i = 0; i < environment_length; i++) {
program_environment_[i] = environment[i];
}
program_environment_[environment_length] = nullptr;
}
}
int Start() {
// Create pipes required.
int err = CreatePipes();
if (err != 0) {
return err;
}
pid_t pid;
err = Spawn(&pid);
if (err != 0) {
errno = err;
return CleanupAndReturnError();
}
if (Process::ModeIsAttached(mode_)) {
ExitCodeHandler::EnsureStarted();
}
// If the child process is not started in detached mode, be sure to
// listen for exit-codes, now that we have a non detached child process
// and also Register this child process.
if (Process::ModeIsAttached(mode_)) {
RegisterProcess(pid);
}
if (Process::ModeHasStdio(mode_)) {
// Connect stdio, stdout and stderr.
FDUtils::SetNonBlocking(stdout_pipe_[0]);
*in_ = stdout_pipe_[0];
stdout_pipe_[0] = -1;
CloseWriteEndOfPipe(stdout_pipe_);
FDUtils::SetNonBlocking(stdin_pipe_[1]);
*out_ = stdin_pipe_[1];
stdin_pipe_[1] = -1;
CloseReadEndOfPipe(stdin_pipe_);
FDUtils::SetNonBlocking(stderr_pipe_[0]);
*err_ = stderr_pipe_[0];
stderr_pipe_[0] = -1;
CloseWriteEndOfPipe(stderr_pipe_);
} else {
// Close all fds.
ClosePipe(stdin_pipe_);
ASSERT(stdout_pipe_[0] == -1);
ASSERT(stdout_pipe_[1] == -1);
ASSERT(stderr_pipe_[0] == -1);
ASSERT(stderr_pipe_[1] == -1);
}
ASSERT(exit_pipe_[0] == -1);
ASSERT(exit_pipe_[1] == -1);
*id_ = pid;
return 0;
}
private:
int CreatePipes() {
int result;
if (Process::ModeHasStdio(mode_)) {
result = TEMP_FAILURE_RETRY(pipe(stdin_pipe_));
if (result < 0) {
return CleanupAndReturnError();
}
FDUtils::SetCloseOnExec(stdin_pipe_[0]);
FDUtils::SetCloseOnExec(stdin_pipe_[1]);
result = TEMP_FAILURE_RETRY(pipe(stdout_pipe_));
if (result < 0) {
return CleanupAndReturnError();
}
FDUtils::SetCloseOnExec(stdout_pipe_[0]);
FDUtils::SetCloseOnExec(stdout_pipe_[1]);
result = TEMP_FAILURE_RETRY(pipe(stderr_pipe_));
if (result < 0) {
return CleanupAndReturnError();
}
FDUtils::SetCloseOnExec(stderr_pipe_[0]);
FDUtils::SetCloseOnExec(stderr_pipe_[1]);
}
if (Process::ModeIsAttached(mode_)) {
result = TEMP_FAILURE_RETRY(pipe(exit_pipe_));
if (result < 0) {
return CleanupAndReturnError();
}
FDUtils::SetCloseOnExec(exit_pipe_[0]);
FDUtils::SetCloseOnExec(exit_pipe_[1]);
}
return 0;
}
int Spawn(pid_t* pid) {
#define TRY(expr) \
do { \
int err = (expr); \
if (err != 0) return err; \
} while (false)
class DestroyFileActions {
public:
explicit DestroyFileActions(posix_spawn_file_actions_t* facts)
: facts_(facts) {}
~DestroyFileActions() { posix_spawn_file_actions_destroy(facts_); }
posix_spawn_file_actions_t* facts_;
};
class DestroyAttr {
public:
explicit DestroyAttr(posix_spawnattr_t* attr) : attr_(attr) {}
~DestroyAttr() { posix_spawnattr_destroy(attr_); }
posix_spawnattr_t* attr_;
};
posix_spawn_file_actions_t facts = {};
posix_spawnattr_t attr = {};
TRY(posix_spawn_file_actions_init(&facts));
DestroyFileActions dfa(&facts);
if (Process::ModeHasStdio(mode_)) {
TRY(posix_spawn_file_actions_adddup2(&facts, stdin_pipe_[0],
STDIN_FILENO));
TRY(posix_spawn_file_actions_adddup2(&facts, stdout_pipe_[1],
STDOUT_FILENO));
TRY(posix_spawn_file_actions_adddup2(&facts, stderr_pipe_[1],
STDERR_FILENO));
} else if (mode_ == kDetached) {
TRY(posix_spawn_file_actions_addopen(&facts, STDIN_FILENO, "/dev/null",
O_RDWR, 0));
TRY(posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null",
O_RDWR, 0));
TRY(posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null",
O_RDWR, 0));
} else {
ASSERT(mode_ == kInheritStdio);
TRY(posix_spawn_file_actions_addinherit_np(&facts, STDIN_FILENO));
TRY(posix_spawn_file_actions_addinherit_np(&facts, STDOUT_FILENO));
TRY(posix_spawn_file_actions_addinherit_np(&facts, STDERR_FILENO));
}
if (working_directory_ != nullptr) {
TRY(posix_spawn_file_actions_addchdir_np(&facts, working_directory_));
}
TRY(posix_spawnattr_init(&attr));
DestroyAttr da(&attr);
TRY(posix_spawnattr_setflags(
&attr, POSIX_SPAWN_CLOEXEC_DEFAULT |
(Process::ModeIsAttached(mode_) ? 0 : POSIX_SPAWN_SETSID)));
return posix_spawnp(pid, path_, &facts, &attr,
const_cast<char* const*>(program_arguments_),
program_environment_);
#undef TRY
}
void RegisterProcess(pid_t pid) {
ASSERT(exit_pipe_[0] != -1);
ASSERT(exit_pipe_[1] != -1);
ExitCodeHandler::AddProcess(pid, exit_pipe_[1]);
exit_pipe_[1] = -1;
*exit_event_ = exit_pipe_[0];
FDUtils::SetNonBlocking(exit_pipe_[0]);
exit_pipe_[0] = -1;
}
int CleanupAndReturnError() {
int actual_errno = errno;
// If CleanupAndReturnError is called without an actual errno make
// sure to return an error anyway.
if (actual_errno == 0) {
actual_errno = EPERM;
}
SetChildOsErrorMessage();
CloseAllPipes();
return actual_errno;
}
void SetChildOsErrorMessage() {
const int kBufferSize = 1024;
char* error_message = DartUtils::ScopedCString(kBufferSize);
*os_error_message_ = Utils::StrError(errno, error_message, kBufferSize);
}
void ClosePipe(int* fds) {
CloseReadEndOfPipe(fds);
CloseWriteEndOfPipe(fds);
}
void CloseReadEndOfPipe(int* fds) {
if (fds[0] != -1) {
close(fds[0]);
fds[0] = -1;
}
}
void CloseWriteEndOfPipe(int* fds) {
if (fds[1] != -1) {
close(fds[1]);
fds[1] = -1;
}
}
void CloseAllPipes() {
ClosePipe(stdout_pipe_);
ClosePipe(stderr_pipe_);
ClosePipe(stdin_pipe_);
ClosePipe(exit_pipe_);
}
int stdout_pipe_[2]; // Pipe for stdout to child process.
int stderr_pipe_[2]; // Pipe for stderr to child process.
int stdin_pipe_[2]; // Pipe for stdin to child process.
int exit_pipe_[2]; // Pipe for exit event.
const char** program_arguments_;
char** program_environment_;
const char* path_;
const char* working_directory_;
ProcessStartMode mode_;
intptr_t* in_;
intptr_t* out_;
intptr_t* err_;
intptr_t* id_;
intptr_t* exit_event_;
char** os_error_message_;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
};
#endif // !defined(DART_HOST_OS_IOS)
int Process::Start(Namespace* namespc,
const char* path,
const 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_event,
char** os_error_message) {
#if defined(DART_HOST_OS_IOS)
return EPERM;
#else // defined(DART_HOST_OS_IOS)
ProcessStarter starter(path, arguments, arguments_length, working_directory,
environment, environment_length, mode, in, out, err,
id, exit_event, os_error_message);
return starter.Start();
#endif // defined(DART_HOST_OS_IOS)
}
#if !defined(DART_HOST_OS_IOS)
static bool CloseProcessBuffers(struct pollfd* fds, int alive) {
int e = errno;
for (int i = 0; i < alive; i++) {
close(fds[i].fd);
}
errno = e;
return false;
}
#endif // !defined(DART_HOST_OS_IOS)
bool Process::Wait(intptr_t pid,
intptr_t in,
intptr_t out,
intptr_t err,
intptr_t exit_event,
ProcessResult* result) {
#if defined(DART_HOST_OS_IOS)
return false;
#else // defined(DART_HOST_OS_IOS)
// Close input to the process right away.
close(in);
// There is no return from this function using Dart_PropagateError
// as memory used by the buffer lists is freed through their
// destructors.
BufferList out_data;
BufferList err_data;
union {
uint8_t bytes[8];
int32_t ints[2];
} exit_code_data = {};
struct pollfd fds[3];
fds[0].fd = out;
fds[1].fd = err;
fds[2].fd = exit_event;
for (int i = 0; i < 3; i++) {
fds[i].events = POLLIN;
}
int alive = 3;
while (alive > 0) {
// Blocking call waiting for events from the child process.
if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) {
return CloseProcessBuffers(fds, alive);
}
// Process incoming data.
for (int i = 0; i < alive; i++) {
intptr_t avail = 0;
if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) {
return CloseProcessBuffers(fds, alive);
}
if ((fds[i].revents & (POLLIN | POLLHUP)) != 0) {
avail = FDUtils::AvailableBytes(fds[i].fd);
// On Mac OS POLLIN can be set with zero available
// bytes. POLLHUP is most likely also set in this case.
if (avail > 0) {
if (fds[i].fd == out) {
if (!out_data.Read(out, avail)) {
return CloseProcessBuffers(fds, alive);
}
} else if (fds[i].fd == err) {
if (!err_data.Read(err, avail)) {
return CloseProcessBuffers(fds, alive);
}
} else if (fds[i].fd == exit_event) {
if (avail == 8) {
intptr_t b =
TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8));
if (b != 8) {
return CloseProcessBuffers(fds, alive);
}
}
} else {
UNREACHABLE();
}
}
}
if (((fds[i].revents & POLLHUP) != 0) ||
(((fds[i].revents & POLLIN) != 0) && (avail == 0))) {
// Remove the pollfd from the list of pollfds.
close(fds[i].fd);
alive--;
if (i < alive) {
fds[i] = fds[alive];
}
// Process the same index again.
i--;
continue;
}
}
}
// All handles closed and all data read.
result->set_stdout_data(out_data.GetData());
result->set_stderr_data(err_data.GetData());
DEBUG_ASSERT(out_data.IsEmpty());
DEBUG_ASSERT(err_data.IsEmpty());
// Calculate the exit code.
intptr_t exit_code = exit_code_data.ints[0];
intptr_t negative = exit_code_data.ints[1];
if (negative != 0) {
exit_code = -exit_code;
}
result->set_exit_code(exit_code);
return true;
#endif // defined(DART_HOST_OS_IOS)
}
int Process::Exec(Namespace* namespc,
const char* path,
const char* arguments[],
intptr_t arguments_length,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len) {
#if defined(DART_HOST_OS_WATCH)
// execvp is not available on watchOS.
Utils::StrError(ENOSYS, errmsg, errmsg_len);
return -1;
#else
if (working_directory != nullptr &&
TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) {
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
}
execvp(const_cast<const char*>(path), const_cast<char* const*>(arguments));
Utils::StrError(errno, errmsg, errmsg_len);
return -1;
#endif
}
static int SignalMap(intptr_t id) {
switch (static_cast<ProcessSignals>(id)) {
case kSighup:
return SIGHUP;
case kSigint:
return SIGINT;
case kSigquit:
return SIGQUIT;
case kSigill:
return SIGILL;
case kSigtrap:
return SIGTRAP;
case kSigabrt:
return SIGABRT;
case kSigbus:
return SIGBUS;
case kSigfpe:
return SIGFPE;
case kSigkill:
return SIGKILL;
case kSigusr1:
return SIGUSR1;
case kSigsegv:
return SIGSEGV;
case kSigusr2:
return SIGUSR2;
case kSigpipe:
return SIGPIPE;
case kSigalrm:
return SIGALRM;
case kSigterm:
return SIGTERM;
case kSigchld:
return SIGCHLD;
case kSigcont:
return SIGCONT;
case kSigstop:
return SIGSTOP;
case kSigtstp:
return SIGTSTP;
case kSigttin:
return SIGTTIN;
case kSigttou:
return SIGTTOU;
case kSigurg:
return SIGURG;
case kSigxcpu:
return SIGXCPU;
case kSigxfsz:
return SIGXFSZ;
case kSigvtalrm:
return SIGVTALRM;
case kSigprof:
return SIGPROF;
case kSigwinch:
return SIGWINCH;
case kSigpoll:
return -1;
case kSigsys:
return SIGSYS;
}
return -1;
}
bool Process::Kill(intptr_t id, int signal) {
#if defined(DART_HOST_OS_IOS)
return false;
#else // defined(DART_HOST_OS_IOS)
return (TEMP_FAILURE_RETRY(kill(id, SignalMap(signal))) != -1);
#endif // defined(DART_HOST_OS_IOS)
}
void Process::TerminateExitCodeHandler() {
#if !defined(DART_HOST_OS_IOS)
ExitCodeHandler::TerminateExitCodeThread();
#endif // !defined(DART_HOST_OS_IOS)
}
intptr_t Process::CurrentProcessId() {
return static_cast<intptr_t>(getpid());
}
int64_t Process::CurrentRSS() {
struct mach_task_basic_info info;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
kern_return_t result =
task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info), &infoCount);
if (result != KERN_SUCCESS) {
return -1;
}
return info.resident_size;
}
int64_t Process::MaxRSS() {
struct rusage usage;
usage.ru_maxrss = 0;
int r = getrusage(RUSAGE_SELF, &usage);
if (r < 0) {
return -1;
}
return usage.ru_maxrss;
}
static Mutex* signal_mutex = nullptr;
static SignalInfo* signal_handlers = nullptr;
static constexpr int kSignalsCount = 7;
static const int kSignals[kSignalsCount] = {
SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH,
SIGQUIT // Allow VMService to listen on SIGQUIT.
};
SignalInfo::~SignalInfo() {
close(fd_);
}
static void SignalHandler(int signal) {
MutexLocker lock(signal_mutex);
const SignalInfo* handler = signal_handlers;
while (handler != nullptr) {
if (handler->signal() == signal) {
int value = 0;
VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1));
}
handler = handler->next();
}
}
intptr_t Process::SetSignalHandler(intptr_t signal) {
signal = SignalMap(signal);
if (signal == -1) {
return -1;
}
bool found = false;
for (int i = 0; i < kSignalsCount; i++) {
if (kSignals[i] == signal) {
found = true;
break;
}
}
if (!found) {
return -1;
}
int fds[2];
if (NO_RETRY_EXPECTED(pipe(fds)) != 0) {
return -1;
}
if (!FDUtils::SetCloseOnExec(fds[0]) || !FDUtils::SetCloseOnExec(fds[1]) ||
!FDUtils::SetNonBlocking(fds[0])) {
close(fds[0]);
close(fds[1]);
return -1;
}
ThreadSignalBlocker blocker(kSignalsCount, kSignals);
MutexLocker lock(signal_mutex);
SignalInfo* handler = signal_handlers;
bool listen = true;
sa_handler_t oldact_handler = nullptr;
while (handler != nullptr) {
if (handler->signal() == signal) {
oldact_handler = handler->oldact();
listen = false;
break;
}
handler = handler->next();
}
if (listen) {
struct sigaction act = {};
act.sa_handler = SignalHandler;
sigemptyset(&act.sa_mask);
for (int i = 0; i < kSignalsCount; i++) {
sigaddset(&act.sa_mask, kSignals[i]);
}
struct sigaction oldact = {};
intptr_t status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact));
if (status < 0) {
close(fds[0]);
close(fds[1]);
return -1;
}
oldact_handler = oldact.sa_handler;
}
signal_handlers =
new SignalInfo(fds[1], signal, oldact_handler, signal_handlers);
return fds[0];
}
void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {
signal = SignalMap(signal);
if (signal == -1) {
return;
}
ThreadSignalBlocker blocker(kSignalsCount, kSignals);
MutexLocker lock(signal_mutex);
SignalInfo* handler = signal_handlers;
sa_handler_t oldact_handler = SIG_DFL;
bool any_removed = false;
bool any_remaining = false;
while (handler != nullptr) {
bool remove = false;
if (handler->signal() == signal) {
if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
if (signal_handlers == handler) {
signal_handlers = handler->next();
}
handler->Unlink();
remove = true;
oldact_handler = handler->oldact();
any_removed = true;
} else {
any_remaining = true;
}
}
SignalInfo* next = handler->next();
if (remove) {
delete handler;
}
handler = next;
}
if (any_removed && !any_remaining) {
struct sigaction act = {};
act.sa_handler = oldact_handler;
VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, nullptr));
}
}
void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {
ThreadSignalBlocker blocker(kSignalsCount, kSignals);
MutexLocker lock(signal_mutex);
SignalInfo* handler = signal_handlers;
sa_handler_t oldact_handler = SIG_DFL;
bool any_remaining = false;
intptr_t signal = -1;
while (handler != nullptr) {
bool remove = false;
if (handler->fd() == fd) {
if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
if (signal_handlers == handler) {
signal_handlers = handler->next();
}
handler->Unlink();
remove = true;
signal = handler->signal();
} else {
any_remaining = true;
}
}
SignalInfo* next = handler->next();
if (remove) {
delete handler;
}
handler = next;
}
if ((signal != -1) && !any_remaining) {
struct sigaction act = {};
act.sa_handler = oldact_handler;
VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, nullptr));
}
}
#if !defined(DART_HOST_OS_IOS)
void ExitCodeHandler::Init() {
ASSERT(ExitCodeHandler::monitor_ == nullptr);
ExitCodeHandler::monitor_ = new Monitor();
}
void ExitCodeHandler::Cleanup() {
ASSERT(ExitCodeHandler::monitor_ != nullptr);
delete ExitCodeHandler::monitor_;
ExitCodeHandler::monitor_ = nullptr;
}
#endif // !defined(DART_HOST_OS_IOS)
void Process::Init() {
#if !defined(DART_HOST_OS_IOS)
ExitCodeHandler::Init();
#endif // !defined(DART_HOST_OS_IOS)
ASSERT(signal_mutex == nullptr);
signal_mutex = new Mutex();
signal_handlers = nullptr;
ASSERT(Process::global_exit_code_mutex_ == nullptr);
Process::global_exit_code_mutex_ = new Mutex();
}
void Process::Cleanup() {
ClearAllSignalHandlers();
ASSERT(signal_mutex != nullptr);
delete signal_mutex;
signal_mutex = nullptr;
ASSERT(Process::global_exit_code_mutex_ != nullptr);
delete Process::global_exit_code_mutex_;
Process::global_exit_code_mutex_ = nullptr;
#if !defined(DART_HOST_OS_IOS)
ExitCodeHandler::Cleanup();
#endif // !defined(DART_HOST_OS_IOS)
}
} // namespace bin
} // namespace dart
#endif // defined(DART_HOST_OS_MACOS)