Chromium Code Reviews| 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 the communicating with a new process. The handles array | |
|
Mads Ager (google)
2011/10/13 07:45:31
for the communicating -> for communicating
Søren Gjesse
2011/10/13 10:36:08
Done.
| |
| 77 // will contain the read and write ends of the pipe. Based on the is_input | |
| 78 // argument (seen from the new process) either the read or the write end of | |
|
Mads Ager (google)
2011/10/13 07:45:31
The parenthetical '(seen from the new process)' co
Søren Gjesse
2011/10/13 10:36:08
Done.
| |
| 79 // the handle will be 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 unsigned int __stdcall TerminationWaitThread(void* args) { | |
| 161 ProcessInfo* process = reinterpret_cast<ProcessInfo*>(args); | |
| 162 WaitForSingleObject(process->process_handle(), INFINITE); | |
| 163 DWORD exit_code; | |
| 164 BOOL ok = GetExitCodeProcess(process->process_handle(), &exit_code); | |
| 165 if (!ok) { | |
| 166 fprintf(stderr, "GetExitCodeProcess failed %d\n", GetLastError()); | |
| 167 } | |
| 168 intptr_t message[2] = { process->pid(), static_cast<intptr_t>(exit_code) }; | |
| 169 DWORD written; | |
| 170 ok = WriteFile( | |
| 171 process->exit_pipe(), message, sizeof(message), &written, NULL); | |
| 172 if (!ok || written != 8) { | |
| 173 fprintf(stderr, "FileWrite failed %d\n", GetLastError()); | |
| 174 } | |
| 175 return 0; | |
| 176 } | |
| 177 | |
| 8 | 178 |
| 9 int Process::Start(const char* path, | 179 int Process::Start(const char* path, |
| 10 char* arguments[], | 180 char* arguments[], |
| 11 intptr_t arguments_length, | 181 intptr_t arguments_length, |
| 12 intptr_t* in, | 182 intptr_t* in, |
| 13 intptr_t* out, | 183 intptr_t* out, |
| 14 intptr_t* err, | 184 intptr_t* err, |
| 15 intptr_t* id, | 185 intptr_t* id, |
| 16 intptr_t* exit_event, | 186 intptr_t* exit_handler, |
| 17 char* os_error_message, | 187 char* os_error_message, |
| 18 int os_error_message_len) { | 188 int os_error_message_len) { |
| 189 HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; | |
| 190 HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; | |
| 191 HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; | |
| 192 HANDLE exit_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; | |
| 193 // TODO(sgjesse): Find a better way of generating unique pipe names | |
| 194 // (e.g. UUID). | |
| 195 DWORD current_pid = GetCurrentProcessId(); | |
| 196 static int pipe_id = 0; | |
| 197 char pipe_name[80]; | |
| 198 pipe_id++; | |
| 199 snprintf(pipe_name, sizeof(pipe_name), | |
| 200 "\\\\.\\Pipe\\dart%d_%d_1", current_pid, pipe_id); | |
| 201 if (!CreateProcessPipe(stdin_handles, pipe_name, true)) goto cleanup; | |
|
Mads Ager (google)
2011/10/13 07:45:31
Let's create a cleanup method and call that and th
Søren Gjesse
2011/10/13 10:36:08
Done.
| |
| 202 snprintf(pipe_name, sizeof(pipe_name), | |
| 203 "\\\\.\\Pipe\\dart%d_%d_2", current_pid, pipe_id); | |
| 204 if (!CreateProcessPipe(stdout_handles, pipe_name, false)) goto cleanup; | |
| 205 snprintf(pipe_name, sizeof(pipe_name), | |
| 206 "\\\\.\\Pipe\\dart%d_%d_3", current_pid, pipe_id); | |
| 207 if (!CreateProcessPipe(stderr_handles, pipe_name, false)) goto cleanup; | |
| 208 snprintf(pipe_name, sizeof(pipe_name), | |
| 209 "\\\\.\\Pipe\\dart%d_%d_4", current_pid, pipe_id); | |
| 210 if (!CreateProcessPipe(exit_handles, pipe_name, false)) goto cleanup; | |
| 211 | |
| 19 // Setup info structures. | 212 // Setup info structures. |
| 20 STARTUPINFO startup_info; | 213 STARTUPINFO startup_info; |
| 21 PROCESS_INFORMATION process_info; | |
| 22 ZeroMemory(&startup_info, sizeof(startup_info)); | 214 ZeroMemory(&startup_info, sizeof(startup_info)); |
| 23 startup_info.cb = sizeof(startup_info); | 215 startup_info.cb = sizeof(startup_info); |
| 216 startup_info.hStdInput = stdin_handles[kReadHandle]; | |
| 217 startup_info.hStdOutput = stdout_handles[kWriteHandle]; | |
| 218 startup_info.hStdError = stderr_handles[kWriteHandle]; | |
| 219 startup_info.dwFlags |= STARTF_USESTDHANDLES; | |
| 220 | |
| 221 PROCESS_INFORMATION process_info; | |
| 24 ZeroMemory(&process_info, sizeof(process_info)); | 222 ZeroMemory(&process_info, sizeof(process_info)); |
| 25 | 223 |
| 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. | 224 // Compute command-line length. |
| 30 int command_line_length = strlen(path); | 225 int command_line_length = strlen(path); |
| 31 for (int i = 0; i < arguments_length; i++) { | 226 for (int i = 0; i < arguments_length; i++) { |
| 32 command_line_length += strlen(arguments[i]); | 227 command_line_length += strlen(arguments[i]); |
| 33 } | 228 } |
| 34 // Account for two occurrences of '"' around the command, one | 229 // Account for two occurrences of '"' around the command, one |
| 35 // space per argument and a terminating '\0'. | 230 // space per argument and a terminating '\0'. |
| 36 command_line_length += 2 + arguments_length + 1; | 231 command_line_length += 2 + arguments_length + 1; |
| 37 static const int kMaxCommandLineLength = 32768; | 232 static const int kMaxCommandLineLength = 32768; |
| 38 if (command_line_length > kMaxCommandLineLength) { | 233 if (command_line_length > kMaxCommandLineLength) { |
| 39 return 1; | 234 goto cleanup; |
| 40 } | 235 } |
| 41 | 236 |
| 42 // Put together command-line string. | 237 // Put together command-line string. |
| 43 char* command_line = new char[command_line_length]; | 238 char* command_line = new char[command_line_length]; |
| 44 int len = 0; | 239 int len = 0; |
| 45 int remaining = command_line_length; | 240 int remaining = command_line_length; |
| 46 int written = snprintf(command_line + len, remaining, "\"%s\"", path); | 241 int written = snprintf(command_line + len, remaining, "\"%s\"", path); |
| 47 len += written; | 242 len += written; |
| 48 remaining -= written; | 243 remaining -= written; |
| 49 ASSERT(remaining >= 0); | 244 ASSERT(remaining >= 0); |
| 50 for (int i = 0; i < arguments_length; i++) { | 245 for (int i = 0; i < arguments_length; i++) { |
| 51 written = snprintf(command_line + len, remaining, " %s", arguments[i]); | 246 written = snprintf(command_line + len, remaining, " %s", arguments[i]); |
| 52 len += written; | 247 len += written; |
| 53 remaining -= written; | 248 remaining -= written; |
| 54 ASSERT(remaining >= 0); | 249 ASSERT(remaining >= 0); |
| 55 } | 250 } |
| 56 | 251 |
| 57 // Create process. | 252 // Create process. |
| 58 BOOL result = CreateProcess(NULL, // ApplicationName | 253 BOOL result = CreateProcess(NULL, // ApplicationName |
| 59 command_line, | 254 command_line, |
| 60 NULL, // ProcessAttributes | 255 NULL, // ProcessAttributes |
| 61 NULL, // ThreadAttributes | 256 NULL, // ThreadAttributes |
| 62 FALSE, // InheritHandles | 257 TRUE, // InheritHandles |
| 63 0, // CreationFlags | 258 0, // CreationFlags |
| 64 NULL, // Environment | 259 NULL, // Environment |
| 65 NULL, // CurrentDirectory, | 260 NULL, // CurrentDirectory, |
| 66 &startup_info, | 261 &startup_info, |
| 67 &process_info); | 262 &process_info); |
| 68 | 263 |
| 69 // Deallocate command-line string. | 264 // Deallocate command-line string. |
| 70 delete[] command_line; | 265 delete[] command_line; |
| 71 | 266 |
| 72 if (result == 0) { | 267 if (result == 0) { |
| 73 return 1; | 268 goto cleanup; |
| 74 } | 269 } |
| 75 | 270 |
| 76 // Return process handle. | 271 ProcessInfo* process = new ProcessInfo(process_info.dwProcessId, |
| 77 *id = reinterpret_cast<intptr_t>(process_info.hProcess); | 272 process_info.hProcess, |
| 273 exit_handles[kWriteHandle]); | |
| 274 AddProcess(process); | |
| 275 | |
| 276 // TODO(sgjesse): Don't use a separate thread for waiting for each process to | |
| 277 // terminate. | |
| 278 uint32_t tid; | |
| 279 uintptr_t thread_handle = | |
| 280 _beginthreadex(NULL, 32 * 1024, TerminationWaitThread, process, 0, &tid); | |
| 281 if (thread_handle == -1) { | |
| 282 FATAL("Failed to start event handler thread"); | |
| 283 } | |
| 284 | |
| 285 | |
| 286 // Connect the three std streams. | |
| 287 FileHandle* stdin_handle = new FileHandle(stdin_handles[kWriteHandle]); | |
| 288 CloseHandle(stdin_handles[kReadHandle]); | |
| 289 FileHandle* stdout_handle = new FileHandle(stdout_handles[kReadHandle]); | |
| 290 CloseHandle(stdout_handles[kWriteHandle]); | |
| 291 FileHandle* stderr_handle = new FileHandle(stderr_handles[kReadHandle]); | |
| 292 CloseHandle(stderr_handles[kWriteHandle]); | |
| 293 FileHandle* exit_handle = new FileHandle(exit_handles[kReadHandle]); | |
| 294 *in = reinterpret_cast<intptr_t>(stdout_handle); | |
| 295 *out = reinterpret_cast<intptr_t>(stdin_handle); | |
| 296 *err = reinterpret_cast<intptr_t>(stderr_handle); | |
| 297 *exit_handler = reinterpret_cast<intptr_t>(exit_handle); | |
| 298 | |
| 299 CloseHandle(process_info.hThread); | |
| 300 | |
| 301 // Return process id. | |
| 302 *id = process->pid(); | |
| 78 return 0; | 303 return 0; |
| 304 | |
| 305 cleanup: | |
| 306 int error_code = GetLastError(); | |
| 307 // TODO(sgjesse): Use FormatMessage to get the error message. | |
| 308 char message[80]; | |
| 309 snprintf( | |
| 310 os_error_message, os_error_message_len, "OS Error %d", error_code); | |
| 311 CloseProcessPipe(stdin_handles); | |
| 312 CloseProcessPipe(stdout_handles); | |
| 313 CloseProcessPipe(stderr_handles); | |
| 314 CloseProcessPipe(exit_handles); | |
| 315 return error_code; | |
| 79 } | 316 } |
| 80 | 317 |
| 81 | 318 |
| 82 bool Process::Kill(intptr_t id) { | 319 bool Process::Kill(intptr_t id) { |
| 83 HANDLE process_handle = reinterpret_cast<HANDLE>(id); | 320 ProcessInfo* process = LookupProcess(id); |
| 84 BOOL result = TerminateProcess(process_handle, -1); | 321 ASSERT(process != NULL); |
| 85 if (result == 0) { | 322 if (process != NULL) { |
| 86 return false; | 323 BOOL result = TerminateProcess(process->process_handle(), -1); |
| 324 if (result == 0) { | |
| 325 return false; | |
| 326 } | |
| 87 } | 327 } |
| 88 CloseHandle(process_handle); | |
| 89 return true; | 328 return true; |
| 90 } | 329 } |
| 91 | 330 |
| 92 | 331 |
| 93 void Process::Exit(intptr_t id) { | 332 void Process::Exit(intptr_t id) { |
| 333 RemoveProcess(id); | |
| 94 } | 334 } |
| OLD | NEW |