| // 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/dartutils.h" |
| #include "bin/dbg_connection.h" |
| #include "bin/eventhandler.h" |
| #include "bin/io_buffer.h" |
| #include "bin/log.h" |
| #include "bin/platform.h" |
| #include "bin/process.h" |
| #include "bin/socket.h" |
| #include "bin/utils.h" |
| |
| #include "include/dart_api.h" |
| |
| namespace dart { |
| namespace bin { |
| |
| // Global flag that is used to indicate that the VM should do a clean |
| // shutdown. |
| bool do_vm_shutdown = false; |
| |
| static const int kProcessIdNativeField = 0; |
| |
| int Process::global_exit_code_ = 0; |
| Mutex* Process::global_exit_code_mutex_ = new Mutex(); |
| |
| // 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 = new char*[len]; |
| for (int i = 0; i < len; i++) { |
| Dart_Handle arg = Dart_ListGetAt(strings, i); |
| if (Dart_IsError(arg)) { |
| delete[] string_args; |
| 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); |
| } |
| delete[] string_args; |
| return NULL; |
| } |
| string_args[i] = const_cast<char *>(DartUtils::GetStringValue(arg)); |
| } |
| return string_args; |
| } |
| |
| 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)) { |
| delete[] string_args; |
| 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) { |
| delete[] string_args; |
| 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; |
| |
| 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); |
| Socket::SetSocketIdNativeField(stdout_handle, process_stdout); |
| Socket::SetSocketIdNativeField(stderr_handle, process_stderr); |
| } |
| if (mode == kNormal) { |
| Socket::SetSocketIdNativeField(exit_handle, exit_event); |
| } |
| 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); |
| } |
| } |
| delete[] string_args; |
| delete[] string_environment; |
| free(os_error_message); |
| Dart_SetReturnValue(args, Dart_NewBoolean(error_code == 0)); |
| } |
| |
| |
| void FUNCTION_NAME(Process_Wait)(Dart_NativeArguments args) { |
| Dart_Handle process = Dart_GetNativeArgument(args, 0); |
| intptr_t process_stdin = |
| Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 1)); |
| intptr_t process_stdout = |
| Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 2)); |
| intptr_t process_stderr = |
| Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 3)); |
| intptr_t exit_event = |
| Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 4)); |
| ProcessResult result; |
| intptr_t pid; |
| Process::GetProcessIdNativeField(process, &pid); |
| if (Process::Wait(pid, |
| process_stdin, |
| process_stdout, |
| process_stderr, |
| exit_event, |
| &result)) { |
| 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); |
| if (Dart_IsError(error)) Dart_PropagateError(error); |
| 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); |
| Dart_ShutdownIsolate(); |
| Process::TerminateExitCodeHandler(); |
| char* error = Dart_Cleanup(); |
| if (error != NULL) { |
| Log::PrintErr("VM cleanup failed: %s\n", error); |
| free(error); |
| } |
| if (do_vm_shutdown) { |
| #ifdef LEGACY_DEBUG_PROTOCOL_ENABLED |
| // Note that this dependency crosses logical project boundaries by making |
| // the dart:io implementation depend upon the standalone VM's legacy debug |
| // protocol. This breaks projects which want to use our dart:io |
| // implementation. Because the protocol is going away shortly, it's |
| // reasonable to leave it behind a #ifdef that is only enabled for the |
| // standalone VM for now. |
| DebuggerConnectionHandler::StopHandler(); |
| #endif |
| EventHandler::Stop(); |
| } |
| 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 = |
| reinterpret_cast<uint8_t*>(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); |
| free(str); |
| if (Dart_IsError(result)) Dart_PropagateError(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); |
| 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)) { |
| free(const_cast<char*>(system_string)); |
| Dart_PropagateError(result); |
| } |
| memmove(buffer, system_string, system_len); |
| free(const_cast<char*>(system_string)); |
| Dart_SetReturnValue(args, external_array); |
| } |
| |
| } // namespace bin |
| } // namespace dart |