OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
| 5 #include <process.h> |
| 6 |
5 #include "bin/builtin.h" | 7 #include "bin/builtin.h" |
6 #include "bin/globals.h" | 8 #include "bin/globals.h" |
7 #include "bin/process.h" | 9 #include "bin/process.h" |
| 10 #include "bin/eventhandler.h" |
| 11 |
| 12 static const int kReadHandle = 0; |
| 13 static const int kWriteHandle = 1; |
| 14 |
| 15 class ProcessInfo { |
| 16 public: |
| 17 ProcessInfo(DWORD process_id, HANDLE process_handle, HANDLE exit_pipe) |
| 18 : process_id_(process_id), |
| 19 process_handle_(process_handle), |
| 20 exit_pipe_(exit_pipe) { } |
| 21 |
| 22 intptr_t pid() { return process_id_; } |
| 23 HANDLE process_handle() { return process_handle_; } |
| 24 HANDLE exit_pipe() { return exit_pipe_; } |
| 25 ProcessInfo* next() { return next_; } |
| 26 void set_next(ProcessInfo* next) { next_ = next; } |
| 27 |
| 28 private: |
| 29 DWORD process_id_; // Process id. |
| 30 HANDLE process_handle_; // Process handle. |
| 31 HANDLE exit_pipe_; // File descriptor for pipe to report exit code. |
| 32 ProcessInfo* next_; |
| 33 }; |
| 34 |
| 35 |
| 36 ProcessInfo* active_processes = NULL; |
| 37 |
| 38 |
| 39 static void AddProcess(ProcessInfo* process) { |
| 40 process->set_next(active_processes); |
| 41 active_processes = process; |
| 42 } |
| 43 |
| 44 |
| 45 static ProcessInfo* LookupProcess(intptr_t pid) { |
| 46 ProcessInfo* current = active_processes; |
| 47 while (current != NULL) { |
| 48 if (current->pid() == pid) { |
| 49 return current; |
| 50 } |
| 51 current = current->next(); |
| 52 } |
| 53 return NULL; |
| 54 } |
| 55 |
| 56 |
| 57 static void RemoveProcess(intptr_t pid) { |
| 58 ProcessInfo* prev = NULL; |
| 59 ProcessInfo* current = active_processes; |
| 60 while (current != NULL) { |
| 61 if (current->pid() == pid) { |
| 62 if (prev == NULL) { |
| 63 active_processes = current->next(); |
| 64 } else { |
| 65 prev->set_next(current->next()); |
| 66 } |
| 67 delete current; |
| 68 return; |
| 69 } |
| 70 prev = current; |
| 71 current = current->next(); |
| 72 } |
| 73 } |
| 74 |
| 75 |
| 76 // Create a pipe for communicating with a new process. The handles array |
| 77 // will contain the read and write ends of the pipe. Based on the is_input |
| 78 // argument either the read or the write end of the handle will be |
| 79 // non-inherited. |
| 80 static bool CreateProcessPipe(HANDLE handles[2], |
| 81 char* pipe_name, |
| 82 bool is_input) { |
| 83 SECURITY_ATTRIBUTES security_attributes; |
| 84 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 85 security_attributes.bInheritHandle = TRUE; |
| 86 security_attributes.lpSecurityDescriptor = NULL; |
| 87 if (is_input) { |
| 88 handles[kWriteHandle] = |
| 89 CreateNamedPipe(pipe_name, |
| 90 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, |
| 91 PIPE_TYPE_BYTE | PIPE_WAIT, |
| 92 1, // Number of pipes |
| 93 1024, // Out buffer size |
| 94 1024, // In buffer size |
| 95 0, // Timeout in ms |
| 96 NULL); |
| 97 |
| 98 if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) { |
| 99 fprintf(stderr, "CreateNamedPipe failed %d\n", GetLastError()); |
| 100 return false; |
| 101 } |
| 102 |
| 103 handles[kReadHandle] = |
| 104 CreateFile(pipe_name, |
| 105 GENERIC_READ, |
| 106 0, |
| 107 &security_attributes, |
| 108 OPEN_EXISTING, |
| 109 FILE_READ_ATTRIBUTES | FILE_FLAG_OVERLAPPED, |
| 110 NULL); |
| 111 if (handles[kReadHandle] == INVALID_HANDLE_VALUE) { |
| 112 fprintf(stderr, "CreateFile failed %d\n", GetLastError()); |
| 113 return false; |
| 114 } |
| 115 } else { |
| 116 handles[kReadHandle] = |
| 117 CreateNamedPipe(pipe_name, |
| 118 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, |
| 119 PIPE_TYPE_BYTE | PIPE_WAIT, |
| 120 1, // Number of pipes |
| 121 1024, // Out buffer size |
| 122 1024, // In buffer size |
| 123 0, // Timeout in ms |
| 124 NULL); |
| 125 |
| 126 if (handles[kReadHandle] == INVALID_HANDLE_VALUE) { |
| 127 fprintf(stderr, "CreateNamedPipe failed %d\n", GetLastError()); |
| 128 return false; |
| 129 } |
| 130 |
| 131 handles[kWriteHandle] = |
| 132 CreateFile(pipe_name, |
| 133 GENERIC_WRITE, |
| 134 0, |
| 135 &security_attributes, |
| 136 OPEN_EXISTING, |
| 137 FILE_WRITE_ATTRIBUTES | FILE_FLAG_OVERLAPPED, |
| 138 NULL); |
| 139 if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) { |
| 140 fprintf(stderr, "CreateFile failed %d\n", GetLastError()); |
| 141 return false; |
| 142 } |
| 143 } |
| 144 return true; |
| 145 } |
| 146 |
| 147 |
| 148 static void CloseProcessPipe(HANDLE handles[2]) { |
| 149 for (int i = kReadHandle; i < kWriteHandle; i++) { |
| 150 if (handles[i] != INVALID_HANDLE_VALUE) { |
| 151 if (!CloseHandle(handles[i])) { |
| 152 fprintf(stderr, "CloseHandle failed %d\n", GetLastError()); |
| 153 } |
| 154 handles[i] = INVALID_HANDLE_VALUE; |
| 155 } |
| 156 } |
| 157 } |
| 158 |
| 159 |
| 160 static int SetOsErrorMessage(char* os_error_message, |
| 161 int os_error_message_len) { |
| 162 int error_code = GetLastError(); |
| 163 // TODO(sgjesse): Use FormatMessage to get the error message. |
| 164 char message[80]; |
| 165 snprintf(os_error_message, os_error_message_len, "OS Error %d", error_code); |
| 166 return error_code; |
| 167 } |
| 168 |
| 169 |
| 170 static unsigned int __stdcall TerminationWaitThread(void* args) { |
| 171 ProcessInfo* process = reinterpret_cast<ProcessInfo*>(args); |
| 172 WaitForSingleObject(process->process_handle(), INFINITE); |
| 173 DWORD exit_code; |
| 174 BOOL ok = GetExitCodeProcess(process->process_handle(), &exit_code); |
| 175 if (!ok) { |
| 176 fprintf(stderr, "GetExitCodeProcess failed %d\n", GetLastError()); |
| 177 } |
| 178 intptr_t message[2] = { process->pid(), static_cast<intptr_t>(exit_code) }; |
| 179 DWORD written; |
| 180 ok = WriteFile( |
| 181 process->exit_pipe(), message, sizeof(message), &written, NULL); |
| 182 if (!ok || written != 8) { |
| 183 fprintf(stderr, "FileWrite failed %d\n", GetLastError()); |
| 184 } |
| 185 return 0; |
| 186 } |
| 187 |
8 | 188 |
9 int Process::Start(const char* path, | 189 int Process::Start(const char* path, |
10 char* arguments[], | 190 char* arguments[], |
11 intptr_t arguments_length, | 191 intptr_t arguments_length, |
12 intptr_t* in, | 192 intptr_t* in, |
13 intptr_t* out, | 193 intptr_t* out, |
14 intptr_t* err, | 194 intptr_t* err, |
15 intptr_t* id, | 195 intptr_t* id, |
16 intptr_t* exit_event, | 196 intptr_t* exit_handler, |
17 char* os_error_message, | 197 char* os_error_message, |
18 int os_error_message_len) { | 198 int os_error_message_len) { |
| 199 HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; |
| 200 HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; |
| 201 HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; |
| 202 HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; |
| 203 // TODO(sgjesse): Find a better way of generating unique pipe names |
| 204 // (e.g. UUID). |
| 205 DWORD current_pid = GetCurrentProcessId(); |
| 206 static int pipe_id = 0; |
| 207 char pipe_name[80]; |
| 208 pipe_id++; |
| 209 snprintf(pipe_name, sizeof(pipe_name), |
| 210 "\\\\.\\Pipe\\dart%d_%d_1", current_pid, pipe_id); |
| 211 if (!CreateProcessPipe(stdin_handles, pipe_name, true)) { |
| 212 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); |
| 213 CloseProcessPipe(stdin_handles); |
| 214 return error_code; |
| 215 } |
| 216 snprintf(pipe_name, sizeof(pipe_name), |
| 217 "\\\\.\\Pipe\\dart%d_%d_2", current_pid, pipe_id); |
| 218 if (!CreateProcessPipe(stdout_handles, pipe_name, false)) { |
| 219 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); |
| 220 CloseProcessPipe(stdin_handles); |
| 221 CloseProcessPipe(stdout_handles); |
| 222 return error_code; |
| 223 } |
| 224 snprintf(pipe_name, sizeof(pipe_name), |
| 225 "\\\\.\\Pipe\\dart%d_%d_3", current_pid, pipe_id); |
| 226 if (!CreateProcessPipe(stderr_handles, pipe_name, false)) { |
| 227 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); |
| 228 CloseProcessPipe(stdin_handles); |
| 229 CloseProcessPipe(stdout_handles); |
| 230 CloseProcessPipe(stderr_handles); |
| 231 return error_code; |
| 232 } |
| 233 snprintf(pipe_name, sizeof(pipe_name), |
| 234 "\\\\.\\Pipe\\dart%d_%d_4", current_pid, pipe_id); |
| 235 if (!CreateProcessPipe(exit_handles, pipe_name, false)) { |
| 236 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); |
| 237 CloseProcessPipe(stdin_handles); |
| 238 CloseProcessPipe(stdout_handles); |
| 239 CloseProcessPipe(stderr_handles); |
| 240 CloseProcessPipe(exit_handles); |
| 241 return error_code; |
| 242 } |
| 243 |
19 // Setup info structures. | 244 // Setup info structures. |
20 STARTUPINFO startup_info; | 245 STARTUPINFO startup_info; |
21 PROCESS_INFORMATION process_info; | |
22 ZeroMemory(&startup_info, sizeof(startup_info)); | 246 ZeroMemory(&startup_info, sizeof(startup_info)); |
23 startup_info.cb = sizeof(startup_info); | 247 startup_info.cb = sizeof(startup_info); |
| 248 startup_info.hStdInput = stdin_handles[kReadHandle]; |
| 249 startup_info.hStdOutput = stdout_handles[kWriteHandle]; |
| 250 startup_info.hStdError = stderr_handles[kWriteHandle]; |
| 251 startup_info.dwFlags |= STARTF_USESTDHANDLES; |
| 252 |
| 253 PROCESS_INFORMATION process_info; |
24 ZeroMemory(&process_info, sizeof(process_info)); | 254 ZeroMemory(&process_info, sizeof(process_info)); |
25 | 255 |
26 // TODO(ager): Once sockets are implemented, use the supplied | |
27 // arguments as in, out and err in the startup info. | |
28 | |
29 // Compute command-line length. | 256 // Compute command-line length. |
30 int command_line_length = strlen(path); | 257 int command_line_length = strlen(path); |
31 for (int i = 0; i < arguments_length; i++) { | 258 for (int i = 0; i < arguments_length; i++) { |
32 command_line_length += strlen(arguments[i]); | 259 command_line_length += strlen(arguments[i]); |
33 } | 260 } |
34 // Account for two occurrences of '"' around the command, one | 261 // Account for two occurrences of '"' around the command, one |
35 // space per argument and a terminating '\0'. | 262 // space per argument and a terminating '\0'. |
36 command_line_length += 2 + arguments_length + 1; | 263 command_line_length += 2 + arguments_length + 1; |
37 static const int kMaxCommandLineLength = 32768; | 264 static const int kMaxCommandLineLength = 32768; |
38 if (command_line_length > kMaxCommandLineLength) { | 265 if (command_line_length > kMaxCommandLineLength) { |
39 return 1; | 266 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); |
| 267 CloseProcessPipe(stdin_handles); |
| 268 CloseProcessPipe(stdout_handles); |
| 269 CloseProcessPipe(stderr_handles); |
| 270 CloseProcessPipe(exit_handles); |
| 271 return error_code; |
40 } | 272 } |
41 | 273 |
42 // Put together command-line string. | 274 // Put together command-line string. |
43 char* command_line = new char[command_line_length]; | 275 char* command_line = new char[command_line_length]; |
44 int len = 0; | 276 int len = 0; |
45 int remaining = command_line_length; | 277 int remaining = command_line_length; |
46 int written = snprintf(command_line + len, remaining, "\"%s\"", path); | 278 int written = snprintf(command_line + len, remaining, "\"%s\"", path); |
47 len += written; | 279 len += written; |
48 remaining -= written; | 280 remaining -= written; |
49 ASSERT(remaining >= 0); | 281 ASSERT(remaining >= 0); |
50 for (int i = 0; i < arguments_length; i++) { | 282 for (int i = 0; i < arguments_length; i++) { |
51 written = snprintf(command_line + len, remaining, " %s", arguments[i]); | 283 written = snprintf(command_line + len, remaining, " %s", arguments[i]); |
52 len += written; | 284 len += written; |
53 remaining -= written; | 285 remaining -= written; |
54 ASSERT(remaining >= 0); | 286 ASSERT(remaining >= 0); |
55 } | 287 } |
56 | 288 |
57 // Create process. | 289 // Create process. |
58 BOOL result = CreateProcess(NULL, // ApplicationName | 290 BOOL result = CreateProcess(NULL, // ApplicationName |
59 command_line, | 291 command_line, |
60 NULL, // ProcessAttributes | 292 NULL, // ProcessAttributes |
61 NULL, // ThreadAttributes | 293 NULL, // ThreadAttributes |
62 FALSE, // InheritHandles | 294 TRUE, // InheritHandles |
63 0, // CreationFlags | 295 0, // CreationFlags |
64 NULL, // Environment | 296 NULL, // Environment |
65 NULL, // CurrentDirectory, | 297 NULL, // CurrentDirectory, |
66 &startup_info, | 298 &startup_info, |
67 &process_info); | 299 &process_info); |
68 | 300 |
69 // Deallocate command-line string. | 301 // Deallocate command-line string. |
70 delete[] command_line; | 302 delete[] command_line; |
71 | 303 |
72 if (result == 0) { | 304 if (result == 0) { |
73 return 1; | 305 int error_code = SetOsErrorMessage(os_error_message, os_error_message_len); |
| 306 CloseProcessPipe(stdin_handles); |
| 307 CloseProcessPipe(stdout_handles); |
| 308 CloseProcessPipe(stderr_handles); |
| 309 CloseProcessPipe(exit_handles); |
| 310 return error_code; |
74 } | 311 } |
75 | 312 |
76 // Return process handle. | 313 ProcessInfo* process = new ProcessInfo(process_info.dwProcessId, |
77 *id = reinterpret_cast<intptr_t>(process_info.hProcess); | 314 process_info.hProcess, |
| 315 exit_handles[kWriteHandle]); |
| 316 AddProcess(process); |
| 317 |
| 318 // TODO(sgjesse): Don't use a separate thread for waiting for each process to |
| 319 // terminate. |
| 320 uint32_t tid; |
| 321 uintptr_t thread_handle = |
| 322 _beginthreadex(NULL, 32 * 1024, TerminationWaitThread, process, 0, &tid); |
| 323 if (thread_handle == -1) { |
| 324 FATAL("Failed to start event handler thread"); |
| 325 } |
| 326 |
| 327 |
| 328 // Connect the three std streams. |
| 329 FileHandle* stdin_handle = new FileHandle(stdin_handles[kWriteHandle]); |
| 330 CloseHandle(stdin_handles[kReadHandle]); |
| 331 FileHandle* stdout_handle = new FileHandle(stdout_handles[kReadHandle]); |
| 332 CloseHandle(stdout_handles[kWriteHandle]); |
| 333 FileHandle* stderr_handle = new FileHandle(stderr_handles[kReadHandle]); |
| 334 CloseHandle(stderr_handles[kWriteHandle]); |
| 335 FileHandle* exit_handle = new FileHandle(exit_handles[kReadHandle]); |
| 336 *in = reinterpret_cast<intptr_t>(stdout_handle); |
| 337 *out = reinterpret_cast<intptr_t>(stdin_handle); |
| 338 *err = reinterpret_cast<intptr_t>(stderr_handle); |
| 339 *exit_handler = reinterpret_cast<intptr_t>(exit_handle); |
| 340 |
| 341 CloseHandle(process_info.hThread); |
| 342 |
| 343 // Return process id. |
| 344 *id = process->pid(); |
78 return 0; | 345 return 0; |
79 } | 346 } |
80 | 347 |
81 | 348 |
82 bool Process::Kill(intptr_t id) { | 349 bool Process::Kill(intptr_t id) { |
83 HANDLE process_handle = reinterpret_cast<HANDLE>(id); | 350 ProcessInfo* process = LookupProcess(id); |
84 BOOL result = TerminateProcess(process_handle, -1); | 351 ASSERT(process != NULL); |
85 if (result == 0) { | 352 if (process != NULL) { |
86 return false; | 353 BOOL result = TerminateProcess(process->process_handle(), -1); |
| 354 if (result == 0) { |
| 355 return false; |
| 356 } |
87 } | 357 } |
88 CloseHandle(process_handle); | |
89 return true; | 358 return true; |
90 } | 359 } |
91 | 360 |
92 | 361 |
93 void Process::Exit(intptr_t id) { | 362 void Process::Exit(intptr_t id) { |
| 363 RemoveProcess(id); |
94 } | 364 } |
OLD | NEW |