1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "bin/process.h"
6
7#include "bin/dartutils.h"
8#include "bin/io_buffer.h"
9#include "bin/namespace.h"
10#include "bin/platform.h"
11#include "bin/socket.h"
12#include "bin/utils.h"
13#include "platform/syslog.h"
14
15#include "include/dart_api.h"
16
17namespace dart {
18namespace bin {
19
20static const int kProcessIdNativeField = 0;
21
22// Extract an array of C strings from a list of Dart strings.
23static char** ExtractCStringList(Dart_Handle strings,
24 Dart_Handle status_handle,
25 const char* error_msg,
26 intptr_t* length) {
27 static const intptr_t kMaxArgumentListLength = 1024 * 1024;
28 ASSERT(Dart_IsList(strings));
29 intptr_t len = 0;
30 Dart_Handle result = Dart_ListLength(strings, &len);
31 ThrowIfError(result);
32 // Protect against user-defined list implementations that can have
33 // arbitrary length.
34 if ((len < 0) || (len > kMaxArgumentListLength)) {
35 result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
36 ThrowIfError(result);
37 result = DartUtils::SetStringField(status_handle, "_errorMessage",
38 "Max argument list length exceeded");
39 ThrowIfError(result);
40 return NULL;
41 }
42 *length = len;
43 char** string_args;
44 string_args =
45 reinterpret_cast<char**>(Dart_ScopeAllocate(len * sizeof(*string_args)));
46 for (int i = 0; i < len; i++) {
47 Dart_Handle arg = Dart_ListGetAt(strings, i);
48 ThrowIfError(arg);
49 if (!Dart_IsString(arg)) {
50 result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
51 ThrowIfError(result);
52 result =
53 DartUtils::SetStringField(status_handle, "_errorMessage", error_msg);
54 ThrowIfError(result);
55 return NULL;
56 }
57 string_args[i] = const_cast<char*>(DartUtils::GetStringValue(arg));
58 }
59 return string_args;
60}
61
62bool Process::ModeIsAttached(ProcessStartMode mode) {
63 return (mode == kNormal) || (mode == kInheritStdio);
64}
65
66bool Process::ModeHasStdio(ProcessStartMode mode) {
67 return (mode == kNormal) || (mode == kDetachedWithStdio);
68}
69
70void Process::ClearAllSignalHandlers() {
71 for (intptr_t i = 1; i <= kLastSignal; i++) {
72 ClearSignalHandler(i, ILLEGAL_PORT);
73 }
74}
75
76void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
77 Dart_Handle process = Dart_GetNativeArgument(args, 0);
78 intptr_t process_stdin;
79 intptr_t process_stdout;
80 intptr_t process_stderr;
81 intptr_t exit_event;
82 Namespace* namespc = Namespace::GetNamespace(args, 1);
83 Dart_Handle status_handle = Dart_GetNativeArgument(args, 11);
84 Dart_Handle path_handle = Dart_GetNativeArgument(args, 2);
85 // The Dart code verifies that the path implements the String
86 // interface. However, only builtin Strings are handled by
87 // GetStringValue.
88 Dart_Handle result;
89 if (!Dart_IsString(path_handle)) {
90 result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
91 ThrowIfError(result);
92 result = DartUtils::SetStringField(status_handle, "_errorMessage",
93 "Path must be a builtin string");
94 ThrowIfError(result);
95 Dart_SetBooleanReturnValue(args, false);
96 return;
97 }
98 const char* path = DartUtils::GetStringValue(path_handle);
99 Dart_Handle arguments = Dart_GetNativeArgument(args, 3);
100 intptr_t args_length = 0;
101 char** string_args =
102 ExtractCStringList(arguments, status_handle,
103 "Arguments must be builtin strings", &args_length);
104 if (string_args == NULL) {
105 Dart_SetBooleanReturnValue(args, false);
106 return;
107 }
108 Dart_Handle working_directory_handle = Dart_GetNativeArgument(args, 4);
109 // Defaults to the current working directoy.
110 const char* working_directory = NULL;
111 if (Dart_IsString(working_directory_handle)) {
112 working_directory = DartUtils::GetStringValue(working_directory_handle);
113 } else if (!Dart_IsNull(working_directory_handle)) {
114 result = DartUtils::SetIntegerField(status_handle, "_errorCode", 0);
115 ThrowIfError(result);
116 result =
117 DartUtils::SetStringField(status_handle, "_errorMessage",
118 "WorkingDirectory must be a builtin string");
119 ThrowIfError(result);
120 Dart_SetBooleanReturnValue(args, false);
121 return;
122 }
123 Dart_Handle environment = Dart_GetNativeArgument(args, 5);
124 intptr_t environment_length = 0;
125 char** string_environment = NULL;
126 if (!Dart_IsNull(environment)) {
127 string_environment = ExtractCStringList(
128 environment, status_handle,
129 "Environment values must be builtin strings", &environment_length);
130 if (string_environment == NULL) {
131 Dart_SetBooleanReturnValue(args, false);
132 return;
133 }
134 }
135 int64_t mode =
136 DartUtils::GetInt64ValueCheckRange(Dart_GetNativeArgument(args, 6), 0, 3);
137 Dart_Handle stdin_handle = Dart_GetNativeArgument(args, 7);
138 Dart_Handle stdout_handle = Dart_GetNativeArgument(args, 8);
139 Dart_Handle stderr_handle = Dart_GetNativeArgument(args, 9);
140 Dart_Handle exit_handle = Dart_GetNativeArgument(args, 10);
141 intptr_t pid = -1;
142 char* os_error_message = NULL; // Scope allocated by Process::Start.
143
144 int error_code = Process::Start(
145 namespc, path, string_args, args_length, working_directory,
146 string_environment, environment_length,
147 static_cast<ProcessStartMode>(mode), &process_stdout, &process_stdin,
148 &process_stderr, &pid, &exit_event, &os_error_message);
149 if (error_code == 0) {
150 if (Process::ModeHasStdio(static_cast<ProcessStartMode>(mode))) {
151 Socket::SetSocketIdNativeField(stdin_handle, process_stdin,
152 Socket::kFinalizerNormal);
153 Socket::SetSocketIdNativeField(stdout_handle, process_stdout,
154 Socket::kFinalizerNormal);
155 Socket::SetSocketIdNativeField(stderr_handle, process_stderr,
156 Socket::kFinalizerNormal);
157 }
158 if (Process::ModeIsAttached(static_cast<ProcessStartMode>(mode))) {
159 Socket::SetSocketIdNativeField(exit_handle, exit_event,
160 Socket::kFinalizerNormal);
161 }
162 Process::SetProcessIdNativeField(process, pid);
163 } else {
164 result =
165 DartUtils::SetIntegerField(status_handle, "_errorCode", error_code);
166 ThrowIfError(result);
167 Dart_Handle val = DartUtils::NewString(os_error_message != NULL
168 ? os_error_message
169 : "Cannot get error message");
170 if (Dart_IsError(val)) {
171 // If conversion of the OS error message to a Dart string fails, fall back
172 // on a stock message.
173 val = DartUtils::NewString("OS error message was a not a utf8 string.");
174 }
175 result = Dart_SetField(status_handle, DartUtils::NewString("_errorMessage"),
176 val);
177 ThrowIfError(result);
178 }
179 Dart_SetBooleanReturnValue(args, error_code == 0);
180}
181
182void FUNCTION_NAME(Process_Wait)(Dart_NativeArguments args) {
183 Dart_Handle process = Dart_GetNativeArgument(args, 0);
184 Socket* process_stdin =
185 Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 1));
186 Socket* process_stdout =
187 Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 2));
188 Socket* process_stderr =
189 Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 3));
190 Socket* exit_event =
191 Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 4));
192 ProcessResult result;
193 intptr_t pid;
194 Process::GetProcessIdNativeField(process, &pid);
195 bool success = Process::Wait(pid, process_stdin->fd(), process_stdout->fd(),
196 process_stderr->fd(), exit_event->fd(), &result);
197 // Process::Wait() closes the file handles, so blow away the fds in the
198 // Sockets so that they don't get picked up by the finalizer on _NativeSocket.
199 process_stdin->CloseFd();
200 process_stdout->CloseFd();
201 process_stderr->CloseFd();
202 exit_event->CloseFd();
203 if (success) {
204 Dart_Handle out = result.stdout_data();
205 ThrowIfError(out);
206 Dart_Handle err = result.stderr_data();
207 ThrowIfError(err);
208 Dart_Handle list = Dart_NewList(4);
209 Dart_ListSetAt(list, 0, Dart_NewInteger(pid));
210 Dart_ListSetAt(list, 1, Dart_NewInteger(result.exit_code()));
211 Dart_ListSetAt(list, 2, out);
212 Dart_ListSetAt(list, 3, err);
213 Dart_SetReturnValue(args, list);
214 } else {
215 Dart_Handle error = DartUtils::NewDartOSError();
216 Process::Kill(pid, 9);
217 Dart_ThrowException(error);
218 }
219}
220
221void FUNCTION_NAME(Process_KillPid)(Dart_NativeArguments args) {
222 intptr_t pid = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
223 intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
224 bool success = Process::Kill(pid, signal);
225 Dart_SetBooleanReturnValue(args, success);
226}
227
228void FUNCTION_NAME(Process_Exit)(Dart_NativeArguments args) {
229 int64_t status = 0;
230 // Ignore result if passing invalid argument and just exit 0.
231 DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &status);
232 Process::RunExitHook(status);
233 Dart_ExitIsolate();
234 Platform::Exit(static_cast<int>(status));
235}
236
237void FUNCTION_NAME(Process_SetExitCode)(Dart_NativeArguments args) {
238 int64_t status = 0;
239 // Ignore result if passing invalid argument and just set exit code to 0.
240 DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &status);
241 Process::SetGlobalExitCode(status);
242}
243
244void FUNCTION_NAME(Process_GetExitCode)(Dart_NativeArguments args) {
245 Dart_SetIntegerReturnValue(args, Process::GlobalExitCode());
246}
247
248void FUNCTION_NAME(Process_Sleep)(Dart_NativeArguments args) {
249 ScopedBlockingCall blocker;
250 int64_t milliseconds = 0;
251 // Ignore result if passing invalid argument and just set exit code to 0.
252 DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 0), &milliseconds);
253 TimerUtils::Sleep(milliseconds);
254}
255
256void FUNCTION_NAME(Process_Pid)(Dart_NativeArguments args) {
257 // Ignore result if passing invalid argument and just set exit code to 0.
258 intptr_t pid = -1;
259 Dart_Handle process = Dart_GetNativeArgument(args, 0);
260 if (Dart_IsNull(process)) {
261 pid = Process::CurrentProcessId();
262 } else {
263 Process::GetProcessIdNativeField(process, &pid);
264 }
265 Dart_SetIntegerReturnValue(args, pid);
266}
267
268void FUNCTION_NAME(Process_SetSignalHandler)(Dart_NativeArguments args) {
269 intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
270 intptr_t id = Process::SetSignalHandler(signal);
271 if (id == -1) {
272 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
273 } else {
274 Dart_SetIntegerReturnValue(args, id);
275 }
276}
277
278void FUNCTION_NAME(Process_ClearSignalHandler)(Dart_NativeArguments args) {
279 intptr_t signal = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0));
280 Process::ClearSignalHandler(signal, Dart_GetMainPortId());
281}
282
283Dart_Handle Process::GetProcessIdNativeField(Dart_Handle process,
284 intptr_t* pid) {
285 return Dart_GetNativeInstanceField(process, kProcessIdNativeField, pid);
286}
287
288Dart_Handle Process::SetProcessIdNativeField(Dart_Handle process,
289 intptr_t pid) {
290 return Dart_SetNativeInstanceField(process, kProcessIdNativeField, pid);
291}
292
293void FUNCTION_NAME(SystemEncodingToString)(Dart_NativeArguments args) {
294 Dart_Handle bytes = Dart_GetNativeArgument(args, 0);
295 intptr_t bytes_length = 0;
296 Dart_Handle result = Dart_ListLength(bytes, &bytes_length);
297 ThrowIfError(result);
298 uint8_t* buffer = Dart_ScopeAllocate(bytes_length + 1);
299 result = Dart_ListGetAsBytes(bytes, 0, buffer, bytes_length);
300 buffer[bytes_length] = '\0';
301 ThrowIfError(result);
302 intptr_t len;
303 char* str = StringUtils::ConsoleStringToUtf8(reinterpret_cast<char*>(buffer),
304 bytes_length, &len);
305 if (str == NULL) {
306 Dart_ThrowException(
307 DartUtils::NewInternalError("SystemEncodingToString failed"));
308 }
309 result = Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(str), len);
310 ThrowIfError(result);
311 Dart_SetReturnValue(args, result);
312}
313
314void FUNCTION_NAME(StringToSystemEncoding)(Dart_NativeArguments args) {
315 Dart_Handle str = Dart_GetNativeArgument(args, 0);
316 char* utf8;
317 intptr_t utf8_len;
318 Dart_Handle result =
319 Dart_StringToUTF8(str, reinterpret_cast<uint8_t**>(&utf8), &utf8_len);
320 ThrowIfError(result);
321 intptr_t system_len;
322 const char* system_string =
323 StringUtils::Utf8ToConsoleString(utf8, utf8_len, &system_len);
324 if (system_string == NULL) {
325 Dart_ThrowException(
326 DartUtils::NewInternalError("StringToSystemEncoding failed"));
327 }
328 uint8_t* buffer = NULL;
329 Dart_Handle external_array = IOBuffer::Allocate(system_len, &buffer);
330 if (Dart_IsNull(external_array)) {
331 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
332 return;
333 }
334 if (!Dart_IsError(external_array)) {
335 memmove(buffer, system_string, system_len);
336 }
337 Dart_SetReturnValue(args, external_array);
338}
339
340void FUNCTION_NAME(ProcessInfo_CurrentRSS)(Dart_NativeArguments args) {
341 int64_t current_rss = Process::CurrentRSS();
342 if (current_rss < 0) {
343 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
344 return;
345 }
346 Dart_SetIntegerReturnValue(args, current_rss);
347}
348
349void FUNCTION_NAME(ProcessInfo_MaxRSS)(Dart_NativeArguments args) {
350 int64_t max_rss = Process::MaxRSS();
351 if (max_rss < 0) {
352 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
353 return;
354 }
355 Dart_SetIntegerReturnValue(args, max_rss);
356}
357
358void Process::GetRSSInformation(int64_t* max_rss, int64_t* current_rss) {
359 ASSERT(max_rss != NULL);
360 ASSERT(current_rss != NULL);
361 *max_rss = Process::MaxRSS();
362 *current_rss = Process::CurrentRSS();
363}
364
365} // namespace bin
366} // namespace dart
367