1// Copyright (c) 2012, 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 "platform/globals.h"
6#if defined(HOST_OS_WINDOWS)
7
8#include "bin/process.h"
9
10#include <process.h> // NOLINT
11#include <psapi.h> // NOLINT
12#include <vector>
13
14#include "bin/builtin.h"
15#include "bin/dartutils.h"
16#include "bin/eventhandler.h"
17#include "bin/lockers.h"
18#include "bin/socket.h"
19#include "bin/thread.h"
20#include "bin/utils.h"
21#include "bin/utils_win.h"
22#include "platform/syslog.h"
23
24namespace dart {
25namespace bin {
26
27static const int kReadHandle = 0;
28static const int kWriteHandle = 1;
29
30int Process::global_exit_code_ = 0;
31Mutex* Process::global_exit_code_mutex_ = nullptr;
32Process::ExitHook Process::exit_hook_ = NULL;
33
34// ProcessInfo is used to map a process id to the process handle,
35// wait handle for registered exit code event and the pipe used to
36// communicate the exit code of the process to Dart.
37// ProcessInfo objects are kept in the static singly-linked
38// ProcessInfoList.
39class ProcessInfo {
40 public:
41 ProcessInfo(DWORD process_id,
42 HANDLE process_handle,
43 HANDLE wait_handle,
44 HANDLE exit_pipe)
45 : process_id_(process_id),
46 process_handle_(process_handle),
47 wait_handle_(wait_handle),
48 exit_pipe_(exit_pipe) {}
49
50 ~ProcessInfo() {
51 BOOL success = CloseHandle(process_handle_);
52 if (!success) {
53 FATAL("Failed to close process handle");
54 }
55 success = CloseHandle(exit_pipe_);
56 if (!success) {
57 FATAL("Failed to close process exit code pipe");
58 }
59 }
60
61 DWORD pid() { return process_id_; }
62 HANDLE process_handle() { return process_handle_; }
63 HANDLE wait_handle() { return wait_handle_; }
64 HANDLE exit_pipe() { return exit_pipe_; }
65 ProcessInfo* next() { return next_; }
66 void set_next(ProcessInfo* next) { next_ = next; }
67
68 private:
69 // Process id.
70 DWORD process_id_;
71 // Process handle.
72 HANDLE process_handle_;
73 // Wait handle identifying the exit-code wait operation registered
74 // with RegisterWaitForSingleObject.
75 HANDLE wait_handle_;
76 // File descriptor for pipe to report exit code.
77 HANDLE exit_pipe_;
78 // Link to next ProcessInfo object in the singly-linked list.
79 ProcessInfo* next_;
80
81 DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
82};
83
84// Singly-linked list of ProcessInfo objects for all active processes
85// started from Dart.
86class ProcessInfoList {
87 public:
88 static void Init();
89 static void Cleanup();
90
91 static void AddProcess(DWORD pid, HANDLE handle, HANDLE pipe) {
92 // Register a callback to extract the exit code, when the process
93 // is signaled. The callback runs in a independent thread from the OS pool.
94 // Because the callback depends on the process list containing
95 // the process, lock the mutex until the process is added to the list.
96 MutexLocker locker(mutex_);
97 HANDLE wait_handle = INVALID_HANDLE_VALUE;
98 BOOL success = RegisterWaitForSingleObject(
99 &wait_handle, handle, &ExitCodeCallback, reinterpret_cast<void*>(pid),
100 INFINITE, WT_EXECUTEONLYONCE);
101 if (!success) {
102 FATAL("Failed to register exit code wait operation.");
103 }
104 ProcessInfo* info = new ProcessInfo(pid, handle, wait_handle, pipe);
105 // Mutate the process list under the mutex.
106 info->set_next(active_processes_);
107 active_processes_ = info;
108 }
109
110 static bool LookupProcess(DWORD pid,
111 HANDLE* handle,
112 HANDLE* wait_handle,
113 HANDLE* pipe) {
114 MutexLocker locker(mutex_);
115 ProcessInfo* current = active_processes_;
116 while (current != NULL) {
117 if (current->pid() == pid) {
118 *handle = current->process_handle();
119 *wait_handle = current->wait_handle();
120 *pipe = current->exit_pipe();
121 return true;
122 }
123 current = current->next();
124 }
125 return false;
126 }
127
128 static void RemoveProcess(DWORD pid) {
129 MutexLocker locker(mutex_);
130 ProcessInfo* prev = NULL;
131 ProcessInfo* current = active_processes_;
132 while (current != NULL) {
133 if (current->pid() == pid) {
134 if (prev == NULL) {
135 active_processes_ = current->next();
136 } else {
137 prev->set_next(current->next());
138 }
139 delete current;
140 return;
141 }
142 prev = current;
143 current = current->next();
144 }
145 }
146
147 private:
148 // Callback called when an exit code is available from one of the
149 // processes in the list.
150 static void CALLBACK ExitCodeCallback(PVOID data, BOOLEAN timed_out) {
151 if (timed_out) {
152 return;
153 }
154 DWORD pid = reinterpret_cast<DWORD>(data);
155 HANDLE handle;
156 HANDLE wait_handle;
157 HANDLE exit_pipe;
158 bool success = LookupProcess(pid, &handle, &wait_handle, &exit_pipe);
159 if (!success) {
160 FATAL("Failed to lookup process in list of active processes");
161 }
162 // Unregister the event in a non-blocking way.
163 BOOL ok = UnregisterWait(wait_handle);
164 if (!ok && (GetLastError() != ERROR_IO_PENDING)) {
165 FATAL("Failed unregistering wait operation");
166 }
167 // Get and report the exit code to Dart.
168 int exit_code;
169 ok = GetExitCodeProcess(handle, reinterpret_cast<DWORD*>(&exit_code));
170 if (!ok) {
171 FATAL1("GetExitCodeProcess failed %d\n", GetLastError());
172 }
173 int negative = 0;
174 if (exit_code < 0) {
175 exit_code = abs(exit_code);
176 negative = 1;
177 }
178 int message[2] = {exit_code, negative};
179 DWORD written;
180 ok = WriteFile(exit_pipe, message, sizeof(message), &written, NULL);
181 // If the process has been closed, the read end of the exit
182 // pipe has been closed. It is therefore not a problem that
183 // WriteFile fails with a closed pipe error
184 // (ERROR_NO_DATA). Other errors should not happen.
185 if (ok && (written != sizeof(message))) {
186 FATAL("Failed to write entire process exit message");
187 } else if (!ok && (GetLastError() != ERROR_NO_DATA)) {
188 FATAL1("Failed to write exit code: %d", GetLastError());
189 }
190 // Remove the process from the list of active processes.
191 RemoveProcess(pid);
192 }
193
194 // Linked list of ProcessInfo objects for all active processes
195 // started from Dart code.
196 static ProcessInfo* active_processes_;
197 // Mutex protecting all accesses to the linked list of active
198 // processes.
199 static Mutex* mutex_;
200
201 DISALLOW_ALLOCATION();
202 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList);
203};
204
205ProcessInfo* ProcessInfoList::active_processes_ = NULL;
206Mutex* ProcessInfoList::mutex_ = nullptr;
207
208// Types of pipes to create.
209enum NamedPipeType { kInheritRead, kInheritWrite, kInheritNone };
210
211// Create a pipe for communicating with a new process. The handles array
212// will contain the read and write ends of the pipe. Based on the type
213// one of the handles will be inheritable.
214// NOTE: If this function returns false the handles might have been allocated
215// and the caller should make sure to close them in case of an error.
216static bool CreateProcessPipe(HANDLE handles[2],
217 wchar_t* pipe_name,
218 NamedPipeType type) {
219 // Security attributes describing an inheritable handle.
220 SECURITY_ATTRIBUTES inherit_handle;
221 inherit_handle.nLength = sizeof(SECURITY_ATTRIBUTES);
222 inherit_handle.bInheritHandle = TRUE;
223 inherit_handle.lpSecurityDescriptor = NULL;
224
225 if (type == kInheritRead) {
226 handles[kWriteHandle] =
227 CreateNamedPipeW(pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
228 PIPE_TYPE_BYTE | PIPE_WAIT,
229 1, // Number of pipes
230 1024, // Out buffer size
231 1024, // In buffer size
232 0, // Timeout in ms
233 NULL);
234
235 if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) {
236 Syslog::PrintErr("CreateNamedPipe failed %d\n", GetLastError());
237 return false;
238 }
239
240 handles[kReadHandle] =
241 CreateFileW(pipe_name, GENERIC_READ, 0, &inherit_handle, OPEN_EXISTING,
242 FILE_READ_ATTRIBUTES | FILE_FLAG_OVERLAPPED, NULL);
243 if (handles[kReadHandle] == INVALID_HANDLE_VALUE) {
244 Syslog::PrintErr("CreateFile failed %d\n", GetLastError());
245 return false;
246 }
247 } else {
248 ASSERT((type == kInheritWrite) || (type == kInheritNone));
249 handles[kReadHandle] =
250 CreateNamedPipeW(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
251 PIPE_TYPE_BYTE | PIPE_WAIT,
252 1, // Number of pipes
253 1024, // Out buffer size
254 1024, // In buffer size
255 0, // Timeout in ms
256 NULL);
257
258 if (handles[kReadHandle] == INVALID_HANDLE_VALUE) {
259 Syslog::PrintErr("CreateNamedPipe failed %d\n", GetLastError());
260 return false;
261 }
262
263 handles[kWriteHandle] = CreateFileW(
264 pipe_name, GENERIC_WRITE, 0,
265 (type == kInheritWrite) ? &inherit_handle : NULL, OPEN_EXISTING,
266 FILE_WRITE_ATTRIBUTES | FILE_FLAG_OVERLAPPED, NULL);
267 if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) {
268 Syslog::PrintErr("CreateFile failed %d\n", GetLastError());
269 return false;
270 }
271 }
272 return true;
273}
274
275static void CloseProcessPipe(HANDLE handles[2]) {
276 for (int i = kReadHandle; i < kWriteHandle; i++) {
277 if (handles[i] != INVALID_HANDLE_VALUE) {
278 if (!CloseHandle(handles[i])) {
279 Syslog::PrintErr("CloseHandle failed %d\n", GetLastError());
280 }
281 handles[i] = INVALID_HANDLE_VALUE;
282 }
283 }
284}
285
286static void CloseProcessPipes(HANDLE handles1[2],
287 HANDLE handles2[2],
288 HANDLE handles3[2],
289 HANDLE handles4[2]) {
290 CloseProcessPipe(handles1);
291 CloseProcessPipe(handles2);
292 CloseProcessPipe(handles3);
293 CloseProcessPipe(handles4);
294}
295
296static int SetOsErrorMessage(char** os_error_message) {
297 int error_code = GetLastError();
298 const int kMaxMessageLength = 256;
299 wchar_t message[kMaxMessageLength];
300 FormatMessageIntoBuffer(error_code, message, kMaxMessageLength);
301 *os_error_message = StringUtilsWin::WideToUtf8(message);
302 return error_code;
303}
304
305// Open an inheritable handle to NUL.
306static HANDLE OpenNul() {
307 SECURITY_ATTRIBUTES inherit_handle;
308 inherit_handle.nLength = sizeof(SECURITY_ATTRIBUTES);
309 inherit_handle.bInheritHandle = TRUE;
310 inherit_handle.lpSecurityDescriptor = NULL;
311 HANDLE nul = CreateFile(L"NUL", GENERIC_READ | GENERIC_WRITE, 0,
312 &inherit_handle, OPEN_EXISTING, 0, NULL);
313 if (nul == INVALID_HANDLE_VALUE) {
314 Syslog::PrintErr("CloseHandle failed %d\n", GetLastError());
315 }
316 return nul;
317}
318
319typedef BOOL(WINAPI* InitProcThreadAttrListFn)(LPPROC_THREAD_ATTRIBUTE_LIST,
320 DWORD,
321 DWORD,
322 PSIZE_T);
323
324typedef BOOL(WINAPI* UpdateProcThreadAttrFn)(LPPROC_THREAD_ATTRIBUTE_LIST,
325 DWORD,
326 DWORD_PTR,
327 PVOID,
328 SIZE_T,
329 PVOID,
330 PSIZE_T);
331
332typedef VOID(WINAPI* DeleteProcThreadAttrListFn)(LPPROC_THREAD_ATTRIBUTE_LIST);
333
334static InitProcThreadAttrListFn init_proc_thread_attr_list = NULL;
335static UpdateProcThreadAttrFn update_proc_thread_attr = NULL;
336static DeleteProcThreadAttrListFn delete_proc_thread_attr_list = NULL;
337
338static Mutex* initialized_mutex = nullptr;
339static bool load_attempted = false;
340
341static bool EnsureInitialized() {
342 HMODULE kernel32_module = GetModuleHandleW(L"kernel32.dll");
343 if (!load_attempted) {
344 MutexLocker locker(initialized_mutex);
345 if (load_attempted) {
346 return (delete_proc_thread_attr_list != NULL);
347 }
348 init_proc_thread_attr_list = reinterpret_cast<InitProcThreadAttrListFn>(
349 GetProcAddress(kernel32_module, "InitializeProcThreadAttributeList"));
350 update_proc_thread_attr = reinterpret_cast<UpdateProcThreadAttrFn>(
351 GetProcAddress(kernel32_module, "UpdateProcThreadAttribute"));
352 delete_proc_thread_attr_list = reinterpret_cast<DeleteProcThreadAttrListFn>(
353 GetProcAddress(kernel32_module, "DeleteProcThreadAttributeList"));
354 load_attempted = true;
355 return (delete_proc_thread_attr_list != NULL);
356 }
357 return (delete_proc_thread_attr_list != NULL);
358}
359
360const int kMaxPipeNameSize = 80;
361template <int Count>
362static int GenerateNames(wchar_t pipe_names[Count][kMaxPipeNameSize]) {
363 UUID uuid;
364 RPC_STATUS status = UuidCreateSequential(&uuid);
365 if ((status != RPC_S_OK) && (status != RPC_S_UUID_LOCAL_ONLY)) {
366 return status;
367 }
368 RPC_WSTR uuid_string;
369 status = UuidToStringW(&uuid, &uuid_string);
370 if (status != RPC_S_OK) {
371 return status;
372 }
373 for (int i = 0; i < Count; i++) {
374 static const wchar_t* prefix = L"\\\\.\\Pipe\\dart";
375 _snwprintf(pipe_names[i], kMaxPipeNameSize, L"%s_%s_%d", prefix,
376 uuid_string, i + 1);
377 }
378 status = RpcStringFreeW(&uuid_string);
379 if (status != RPC_S_OK) {
380 return status;
381 }
382 return 0;
383}
384
385class ProcessStarter {
386 public:
387 ProcessStarter(const char* path,
388 char* arguments[],
389 intptr_t arguments_length,
390 const char* working_directory,
391 char* environment[],
392 intptr_t environment_length,
393 ProcessStartMode mode,
394 intptr_t* in,
395 intptr_t* out,
396 intptr_t* err,
397 intptr_t* id,
398 intptr_t* exit_handler,
399 char** os_error_message)
400 : path_(path),
401 working_directory_(working_directory),
402 mode_(mode),
403 in_(in),
404 out_(out),
405 err_(err),
406 id_(id),
407 exit_handler_(exit_handler),
408 os_error_message_(os_error_message) {
409 stdin_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
410 stdin_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
411 stdout_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
412 stdout_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
413 stderr_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
414 stderr_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
415 exit_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
416 exit_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
417
418 // Transform input strings to system format.
419 const wchar_t* system_path = StringUtilsWin::Utf8ToWide(path_);
420 wchar_t** system_arguments;
421 system_arguments = reinterpret_cast<wchar_t**>(
422 Dart_ScopeAllocate(arguments_length * sizeof(*system_arguments)));
423 for (int i = 0; i < arguments_length; i++) {
424 system_arguments[i] = StringUtilsWin::Utf8ToWide(arguments[i]);
425 }
426
427 // Compute command-line length.
428 int command_line_length = wcslen(system_path);
429 for (int i = 0; i < arguments_length; i++) {
430 command_line_length += wcslen(system_arguments[i]);
431 }
432 // Account for null termination and one space per argument.
433 command_line_length += arguments_length + 1;
434
435 // Put together command-line string.
436 command_line_ = reinterpret_cast<wchar_t*>(
437 Dart_ScopeAllocate(command_line_length * sizeof(*command_line_)));
438 int len = 0;
439 int remaining = command_line_length;
440 int written =
441 _snwprintf(command_line_ + len, remaining, L"%s", system_path);
442 len += written;
443 remaining -= written;
444 ASSERT(remaining >= 0);
445 for (int i = 0; i < arguments_length; i++) {
446 written = _snwprintf(command_line_ + len, remaining, L" %s",
447 system_arguments[i]);
448 len += written;
449 remaining -= written;
450 ASSERT(remaining >= 0);
451 }
452
453 // Create environment block if an environment is supplied.
454 environment_block_ = NULL;
455 if (environment != NULL) {
456 wchar_t** system_environment;
457 system_environment = reinterpret_cast<wchar_t**>(
458 Dart_ScopeAllocate(environment_length * sizeof(*system_environment)));
459 // Convert environment strings to system strings.
460 for (intptr_t i = 0; i < environment_length; i++) {
461 system_environment[i] = StringUtilsWin::Utf8ToWide(environment[i]);
462 }
463
464 // An environment block is a sequence of zero-terminated strings
465 // followed by a block-terminating zero char.
466 intptr_t block_size = 1;
467 for (intptr_t i = 0; i < environment_length; i++) {
468 block_size += wcslen(system_environment[i]) + 1;
469 }
470 environment_block_ = reinterpret_cast<wchar_t*>(
471 Dart_ScopeAllocate(block_size * sizeof(*environment_block_)));
472 intptr_t block_index = 0;
473 for (intptr_t i = 0; i < environment_length; i++) {
474 intptr_t len = wcslen(system_environment[i]);
475 intptr_t result = _snwprintf(environment_block_ + block_index, len,
476 L"%s", system_environment[i]);
477 ASSERT(result == len);
478 block_index += len;
479 environment_block_[block_index++] = '\0';
480 }
481 // Block-terminating zero char.
482 environment_block_[block_index++] = '\0';
483 ASSERT(block_index == block_size);
484 }
485
486 system_working_directory_ = NULL;
487 if (working_directory_ != NULL) {
488 system_working_directory_ =
489 StringUtilsWin::Utf8ToWide(working_directory_);
490 }
491
492 attribute_list_ = NULL;
493 }
494
495 ~ProcessStarter() {
496 if (attribute_list_ != NULL) {
497 delete_proc_thread_attr_list(attribute_list_);
498 }
499 }
500
501 int Start() {
502 // Create pipes required.
503 int err = CreatePipes();
504 if (err != 0) {
505 return err;
506 }
507
508 // Setup info structures.
509 STARTUPINFOEXW startup_info;
510 ZeroMemory(&startup_info, sizeof(startup_info));
511 startup_info.StartupInfo.cb = sizeof(startup_info);
512 if (mode_ != kInheritStdio) {
513 startup_info.StartupInfo.hStdInput = stdin_handles_[kReadHandle];
514 startup_info.StartupInfo.hStdOutput = stdout_handles_[kWriteHandle];
515 startup_info.StartupInfo.hStdError = stderr_handles_[kWriteHandle];
516 startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
517
518 bool supports_proc_thread_attr_lists = EnsureInitialized();
519 if (supports_proc_thread_attr_lists) {
520 // Setup the handles to inherit. We only want to inherit the three
521 // handles for stdin, stdout and stderr.
522 SIZE_T size = 0;
523 // The call to determine the size of an attribute list always fails with
524 // ERROR_INSUFFICIENT_BUFFER and that error should be ignored.
525 if (!init_proc_thread_attr_list(NULL, 1, 0, &size) &&
526 (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
527 return CleanupAndReturnError();
528 }
529 attribute_list_ = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
530 Dart_ScopeAllocate(size));
531 ZeroMemory(attribute_list_, size);
532 if (!init_proc_thread_attr_list(attribute_list_, 1, 0, &size)) {
533 return CleanupAndReturnError();
534 }
535 inherited_handles_ = {stdin_handles_[kReadHandle],
536 stdout_handles_[kWriteHandle],
537 stderr_handles_[kWriteHandle]};
538 if (!update_proc_thread_attr(
539 attribute_list_, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
540 inherited_handles_.data(),
541 inherited_handles_.size() * sizeof(HANDLE), NULL, NULL)) {
542 return CleanupAndReturnError();
543 }
544 startup_info.lpAttributeList = attribute_list_;
545 }
546 }
547
548 PROCESS_INFORMATION process_info;
549 ZeroMemory(&process_info, sizeof(process_info));
550
551 // Create process.
552 DWORD creation_flags =
553 EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT;
554 if (!Process::ModeIsAttached(mode_)) {
555 creation_flags |= DETACHED_PROCESS;
556 }
557 BOOL result = CreateProcessW(
558 NULL, // ApplicationName
559 command_line_,
560 NULL, // ProcessAttributes
561 NULL, // ThreadAttributes
562 TRUE, // InheritHandles
563 creation_flags, environment_block_, system_working_directory_,
564 reinterpret_cast<STARTUPINFOW*>(&startup_info), &process_info);
565
566 if (result == 0) {
567 Syslog::PrintErr("CreateProcessW failed %d\n", GetLastError());
568 return CleanupAndReturnError();
569 }
570
571 if (mode_ != kInheritStdio) {
572 CloseHandle(stdin_handles_[kReadHandle]);
573 CloseHandle(stdout_handles_[kWriteHandle]);
574 CloseHandle(stderr_handles_[kWriteHandle]);
575 }
576 if (Process::ModeIsAttached(mode_)) {
577 ProcessInfoList::AddProcess(process_info.dwProcessId,
578 process_info.hProcess,
579 exit_handles_[kWriteHandle]);
580 }
581 if (mode_ != kDetached) {
582 // Connect the three stdio streams.
583 if (Process::ModeHasStdio(mode_)) {
584 FileHandle* stdin_handle = new FileHandle(stdin_handles_[kWriteHandle]);
585 FileHandle* stdout_handle =
586 new FileHandle(stdout_handles_[kReadHandle]);
587 FileHandle* stderr_handle =
588 new FileHandle(stderr_handles_[kReadHandle]);
589 *in_ = reinterpret_cast<intptr_t>(stdout_handle);
590 *out_ = reinterpret_cast<intptr_t>(stdin_handle);
591 *err_ = reinterpret_cast<intptr_t>(stderr_handle);
592 }
593 if (Process::ModeIsAttached(mode_)) {
594 FileHandle* exit_handle = new FileHandle(exit_handles_[kReadHandle]);
595 *exit_handler_ = reinterpret_cast<intptr_t>(exit_handle);
596 }
597 }
598
599 CloseHandle(process_info.hThread);
600
601 // Return process id.
602 *id_ = process_info.dwProcessId;
603 return 0;
604 }
605
606 int CreatePipes() {
607 // Generate unique pipe names for the four named pipes needed.
608 wchar_t pipe_names[4][kMaxPipeNameSize];
609 int status = GenerateNames<4>(pipe_names);
610 if (status != 0) {
611 SetOsErrorMessage(os_error_message_);
612 Syslog::PrintErr("UuidCreateSequential failed %d\n", status);
613 return status;
614 }
615
616 if (mode_ != kDetached) {
617 // Open pipes for stdin, stdout, stderr and for communicating the exit
618 // code.
619 if (Process::ModeHasStdio(mode_)) {
620 if (!CreateProcessPipe(stdin_handles_, pipe_names[0], kInheritRead) ||
621 !CreateProcessPipe(stdout_handles_, pipe_names[1], kInheritWrite) ||
622 !CreateProcessPipe(stderr_handles_, pipe_names[2], kInheritWrite)) {
623 return CleanupAndReturnError();
624 }
625 }
626 // Only open exit code pipe for non detached processes.
627 if (Process::ModeIsAttached(mode_)) {
628 if (!CreateProcessPipe(exit_handles_, pipe_names[3], kInheritNone)) {
629 return CleanupAndReturnError();
630 }
631 }
632 } else {
633 // Open NUL for stdin, stdout, and stderr.
634 stdin_handles_[kReadHandle] = OpenNul();
635 if (stdin_handles_[kReadHandle] == INVALID_HANDLE_VALUE) {
636 return CleanupAndReturnError();
637 }
638
639 stdout_handles_[kWriteHandle] = OpenNul();
640 if (stdout_handles_[kWriteHandle] == INVALID_HANDLE_VALUE) {
641 return CleanupAndReturnError();
642 }
643
644 stderr_handles_[kWriteHandle] = OpenNul();
645 if (stderr_handles_[kWriteHandle] == INVALID_HANDLE_VALUE) {
646 return CleanupAndReturnError();
647 }
648 }
649 return 0;
650 }
651
652 int CleanupAndReturnError() {
653 int error_code = SetOsErrorMessage(os_error_message_);
654 CloseProcessPipes(stdin_handles_, stdout_handles_, stderr_handles_,
655 exit_handles_);
656 return error_code;
657 }
658
659 HANDLE stdin_handles_[2];
660 HANDLE stdout_handles_[2];
661 HANDLE stderr_handles_[2];
662 HANDLE exit_handles_[2];
663
664 const wchar_t* system_working_directory_;
665 wchar_t* command_line_;
666 wchar_t* environment_block_;
667 std::vector<HANDLE> inherited_handles_;
668 LPPROC_THREAD_ATTRIBUTE_LIST attribute_list_;
669
670 const char* path_;
671 const char* working_directory_;
672 ProcessStartMode mode_;
673 intptr_t* in_;
674 intptr_t* out_;
675 intptr_t* err_;
676 intptr_t* id_;
677 intptr_t* exit_handler_;
678 char** os_error_message_;
679
680 private:
681 DISALLOW_ALLOCATION();
682 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
683};
684
685int Process::Start(Namespace* namespc,
686 const char* path,
687 char* arguments[],
688 intptr_t arguments_length,
689 const char* working_directory,
690 char* environment[],
691 intptr_t environment_length,
692 ProcessStartMode mode,
693 intptr_t* in,
694 intptr_t* out,
695 intptr_t* err,
696 intptr_t* id,
697 intptr_t* exit_handler,
698 char** os_error_message) {
699 ProcessStarter starter(path, arguments, arguments_length, working_directory,
700 environment, environment_length, mode, in, out, err,
701 id, exit_handler, os_error_message);
702 return starter.Start();
703}
704
705class BufferList : public BufferListBase {
706 public:
707 BufferList() : read_pending_(true) {}
708
709 // Indicate that data has been read into the buffer provided to
710 // overlapped read.
711 void DataIsRead(intptr_t size) {
712 ASSERT(read_pending_ == true);
713 set_data_size(data_size() + size);
714 set_free_size(free_size() - size);
715 ASSERT(free_size() >= 0);
716 read_pending_ = false;
717 }
718
719 // The access to the read buffer for overlapped read.
720 bool GetReadBuffer(uint8_t** buffer, intptr_t* size) {
721 ASSERT(!read_pending_);
722 if (free_size() == 0) {
723 if (!Allocate()) {
724 return false;
725 }
726 }
727 ASSERT(free_size() > 0);
728 ASSERT(free_size() <= kBufferSize);
729 *buffer = FreeSpaceAddress();
730 *size = free_size();
731 read_pending_ = true;
732 return true;
733 }
734
735 intptr_t GetDataSize() { return data_size(); }
736
737 uint8_t* GetFirstDataBuffer() {
738 ASSERT(head() != NULL);
739 ASSERT(head() == tail());
740 ASSERT(data_size() <= kBufferSize);
741 return head()->data();
742 }
743
744 void FreeDataBuffer() { Free(); }
745
746 private:
747 bool read_pending_;
748
749 DISALLOW_COPY_AND_ASSIGN(BufferList);
750};
751
752class OverlappedHandle {
753 public:
754 OverlappedHandle() {}
755
756 void Init(HANDLE handle, HANDLE event) {
757 handle_ = handle;
758 event_ = event;
759 ClearOverlapped();
760 }
761
762 bool HasEvent(HANDLE event) { return (event_ == event); }
763
764 bool Read() {
765 // Get the data read as a result of a completed overlapped operation.
766 if (overlapped_.InternalHigh > 0) {
767 buffer_.DataIsRead(overlapped_.InternalHigh);
768 } else {
769 buffer_.DataIsRead(0);
770 }
771
772 // Keep reading until error or pending operation.
773 while (true) {
774 ClearOverlapped();
775 uint8_t* buffer;
776 intptr_t buffer_size;
777 if (!buffer_.GetReadBuffer(&buffer, &buffer_size)) {
778 return false;
779 }
780 BOOL ok = ReadFile(handle_, buffer, buffer_size, NULL, &overlapped_);
781 if (!ok) {
782 return (GetLastError() == ERROR_IO_PENDING);
783 }
784 buffer_.DataIsRead(overlapped_.InternalHigh);
785 }
786 }
787
788 Dart_Handle GetData() { return buffer_.GetData(); }
789
790 intptr_t GetDataSize() { return buffer_.GetDataSize(); }
791
792 uint8_t* GetFirstDataBuffer() { return buffer_.GetFirstDataBuffer(); }
793
794 void FreeDataBuffer() { return buffer_.FreeDataBuffer(); }
795
796#if defined(DEBUG)
797 bool IsEmpty() const { return buffer_.IsEmpty(); }
798#endif
799
800 void Close() {
801 CloseHandle(handle_);
802 CloseHandle(event_);
803 handle_ = INVALID_HANDLE_VALUE;
804 overlapped_.hEvent = INVALID_HANDLE_VALUE;
805 }
806
807 private:
808 void ClearOverlapped() {
809 memset(&overlapped_, 0, sizeof(overlapped_));
810 overlapped_.hEvent = event_;
811 }
812
813 OVERLAPPED overlapped_;
814 HANDLE handle_;
815 HANDLE event_;
816 BufferList buffer_;
817
818 DISALLOW_ALLOCATION();
819 DISALLOW_COPY_AND_ASSIGN(OverlappedHandle);
820};
821
822bool Process::Wait(intptr_t pid,
823 intptr_t in,
824 intptr_t out,
825 intptr_t err,
826 intptr_t exit_event,
827 ProcessResult* result) {
828 // Close input to the process right away.
829 reinterpret_cast<FileHandle*>(in)->Close();
830
831 // All pipes created to the sub-process support overlapped IO.
832 FileHandle* stdout_handle = reinterpret_cast<FileHandle*>(out);
833 ASSERT(stdout_handle->SupportsOverlappedIO());
834 FileHandle* stderr_handle = reinterpret_cast<FileHandle*>(err);
835 ASSERT(stderr_handle->SupportsOverlappedIO());
836 FileHandle* exit_handle = reinterpret_cast<FileHandle*>(exit_event);
837 ASSERT(exit_handle->SupportsOverlappedIO());
838
839 // Create three events for overlapped IO. These are created as already
840 // signalled to ensure they have read called at least once.
841 static const int kHandles = 3;
842 HANDLE events[kHandles];
843 for (int i = 0; i < kHandles; i++) {
844 events[i] = CreateEvent(NULL, FALSE, TRUE, NULL);
845 }
846
847 // Setup the structure for handling overlapped IO.
848 OverlappedHandle oh[kHandles];
849 oh[0].Init(stdout_handle->handle(), events[0]);
850 oh[1].Init(stderr_handle->handle(), events[1]);
851 oh[2].Init(exit_handle->handle(), events[2]);
852
853 // Continue until all handles are closed.
854 int alive = kHandles;
855 while (alive > 0) {
856 // Blocking call waiting for events from the child process.
857 DWORD wait_result = WaitForMultipleObjects(alive, events, FALSE, INFINITE);
858
859 // Find the handle signalled.
860 int index = wait_result - WAIT_OBJECT_0;
861 for (int i = 0; i < kHandles; i++) {
862 if (oh[i].HasEvent(events[index])) {
863 bool ok = oh[i].Read();
864 if (!ok) {
865 if (GetLastError() == ERROR_BROKEN_PIPE) {
866 oh[i].Close();
867 alive--;
868 if (index < alive) {
869 events[index] = events[alive];
870 }
871 } else if (err != ERROR_IO_PENDING) {
872 DWORD e = GetLastError();
873 oh[0].Close();
874 oh[1].Close();
875 oh[2].Close();
876 SetLastError(e);
877 return false;
878 }
879 }
880 break;
881 }
882 }
883 }
884
885 // All handles closed and all data read.
886 result->set_stdout_data(oh[0].GetData());
887 result->set_stderr_data(oh[1].GetData());
888 DEBUG_ASSERT(oh[0].IsEmpty());
889 DEBUG_ASSERT(oh[1].IsEmpty());
890
891 // Calculate the exit code.
892 ASSERT(oh[2].GetDataSize() == 8);
893 uint32_t exit_codes[2];
894 memmove(&exit_codes, oh[2].GetFirstDataBuffer(), sizeof(exit_codes));
895 oh[2].FreeDataBuffer();
896 intptr_t exit_code = exit_codes[0];
897 intptr_t negative = exit_codes[1];
898 if (negative != 0) {
899 exit_code = -exit_code;
900 }
901 result->set_exit_code(exit_code);
902 return true;
903}
904
905bool Process::Kill(intptr_t id, int signal) {
906 USE(signal); // signal is not used on Windows.
907 HANDLE process_handle;
908 HANDLE wait_handle;
909 HANDLE exit_pipe;
910 // First check the process info list for the process to get a handle to it.
911 bool success = ProcessInfoList::LookupProcess(id, &process_handle,
912 &wait_handle, &exit_pipe);
913 // For detached processes we don't have the process registered in the
914 // process info list. Try to look it up through the OS.
915 if (!success) {
916 process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, id);
917 // The process is already dead.
918 if (process_handle == INVALID_HANDLE_VALUE) {
919 return false;
920 }
921 }
922 BOOL result = TerminateProcess(process_handle, -1);
923 return result ? true : false;
924}
925
926void Process::TerminateExitCodeHandler() {
927 // Nothing needs to be done on Windows.
928}
929
930intptr_t Process::CurrentProcessId() {
931 return static_cast<intptr_t>(GetCurrentProcessId());
932}
933
934int64_t Process::CurrentRSS() {
935 PROCESS_MEMORY_COUNTERS pmc;
936 if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
937 return -1;
938 }
939 return pmc.WorkingSetSize;
940}
941
942int64_t Process::MaxRSS() {
943 PROCESS_MEMORY_COUNTERS pmc;
944 if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
945 return -1;
946 }
947 return pmc.PeakWorkingSetSize;
948}
949
950static SignalInfo* signal_handlers = NULL;
951static Mutex* signal_mutex = nullptr;
952
953SignalInfo::~SignalInfo() {
954 FileHandle* file_handle = reinterpret_cast<FileHandle*>(fd_);
955 file_handle->Close();
956 file_handle->Release();
957}
958
959BOOL WINAPI SignalHandler(DWORD signal) {
960 MutexLocker lock(signal_mutex);
961 const SignalInfo* handler = signal_handlers;
962 bool handled = false;
963 while (handler != NULL) {
964 if (handler->signal() == signal) {
965 int value = 0;
966 SocketBase::Write(handler->fd(), &value, 1, SocketBase::kAsync);
967 handled = true;
968 }
969 handler = handler->next();
970 }
971 return handled;
972}
973
974intptr_t GetWinSignal(intptr_t signal) {
975 switch (signal) {
976 case kSighup:
977 return CTRL_CLOSE_EVENT;
978 case kSigint:
979 return CTRL_C_EVENT;
980 default:
981 return -1;
982 }
983}
984
985intptr_t Process::SetSignalHandler(intptr_t signal) {
986 signal = GetWinSignal(signal);
987 if (signal == -1) {
988 SetLastError(ERROR_NOT_SUPPORTED);
989 return -1;
990 }
991
992 // Generate a unique pipe name for the named pipe.
993 wchar_t pipe_name[kMaxPipeNameSize];
994 int status = GenerateNames<1>(&pipe_name);
995 if (status != 0) {
996 return status;
997 }
998
999 HANDLE fds[2];
1000 if (!CreateProcessPipe(fds, pipe_name, kInheritNone)) {
1001 int error_code = GetLastError();
1002 CloseProcessPipe(fds);
1003 SetLastError(error_code);
1004 return -1;
1005 }
1006 MutexLocker lock(signal_mutex);
1007 FileHandle* write_handle = new FileHandle(fds[kWriteHandle]);
1008 write_handle->EnsureInitialized(EventHandler::delegate());
1009 intptr_t write_fd = reinterpret_cast<intptr_t>(write_handle);
1010 if (signal_handlers == NULL) {
1011 if (SetConsoleCtrlHandler(SignalHandler, true) == 0) {
1012 int error_code = GetLastError();
1013 // Since SetConsoleCtrlHandler failed, the IO completion port will
1014 // never receive an event for this handle, and will therefore never
1015 // release the reference Retained by EnsureInitialized(). So, we
1016 // have to do a second Release() here.
1017 write_handle->Release();
1018 write_handle->Release();
1019 CloseProcessPipe(fds);
1020 SetLastError(error_code);
1021 return -1;
1022 }
1023 }
1024 signal_handlers = new SignalInfo(write_fd, signal, signal_handlers);
1025 return reinterpret_cast<intptr_t>(new FileHandle(fds[kReadHandle]));
1026}
1027
1028void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {
1029 signal = GetWinSignal(signal);
1030 if (signal == -1) {
1031 return;
1032 }
1033 MutexLocker lock(signal_mutex);
1034 SignalInfo* handler = signal_handlers;
1035 while (handler != NULL) {
1036 bool remove = false;
1037 if (handler->signal() == signal) {
1038 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1039 if (signal_handlers == handler) {
1040 signal_handlers = handler->next();
1041 }
1042 handler->Unlink();
1043 FileHandle* file_handle = reinterpret_cast<FileHandle*>(handler->fd());
1044 file_handle->Release();
1045 remove = true;
1046 }
1047 }
1048 SignalInfo* next = handler->next();
1049 if (remove) {
1050 delete handler;
1051 }
1052 handler = next;
1053 }
1054 if (signal_handlers == NULL) {
1055 USE(SetConsoleCtrlHandler(SignalHandler, false));
1056 }
1057}
1058
1059void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {
1060 MutexLocker lock(signal_mutex);
1061 SignalInfo* handler = signal_handlers;
1062 while (handler != NULL) {
1063 bool remove = false;
1064 if (handler->fd() == fd) {
1065 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1066 if (signal_handlers == handler) {
1067 signal_handlers = handler->next();
1068 }
1069 handler->Unlink();
1070 FileHandle* file_handle = reinterpret_cast<FileHandle*>(handler->fd());
1071 file_handle->Release();
1072 remove = true;
1073 }
1074 }
1075 SignalInfo* next = handler->next();
1076 if (remove) {
1077 delete handler;
1078 }
1079 handler = next;
1080 }
1081 if (signal_handlers == NULL) {
1082 USE(SetConsoleCtrlHandler(SignalHandler, false));
1083 }
1084}
1085
1086void ProcessInfoList::Init() {
1087 ASSERT(ProcessInfoList::mutex_ == nullptr);
1088 ProcessInfoList::mutex_ = new Mutex();
1089}
1090
1091void ProcessInfoList::Cleanup() {
1092 ASSERT(ProcessInfoList::mutex_ != nullptr);
1093 delete ProcessInfoList::mutex_;
1094 ProcessInfoList::mutex_ = nullptr;
1095}
1096
1097void Process::Init() {
1098 ProcessInfoList::Init();
1099
1100 ASSERT(signal_mutex == nullptr);
1101 signal_mutex = new Mutex();
1102
1103 ASSERT(initialized_mutex == nullptr);
1104 initialized_mutex = new Mutex();
1105
1106 ASSERT(Process::global_exit_code_mutex_ == nullptr);
1107 Process::global_exit_code_mutex_ = new Mutex();
1108}
1109
1110void Process::Cleanup() {
1111 ClearAllSignalHandlers();
1112
1113 ASSERT(signal_mutex != nullptr);
1114 delete signal_mutex;
1115 signal_mutex = nullptr;
1116
1117 ASSERT(initialized_mutex != nullptr);
1118 delete initialized_mutex;
1119 initialized_mutex = nullptr;
1120
1121 ASSERT(Process::global_exit_code_mutex_ != nullptr);
1122 delete Process::global_exit_code_mutex_;
1123 Process::global_exit_code_mutex_ = nullptr;
1124
1125 ProcessInfoList::Cleanup();
1126}
1127
1128} // namespace bin
1129} // namespace dart
1130
1131#endif // defined(HOST_OS_WINDOWS)
1132