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 |