dart-sdk/runtime/bin/process.cc
Zachary Anderson 89dba57bcf [dart:io] Adds a finalizer to _NativeSocket to avoid socket leaks
The finalizer sends the "close" message to the EventHandler for the
file descriptor in the _NativeSocket's native field. To avoid races and
spurious messages, this CL stores a pointer to a wrapper object in the
native field instead of the file descriptor. All messsages about the
_NativeSocket sent to the EventHandler use the wrapper object instead of
the file descriptor. When the EventHandler closes the file, the file
descriptor in the wrapper object is set to -1 so that the finalizer will
instead do nothing.

On Windows, there is another level of indirection since the OS HANDLEs
were already wrapped in various kinds of Handle objects. As an additional
complication, ClientSocket close on Windows is asynchronous, so the
EventHandler may shutdown before all of the ClientSocket Handles can be
destroyed.

related #27898, #28081

R=johnmccutchan@google.com

Review-Url: https://codereview.chromium.org/2760293002 .
2017-03-28 07:44:05 -07:00

369 lines
13 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.
#if !defined(DART_IO_DISABLED)
#include "bin/process.h"
#include "bin/dartutils.h"
#include "bin/io_buffer.h"
#include "bin/log.h"
#include "bin/platform.h"
#include "bin/socket.h"
#include "bin/utils.h"
#include "include/dart_api.h"
namespace dart {
namespace bin {
static const 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 const intptr_t kMaxArgumentListLength = 1024 * 1024;
ASSERT(Dart_IsList(strings));
intptr_t len = 0;
Dart_Handle result = Dart_ListLength(strings, &len);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
// Protect against user-defined list implementations that can have
// arbitrary length.
if ((len < 0) || (len > kMaxArgumentListLength)) {
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result = DartUtils::SetStringField(status_handle, "_errorMessage",
"Max argument list length exceeded");
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
return NULL;
}
*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);
if (Dart_IsError(arg)) {
Dart_PropagateError(arg);
}
if (!Dart_IsString(arg)) {
result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result =
DartUtils::SetStringField(status_handle, "_errorMessage", error_msg);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
return NULL;
}
string_args[i] = const_cast<char*>(DartUtils::GetStringValue(arg));
}
return string_args;
}
void Process::ClearAllSignalHandlers() {
for (intptr_t i = 1; i <= kLastSignal; i++) {
ClearSignalHandler(i);
}
}
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;
Dart_Handle result;
Dart_Handle status_handle = Dart_GetNativeArgument(args, 10);
Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
// 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);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result = DartUtils::SetStringField(status_handle, "_errorMessage",
"Path must be a builtin string");
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
Dart_SetReturnValue(args, Dart_NewBoolean(false));
return;
}
const char* path = DartUtils::GetStringValue(path_handle);
Dart_Handle arguments = Dart_GetNativeArgument(args, 2);
intptr_t args_length = 0;
char** string_args =
ExtractCStringList(arguments, status_handle,
"Arguments must be builtin strings", &args_length);
if (string_args == NULL) {
Dart_SetReturnValue(args, Dart_NewBoolean(false));
return;
}
Dart_Handle working_directory_handle = Dart_GetNativeArgument(args, 3);
// Defaults to the current working directoy.
const char* working_directory = NULL;
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);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result =
DartUtils::SetStringField(status_handle, "_errorMessage",
"WorkingDirectory must be a builtin string");
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
Dart_SetReturnValue(args, Dart_NewBoolean(false));
return;
}
Dart_Handle environment = Dart_GetNativeArgument(args, 4);
intptr_t environment_length = 0;
char** string_environment = NULL;
if (!Dart_IsNull(environment)) {
string_environment = ExtractCStringList(
environment, status_handle,
"Environment values must be builtin strings", &environment_length);
if (string_environment == NULL) {
Dart_SetReturnValue(args, Dart_NewBoolean(false));
return;
}
}
int64_t mode =
DartUtils::GetInt64ValueCheckRange(Dart_GetNativeArgument(args, 5), 0, 2);
Dart_Handle stdin_handle = Dart_GetNativeArgument(args, 6);
Dart_Handle stdout_handle = Dart_GetNativeArgument(args, 7);
Dart_Handle stderr_handle = Dart_GetNativeArgument(args, 8);
Dart_Handle exit_handle = Dart_GetNativeArgument(args, 9);
intptr_t pid = -1;
char* os_error_message = NULL; // Scope allocated by Process::Start.
int error_code = Process::Start(
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 (mode != kDetached) {
Socket::SetSocketIdNativeField(stdin_handle, process_stdin, false);
Socket::SetSocketIdNativeField(stdout_handle, process_stdout, false);
Socket::SetSocketIdNativeField(stderr_handle, process_stderr, false);
}
if (mode == kNormal) {
Socket::SetSocketIdNativeField(exit_handle, exit_event, false);
}
Process::SetProcessIdNativeField(process, pid);
} else {
result =
DartUtils::SetIntegerField(status_handle, "_errorCode", error_code);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result = DartUtils::SetStringField(status_handle, "_errorMessage",
os_error_message != NULL
? os_error_message
: "Cannot get error message");
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
}
Dart_SetReturnValue(args, Dart_NewBoolean(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->SetClosedFd();
process_stdout->SetClosedFd();
process_stderr->SetClosedFd();
exit_event->SetClosedFd();
if (success) {
Dart_Handle out = result.stdout_data();
if (Dart_IsError(out)) {
Dart_PropagateError(out);
}
Dart_Handle err = result.stderr_data();
if (Dart_IsError(err)) {
Dart_PropagateError(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_SetReturnValue(args, Dart_NewBoolean(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();
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_SetReturnValue(args, Dart_NewInteger(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_SetReturnValue(args, Dart_NewInteger(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_SetReturnValue(args, Dart_NewInteger(id));
}
}
void FUNCTION_NAME(Process_ClearSignalHandler)(Dart_NativeArguments args) {
intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
Process::ClearSignalHandler(signal);
}
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);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
uint8_t* buffer = Dart_ScopeAllocate(bytes_length + 1);
result = Dart_ListGetAsBytes(bytes, 0, buffer, bytes_length);
buffer[bytes_length] = '\0';
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
intptr_t len;
char* str = StringUtils::ConsoleStringToUtf8(reinterpret_cast<char*>(buffer),
bytes_length, &len);
if (str == NULL) {
Dart_ThrowException(
DartUtils::NewInternalError("SystemEncodingToString failed"));
}
result = Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(str), len);
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);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
intptr_t system_len;
const char* system_string =
StringUtils::Utf8ToConsoleString(utf8, utf8_len, &system_len);
if (system_string == NULL) {
Dart_ThrowException(
DartUtils::NewInternalError("StringToSystemEncoding failed"));
}
uint8_t* buffer = NULL;
Dart_Handle external_array = IOBuffer::Allocate(system_len, &buffer);
if (!Dart_IsError(external_array)) {
memmove(buffer, system_string, system_len);
}
Dart_SetReturnValue(args, external_array);
}
} // namespace bin
} // namespace dart
#endif // !defined(DART_IO_DISABLED)