blob: 9bd547c802b4c60f341ed885f0b83aee7d41bc69 [file] [log] [blame] [edit]
// 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.
#ifndef RUNTIME_BIN_PROCESS_H_
#define RUNTIME_BIN_PROCESS_H_
#include <errno.h>
#include "bin/builtin.h"
#include "bin/io_buffer.h"
#include "bin/lockers.h"
#include "bin/namespace.h"
#include "bin/thread.h"
#include "platform/globals.h"
#if !defined(DART_HOST_OS_WINDOWS)
#include "platform/signal_blocker.h"
#endif
#include "platform/utils.h"
namespace dart {
namespace bin {
class ProcessResult {
public:
ProcessResult() : exit_code_(0) {}
void set_stdout_data(Dart_Handle stdout_data) { stdout_data_ = stdout_data; }
void set_stderr_data(Dart_Handle stderr_data) { stderr_data_ = stderr_data; }
void set_exit_code(intptr_t exit_code) { exit_code_ = exit_code; }
Dart_Handle stdout_data() { return stdout_data_; }
Dart_Handle stderr_data() { return stderr_data_; }
intptr_t exit_code() { return exit_code_; }
private:
Dart_Handle stdout_data_;
Dart_Handle stderr_data_;
intptr_t exit_code_;
DISALLOW_ALLOCATION();
};
// To be kept in sync with ProcessSignal consts in sdk/lib/io/process.dart
// Note that this map is as on Linux.
enum ProcessSignals {
kSighup = 1,
kSigint = 2,
kSigquit = 3,
kSigill = 4,
kSigtrap = 5,
kSigabrt = 6,
kSigbus = 7,
kSigfpe = 8,
kSigkill = 9,
kSigusr1 = 10,
kSigsegv = 11,
kSigusr2 = 12,
kSigpipe = 13,
kSigalrm = 14,
kSigterm = 15,
kSigchld = 17,
kSigcont = 18,
kSigstop = 19,
kSigtstp = 20,
kSigttin = 21,
kSigttou = 22,
kSigurg = 23,
kSigxcpu = 24,
kSigxfsz = 25,
kSigvtalrm = 26,
kSigprof = 27,
kSigwinch = 28,
kSigpoll = 29,
kSigsys = 31,
kLastSignal = kSigsys,
};
// To be kept in sync with ProcessStartMode consts in sdk/lib/io/process.dart.
enum ProcessStartMode {
kNormal = 0,
kInheritStdio = 1,
kDetached = 2,
kDetachedWithStdio = 3,
};
class Process {
public:
static void Init();
static void Cleanup();
// Start a new process providing access to stdin, stdout, stderr and
// process exit streams.
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 bool Wait(intptr_t id,
intptr_t in,
intptr_t out,
intptr_t err,
intptr_t exit_handler,
ProcessResult* result);
// Kill a process with a given pid.
static bool Kill(intptr_t id, int signal);
// Terminate the exit code handler thread. Does not return before
// the thread has terminated.
static void TerminateExitCodeHandler();
static int GlobalExitCode() {
MutexLocker ml(global_exit_code_mutex_);
return global_exit_code_;
}
static void SetGlobalExitCode(int exit_code) {
MutexLocker ml(global_exit_code_mutex_);
global_exit_code_ = exit_code;
}
typedef void (*ExitHook)(int64_t exit_code);
static void SetExitHook(ExitHook hook) { exit_hook_ = hook; }
static void RunExitHook(int64_t exit_code) {
if (exit_hook_ != nullptr) {
exit_hook_(exit_code);
}
}
static intptr_t CurrentProcessId();
static intptr_t SetSignalHandler(intptr_t signal);
// When there is a current Isolate and the 'port' argument is
// Dart_GetMainPortId(), this clears the signal handler for the current
// isolate. When 'port' is ILLEGAL_PORT, this clears all signal handlers for
// 'signal' for all Isolates.
static void ClearSignalHandler(intptr_t signal, Dart_Port port);
static void ClearSignalHandlerByFd(intptr_t fd, Dart_Port port);
static void ClearAllSignalHandlers();
static Dart_Handle GetProcessIdNativeField(Dart_Handle process,
intptr_t* pid);
static Dart_Handle SetProcessIdNativeField(Dart_Handle process, intptr_t pid);
static int64_t CurrentRSS();
static int64_t MaxRSS();
static void GetRSSInformation(int64_t* max_rss, int64_t* current_rss);
static bool ModeIsAttached(ProcessStartMode mode);
static bool ModeHasStdio(ProcessStartMode mode);
private:
static int global_exit_code_;
static Mutex* global_exit_code_mutex_;
static ExitHook exit_hook_;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(Process);
};
typedef void (*sa_handler_t)(int);
class SignalInfo {
public:
SignalInfo(intptr_t fd,
intptr_t signal,
sa_handler_t oldact,
SignalInfo* next)
: fd_(fd),
signal_(signal),
oldact_(oldact),
// SignalInfo is expected to be created when in a isolate.
port_(Dart_GetMainPortId()),
next_(next),
prev_(nullptr) {
if (next_ != nullptr) {
next_->prev_ = this;
}
}
~SignalInfo();
void Unlink() {
if (prev_ != nullptr) {
prev_->next_ = next_;
}
if (next_ != nullptr) {
next_->prev_ = prev_;
}
}
intptr_t fd() const { return fd_; }
intptr_t signal() const { return signal_; }
sa_handler_t oldact() const { return oldact_; }
Dart_Port port() const { return port_; }
SignalInfo* next() const { return next_; }
private:
intptr_t fd_;
intptr_t signal_;
sa_handler_t oldact_;
// The port_ is used to identify what isolate the signal-info belongs to.
Dart_Port port_;
SignalInfo* next_;
SignalInfo* prev_;
DISALLOW_COPY_AND_ASSIGN(SignalInfo);
};
// Utility class for collecting the output when running a process
// synchronously by using Process::Wait. This class is sub-classed in
// the platform specific files to implement reading into the buffers
// allocated.
class BufferListBase {
protected:
static constexpr intptr_t kBufferSize = 16 * 1024;
class BufferListNode {
public:
explicit BufferListNode(intptr_t size) {
data_ = new uint8_t[size];
// We check for a failed allocation below in Allocate()
next_ = nullptr;
}
~BufferListNode() { delete[] data_; }
bool Valid() const { return data_ != nullptr; }
uint8_t* data() const { return data_; }
BufferListNode* next() const { return next_; }
void set_next(BufferListNode* n) { next_ = n; }
private:
uint8_t* data_;
BufferListNode* next_;
DISALLOW_IMPLICIT_CONSTRUCTORS(BufferListNode);
};
public:
BufferListBase()
: head_(nullptr), tail_(nullptr), data_size_(0), free_size_(0) {}
~BufferListBase() {
Free();
DEBUG_ASSERT(IsEmpty());
}
// Returns the collected data as a Uint8List. If an error occurs an
// error handle is returned.
Dart_Handle GetData() {
uint8_t* buffer;
intptr_t buffer_position = 0;
Dart_Handle result = IOBuffer::Allocate(data_size_, &buffer);
if (Dart_IsNull(result)) {
return DartUtils::NewDartOSError();
}
if (Dart_IsError(result)) {
Free();
return result;
}
for (BufferListNode* current = head_; current != nullptr;
current = current->next()) {
intptr_t to_copy = dart::Utils::Minimum(data_size_, kBufferSize);
memmove(buffer + buffer_position, current->data(), to_copy);
buffer_position += to_copy;
data_size_ -= to_copy;
}
ASSERT(data_size_ == 0);
Free();
return result;
}
#if defined(DEBUG)
bool IsEmpty() const { return (head_ == nullptr) && (tail_ == nullptr); }
#endif
protected:
bool Allocate() {
ASSERT(free_size_ == 0);
BufferListNode* node = new BufferListNode(kBufferSize);
if ((node == nullptr) || !node->Valid()) {
// Failed to allocate a buffer for the node.
delete node;
return false;
}
if (head_ == nullptr) {
head_ = node;
tail_ = node;
} else {
ASSERT(tail_->next() == nullptr);
tail_->set_next(node);
tail_ = node;
}
free_size_ = kBufferSize;
return true;
}
void Free() {
BufferListNode* current = head_;
while (current != nullptr) {
BufferListNode* tmp = current;
current = current->next();
delete tmp;
}
head_ = nullptr;
tail_ = nullptr;
data_size_ = 0;
free_size_ = 0;
}
// Returns the address of the first byte in the free space.
uint8_t* FreeSpaceAddress() {
return tail_->data() + (kBufferSize - free_size_);
}
intptr_t data_size() const { return data_size_; }
void set_data_size(intptr_t size) { data_size_ = size; }
intptr_t free_size() const { return free_size_; }
void set_free_size(intptr_t size) { free_size_ = size; }
BufferListNode* head() const { return head_; }
BufferListNode* tail() const { return tail_; }
private:
// Linked list for data collected.
BufferListNode* head_;
BufferListNode* tail_;
// Number of bytes of data collected in the linked list.
intptr_t data_size_;
// Number of free bytes in the last node in the list.
intptr_t free_size_;
DISALLOW_COPY_AND_ASSIGN(BufferListBase);
};
#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \
defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS)
class BufferList : public BufferListBase {
public:
BufferList() {}
bool Read(int fd, intptr_t available) {
// Read all available bytes.
while (available > 0) {
if (free_size() == 0) {
if (!Allocate()) {
errno = ENOMEM;
return false;
}
}
ASSERT(free_size() > 0);
ASSERT(free_size() <= kBufferSize);
intptr_t block_size = dart::Utils::Minimum(free_size(), available);
#if defined(DART_HOST_OS_FUCHSIA)
intptr_t bytes = NO_RETRY_EXPECTED(
read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size));
#else
intptr_t bytes = TEMP_FAILURE_RETRY(
read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size));
#endif // defined(DART_HOST_OS_FUCHSIA)
if (bytes < 0) {
return false;
}
set_data_size(data_size() + bytes);
set_free_size(free_size() - bytes);
available -= bytes;
}
return true;
}
private:
DISALLOW_COPY_AND_ASSIGN(BufferList);
};
#endif // defined(DART_HOST_OS_ANDROID) ...
} // namespace bin
} // namespace dart
#endif // RUNTIME_BIN_PROCESS_H_