mirror of
https://github.com/dart-lang/sdk
synced 2024-10-15 01:09:37 +00:00
53fb57ea5e
Cf. ef4984566b
.
TEST=samples/hello_world
Bug: https://github.com/dart-lang/samples/issues/195
Change-Id: I05cac92500a912c5d3e0771f626878decac18a9b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/324267
Reviewed-by: Brian Quinlan <bquinlan@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
405 lines
15 KiB
C++
405 lines
15 KiB
C++
// Copyright (c) 2013, 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 "bin/process.h"
|
|
|
|
#include "bin/dartutils.h"
|
|
#include "bin/io_buffer.h"
|
|
#include "bin/namespace.h"
|
|
#include "bin/platform.h"
|
|
#include "bin/socket.h"
|
|
#include "bin/utils.h"
|
|
#include "platform/syslog.h"
|
|
|
|
#include "include/dart_api.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
static constexpr int kProcessIdNativeField = 0;
|
|
|
|
// Extract an array of C strings from a list of Dart strings.
|
|
static char** ExtractCStringList(Dart_Handle strings,
|
|
Dart_Handle status_handle,
|
|
const char* error_msg,
|
|
intptr_t* length) {
|
|
static constexpr intptr_t kMaxArgumentListLength = 1024 * 1024;
|
|
ASSERT(Dart_IsList(strings));
|
|
intptr_t len = 0;
|
|
Dart_Handle result = Dart_ListLength(strings, &len);
|
|
ThrowIfError(result);
|
|
// Protect against user-defined list implementations that can have
|
|
// arbitrary length.
|
|
if ((len < 0) || (len > kMaxArgumentListLength)) {
|
|
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
|
|
ThrowIfError(result);
|
|
result = DartUtils::SetStringField(status_handle, "_errorMessage",
|
|
"Max argument list length exceeded");
|
|
ThrowIfError(result);
|
|
return nullptr;
|
|
}
|
|
*length = len;
|
|
char** string_args;
|
|
string_args =
|
|
reinterpret_cast<char**>(Dart_ScopeAllocate(len * sizeof(*string_args)));
|
|
for (int i = 0; i < len; i++) {
|
|
Dart_Handle arg = Dart_ListGetAt(strings, i);
|
|
ThrowIfError(arg);
|
|
if (!Dart_IsString(arg)) {
|
|
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
|
|
ThrowIfError(result);
|
|
result =
|
|
DartUtils::SetStringField(status_handle, "_errorMessage", error_msg);
|
|
ThrowIfError(result);
|
|
return nullptr;
|
|
}
|
|
string_args[i] = const_cast<char*>(DartUtils::GetStringValue(arg));
|
|
}
|
|
return string_args;
|
|
}
|
|
|
|
bool Process::ModeIsAttached(ProcessStartMode mode) {
|
|
return (mode == kNormal) || (mode == kInheritStdio);
|
|
}
|
|
|
|
bool Process::ModeHasStdio(ProcessStartMode mode) {
|
|
return (mode == kNormal) || (mode == kDetachedWithStdio);
|
|
}
|
|
|
|
void Process::ClearAllSignalHandlers() {
|
|
for (intptr_t i = 1; i <= kLastSignal; i++) {
|
|
ClearSignalHandler(i, ILLEGAL_PORT);
|
|
}
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
|
|
Dart_Handle process = Dart_GetNativeArgument(args, 0);
|
|
intptr_t process_stdin;
|
|
intptr_t process_stdout;
|
|
intptr_t process_stderr;
|
|
intptr_t exit_event;
|
|
Namespace* namespc = Namespace::GetNamespace(args, 1);
|
|
Dart_Handle status_handle = Dart_GetNativeArgument(args, 11);
|
|
Dart_Handle path_handle = Dart_GetNativeArgument(args, 2);
|
|
Dart_Handle result;
|
|
|
|
#if DART_HOST_OS_IOS
|
|
// Do the iOS check here because the return value of Process::Start is
|
|
// interpreted as a error with 0 meaning success while `ProcessException`
|
|
// (which will be constructed with `_errorCode`) interprets 0 to mean that
|
|
// no OS error code was available.
|
|
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
|
|
ThrowIfError(result);
|
|
result = DartUtils::SetStringField(
|
|
status_handle, "_errorMessage",
|
|
"Starting new processes is not supported on iOS");
|
|
ThrowIfError(result);
|
|
Dart_SetBooleanReturnValue(args, false);
|
|
return;
|
|
#endif
|
|
|
|
// The Dart code verifies that the path implements the String
|
|
// interface. However, only builtin Strings are handled by
|
|
// GetStringValue.
|
|
if (!Dart_IsString(path_handle)) {
|
|
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
|
|
ThrowIfError(result);
|
|
result = DartUtils::SetStringField(status_handle, "_errorMessage",
|
|
"Path must be a builtin string");
|
|
ThrowIfError(result);
|
|
Dart_SetBooleanReturnValue(args, false);
|
|
return;
|
|
}
|
|
const char* path = DartUtils::GetStringValue(path_handle);
|
|
Dart_Handle arguments = Dart_GetNativeArgument(args, 3);
|
|
intptr_t args_length = 0;
|
|
char** string_args =
|
|
ExtractCStringList(arguments, status_handle,
|
|
"Arguments must be builtin strings", &args_length);
|
|
if (string_args == nullptr) {
|
|
Dart_SetBooleanReturnValue(args, false);
|
|
return;
|
|
}
|
|
Dart_Handle working_directory_handle = Dart_GetNativeArgument(args, 4);
|
|
// Defaults to the current working directory.
|
|
const char* working_directory = nullptr;
|
|
if (Dart_IsString(working_directory_handle)) {
|
|
working_directory = DartUtils::GetStringValue(working_directory_handle);
|
|
} else if (!Dart_IsNull(working_directory_handle)) {
|
|
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
|
|
ThrowIfError(result);
|
|
result =
|
|
DartUtils::SetStringField(status_handle, "_errorMessage",
|
|
"WorkingDirectory must be a builtin string");
|
|
ThrowIfError(result);
|
|
Dart_SetBooleanReturnValue(args, false);
|
|
return;
|
|
}
|
|
Dart_Handle environment = Dart_GetNativeArgument(args, 5);
|
|
intptr_t environment_length = 0;
|
|
char** string_environment = nullptr;
|
|
if (!Dart_IsNull(environment)) {
|
|
string_environment = ExtractCStringList(
|
|
environment, status_handle,
|
|
"Environment values must be builtin strings", &environment_length);
|
|
if (string_environment == nullptr) {
|
|
Dart_SetBooleanReturnValue(args, false);
|
|
return;
|
|
}
|
|
}
|
|
int64_t mode =
|
|
DartUtils::GetInt64ValueCheckRange(Dart_GetNativeArgument(args, 6), 0, 3);
|
|
Dart_Handle stdin_handle = Dart_GetNativeArgument(args, 7);
|
|
Dart_Handle stdout_handle = Dart_GetNativeArgument(args, 8);
|
|
Dart_Handle stderr_handle = Dart_GetNativeArgument(args, 9);
|
|
Dart_Handle exit_handle = Dart_GetNativeArgument(args, 10);
|
|
intptr_t pid = -1;
|
|
char* os_error_message = nullptr; // Scope allocated by Process::Start.
|
|
|
|
int error_code = Process::Start(
|
|
namespc, path, string_args, args_length, working_directory,
|
|
string_environment, environment_length,
|
|
static_cast<ProcessStartMode>(mode), &process_stdout, &process_stdin,
|
|
&process_stderr, &pid, &exit_event, &os_error_message);
|
|
if (error_code == 0) {
|
|
if (Process::ModeHasStdio(static_cast<ProcessStartMode>(mode))) {
|
|
Socket::SetSocketIdNativeField(stdin_handle, process_stdin,
|
|
Socket::kFinalizerNormal);
|
|
Socket::SetSocketIdNativeField(stdout_handle, process_stdout,
|
|
Socket::kFinalizerNormal);
|
|
Socket::SetSocketIdNativeField(stderr_handle, process_stderr,
|
|
Socket::kFinalizerNormal);
|
|
}
|
|
if (Process::ModeIsAttached(static_cast<ProcessStartMode>(mode))) {
|
|
Socket::SetSocketIdNativeField(exit_handle, exit_event,
|
|
Socket::kFinalizerNormal);
|
|
}
|
|
Process::SetProcessIdNativeField(process, pid);
|
|
} else {
|
|
result =
|
|
DartUtils::SetIntegerField(status_handle, "_errorCode", error_code);
|
|
ThrowIfError(result);
|
|
|
|
const char* error_message = (os_error_message != nullptr)
|
|
? os_error_message
|
|
: "Failed to get error message";
|
|
Dart_Handle val = DartUtils::NewString(error_message);
|
|
if (Dart_IsError(val)) {
|
|
// Try to clean the message from non-ASCII characters.
|
|
const intptr_t len = strlen(error_message);
|
|
char* ascii_message =
|
|
reinterpret_cast<char*>(Dart_ScopeAllocate(len + 1));
|
|
for (intptr_t i = 0; i < len; i++) {
|
|
if (static_cast<uint8_t>(error_message[i]) < 0x80) {
|
|
ascii_message[i] = error_message[i];
|
|
} else {
|
|
ascii_message[i] = '?';
|
|
}
|
|
}
|
|
ascii_message[len] = '\0';
|
|
|
|
val = DartUtils::NewStringFormatted(
|
|
"Failed to start %s. OS returned an error (code %d) which can't be "
|
|
"fully converted to Dart string (%s): %s",
|
|
path, error_code, Dart_GetError(val), ascii_message);
|
|
}
|
|
result = Dart_SetField(status_handle, DartUtils::NewString("_errorMessage"),
|
|
val);
|
|
ThrowIfError(result);
|
|
}
|
|
Dart_SetBooleanReturnValue(args, error_code == 0);
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_Wait)(Dart_NativeArguments args) {
|
|
Dart_Handle process = Dart_GetNativeArgument(args, 0);
|
|
Socket* process_stdin =
|
|
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 1));
|
|
Socket* process_stdout =
|
|
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 2));
|
|
Socket* process_stderr =
|
|
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 3));
|
|
Socket* exit_event =
|
|
Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 4));
|
|
ProcessResult result;
|
|
intptr_t pid;
|
|
Process::GetProcessIdNativeField(process, &pid);
|
|
bool success = Process::Wait(pid, process_stdin->fd(), process_stdout->fd(),
|
|
process_stderr->fd(), exit_event->fd(), &result);
|
|
// Process::Wait() closes the file handles, so blow away the fds in the
|
|
// Sockets so that they don't get picked up by the finalizer on _NativeSocket.
|
|
process_stdin->CloseFd();
|
|
process_stdout->CloseFd();
|
|
process_stderr->CloseFd();
|
|
exit_event->CloseFd();
|
|
if (success) {
|
|
Dart_Handle out = result.stdout_data();
|
|
ThrowIfError(out);
|
|
Dart_Handle err = result.stderr_data();
|
|
ThrowIfError(err);
|
|
Dart_Handle list = Dart_NewList(4);
|
|
Dart_ListSetAt(list, 0, Dart_NewInteger(pid));
|
|
Dart_ListSetAt(list, 1, Dart_NewInteger(result.exit_code()));
|
|
Dart_ListSetAt(list, 2, out);
|
|
Dart_ListSetAt(list, 3, err);
|
|
Dart_SetReturnValue(args, list);
|
|
} else {
|
|
Dart_Handle error = DartUtils::NewDartOSError();
|
|
Process::Kill(pid, 9);
|
|
Dart_ThrowException(error);
|
|
}
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_KillPid)(Dart_NativeArguments args) {
|
|
intptr_t pid = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
|
|
intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
|
|
bool success = Process::Kill(pid, signal);
|
|
Dart_SetBooleanReturnValue(args, success);
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_Exit)(Dart_NativeArguments args) {
|
|
int64_t status = 0;
|
|
// Ignore result if passing invalid argument and just exit 0.
|
|
DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &status);
|
|
Process::RunExitHook(status);
|
|
Dart_ExitIsolate();
|
|
// We're not doing a full VM shutdown with Dart_Cleanup, which might block,
|
|
// and other VM threads may be accessing state with global destructors, so
|
|
// we skip global destructors by using _exit instead of exit.
|
|
Platform::_Exit(static_cast<int>(status));
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_SetExitCode)(Dart_NativeArguments args) {
|
|
int64_t status = 0;
|
|
// Ignore result if passing invalid argument and just set exit code to 0.
|
|
DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &status);
|
|
Process::SetGlobalExitCode(status);
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_GetExitCode)(Dart_NativeArguments args) {
|
|
Dart_SetIntegerReturnValue(args, Process::GlobalExitCode());
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_Sleep)(Dart_NativeArguments args) {
|
|
ScopedBlockingCall blocker;
|
|
int64_t milliseconds = 0;
|
|
// Ignore result if passing invalid argument and just set exit code to 0.
|
|
DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &milliseconds);
|
|
TimerUtils::Sleep(milliseconds);
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_Pid)(Dart_NativeArguments args) {
|
|
// Ignore result if passing invalid argument and just set exit code to 0.
|
|
intptr_t pid = -1;
|
|
Dart_Handle process = Dart_GetNativeArgument(args, 0);
|
|
if (Dart_IsNull(process)) {
|
|
pid = Process::CurrentProcessId();
|
|
} else {
|
|
Process::GetProcessIdNativeField(process, &pid);
|
|
}
|
|
Dart_SetIntegerReturnValue(args, pid);
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_SetSignalHandler)(Dart_NativeArguments args) {
|
|
intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
|
|
intptr_t id = Process::SetSignalHandler(signal);
|
|
if (id == -1) {
|
|
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
|
} else {
|
|
Dart_SetIntegerReturnValue(args, id);
|
|
}
|
|
}
|
|
|
|
void FUNCTION_NAME(Process_ClearSignalHandler)(Dart_NativeArguments args) {
|
|
intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
|
|
Process::ClearSignalHandler(signal, Dart_GetMainPortId());
|
|
}
|
|
|
|
Dart_Handle Process::GetProcessIdNativeField(Dart_Handle process,
|
|
intptr_t* pid) {
|
|
return Dart_GetNativeInstanceField(process, kProcessIdNativeField, pid);
|
|
}
|
|
|
|
Dart_Handle Process::SetProcessIdNativeField(Dart_Handle process,
|
|
intptr_t pid) {
|
|
return Dart_SetNativeInstanceField(process, kProcessIdNativeField, pid);
|
|
}
|
|
|
|
void FUNCTION_NAME(SystemEncodingToString)(Dart_NativeArguments args) {
|
|
Dart_Handle bytes = Dart_GetNativeArgument(args, 0);
|
|
intptr_t bytes_length = 0;
|
|
Dart_Handle result = Dart_ListLength(bytes, &bytes_length);
|
|
ThrowIfError(result);
|
|
uint8_t* buffer = Dart_ScopeAllocate(bytes_length + 1);
|
|
result = Dart_ListGetAsBytes(bytes, 0, buffer, bytes_length);
|
|
buffer[bytes_length] = '\0';
|
|
ThrowIfError(result);
|
|
intptr_t len;
|
|
char* str = StringUtils::ConsoleStringToUtf8(reinterpret_cast<char*>(buffer),
|
|
bytes_length, &len);
|
|
if (str == nullptr) {
|
|
Dart_ThrowException(DartUtils::NewDartUnsupportedError(
|
|
"SystemEncodingToString not supported on this operating system"));
|
|
}
|
|
result = Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(str), len);
|
|
ThrowIfError(result);
|
|
Dart_SetReturnValue(args, result);
|
|
}
|
|
|
|
void FUNCTION_NAME(StringToSystemEncoding)(Dart_NativeArguments args) {
|
|
Dart_Handle str = Dart_GetNativeArgument(args, 0);
|
|
char* utf8;
|
|
intptr_t utf8_len;
|
|
Dart_Handle result =
|
|
Dart_StringToUTF8(str, reinterpret_cast<uint8_t**>(&utf8), &utf8_len);
|
|
ThrowIfError(result);
|
|
intptr_t system_len;
|
|
const char* system_string =
|
|
StringUtils::Utf8ToConsoleString(utf8, utf8_len, &system_len);
|
|
if (system_string == nullptr) {
|
|
Dart_ThrowException(DartUtils::NewDartUnsupportedError(
|
|
"StringToSystemEncoding not supported on this operating system"));
|
|
}
|
|
uint8_t* buffer = nullptr;
|
|
Dart_Handle external_array = IOBuffer::Allocate(system_len, &buffer);
|
|
if (Dart_IsNull(external_array)) {
|
|
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
|
return;
|
|
}
|
|
if (!Dart_IsError(external_array)) {
|
|
memmove(buffer, system_string, system_len);
|
|
}
|
|
Dart_SetReturnValue(args, external_array);
|
|
}
|
|
|
|
void FUNCTION_NAME(ProcessInfo_CurrentRSS)(Dart_NativeArguments args) {
|
|
int64_t current_rss = Process::CurrentRSS();
|
|
if (current_rss < 0) {
|
|
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
|
return;
|
|
}
|
|
Dart_SetIntegerReturnValue(args, current_rss);
|
|
}
|
|
|
|
void FUNCTION_NAME(ProcessInfo_MaxRSS)(Dart_NativeArguments args) {
|
|
int64_t max_rss = Process::MaxRSS();
|
|
if (max_rss < 0) {
|
|
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
|
return;
|
|
}
|
|
Dart_SetIntegerReturnValue(args, max_rss);
|
|
}
|
|
|
|
void Process::GetRSSInformation(int64_t* max_rss, int64_t* current_rss) {
|
|
ASSERT(max_rss != nullptr);
|
|
ASSERT(current_rss != nullptr);
|
|
// Max RSS should be queried after current RSS to produce
|
|
// consistent values as current RSS can grow beyond max RSS which
|
|
// was queried before.
|
|
*current_rss = Process::CurrentRSS();
|
|
*max_rss = Process::MaxRSS();
|
|
}
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|