| // 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 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); |
| 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 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); |
| 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 NULL; |
| } |
| 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); |
| // The Dart code verifies that the path implements the String |
| // interface. However, only builtin Strings are handled by |
| // GetStringValue. |
| Dart_Handle result; |
| 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 == NULL) { |
| Dart_SetBooleanReturnValue(args, false); |
| return; |
| } |
| Dart_Handle working_directory_handle = Dart_GetNativeArgument(args, 4); |
| // 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); |
| 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 = NULL; |
| if (!Dart_IsNull(environment)) { |
| string_environment = ExtractCStringList( |
| environment, status_handle, |
| "Environment values must be builtin strings", &environment_length); |
| if (string_environment == NULL) { |
| 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 = NULL; // 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 != NULL) |
| ? 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(); |
| 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 == NULL) { |
| 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 == NULL) { |
| Dart_ThrowException(DartUtils::NewDartUnsupportedError( |
| "StringToSystemEncoding not supported on this operating system")); |
| } |
| uint8_t* buffer = NULL; |
| 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 != NULL); |
| ASSERT(current_rss != NULL); |
| // 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 |