26 use_console_(use_console) {
31 if (!CloseHandle(pipe_))
32 Win32Fatal(
"CloseHandle");
39 HANDLE Subprocess::SetupPipe(HANDLE ioport) {
41 snprintf(pipe_name,
sizeof(pipe_name),
42 "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(),
this);
44 pipe_ = ::CreateNamedPipeA(pipe_name,
45 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
47 PIPE_UNLIMITED_INSTANCES,
48 0, 0, INFINITE, NULL);
49 if (pipe_ == INVALID_HANDLE_VALUE)
50 Win32Fatal(
"CreateNamedPipe");
52 if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)
this, 0))
53 Win32Fatal(
"CreateIoCompletionPort");
55 memset(&overlapped_, 0,
sizeof(overlapped_));
56 if (!ConnectNamedPipe(pipe_, &overlapped_) &&
57 GetLastError() != ERROR_IO_PENDING) {
58 Win32Fatal(
"ConnectNamedPipe");
62 HANDLE output_write_handle =
63 CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
64 HANDLE output_write_child;
65 if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
66 GetCurrentProcess(), &output_write_child,
67 0, TRUE, DUPLICATE_SAME_ACCESS)) {
68 Win32Fatal(
"DuplicateHandle");
70 CloseHandle(output_write_handle);
72 return output_write_child;
76 HANDLE child_pipe = SetupPipe(set->ioport_);
78 SECURITY_ATTRIBUTES security_attributes;
79 memset(&security_attributes, 0,
sizeof(SECURITY_ATTRIBUTES));
80 security_attributes.nLength =
sizeof(SECURITY_ATTRIBUTES);
81 security_attributes.bInheritHandle = TRUE;
84 CreateFileA(
"NUL", GENERIC_READ,
85 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
86 &security_attributes, OPEN_EXISTING, 0, NULL);
87 if (nul == INVALID_HANDLE_VALUE)
88 Fatal(
"couldn't open nul");
90 STARTUPINFOA startup_info;
91 memset(&startup_info, 0,
sizeof(startup_info));
92 startup_info.cb =
sizeof(STARTUPINFO);
94 startup_info.dwFlags = STARTF_USESTDHANDLES;
95 startup_info.hStdInput = nul;
96 startup_info.hStdOutput = child_pipe;
97 startup_info.hStdError = child_pipe;
102 PROCESS_INFORMATION process_info;
103 memset(&process_info, 0,
sizeof(process_info));
106 DWORD process_flags =
use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;
110 if (!CreateProcessA(NULL, (
char*)command.c_str(), NULL, NULL,
113 &startup_info, &process_info)) {
114 DWORD error = GetLastError();
115 if (error == ERROR_FILE_NOT_FOUND) {
119 CloseHandle(child_pipe);
124 buf_ =
"CreateProcess failed: The system cannot find the file " 127 }
else if (error == ERROR_INVALID_PARAMETER) {
130 Win32Fatal(
"CreateProcess",
"is the command line too long?");
132 Win32Fatal(
"CreateProcess");
138 CloseHandle(child_pipe);
141 CloseHandle(process_info.hThread);
142 child_ = process_info.hProcess;
149 if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
150 if (GetLastError() == ERROR_BROKEN_PIPE) {
155 Win32Fatal(
"GetOverlappedResult");
158 if (is_reading_ && bytes)
159 buf_.append(overlapped_buf_, bytes);
161 memset(&overlapped_, 0,
sizeof(overlapped_));
163 if (!::
ReadFile(pipe_, overlapped_buf_,
sizeof(overlapped_buf_),
164 &bytes, &overlapped_)) {
165 if (GetLastError() == ERROR_BROKEN_PIPE) {
170 if (GetLastError() != ERROR_IO_PENDING)
171 Win32Fatal(
"ReadFile");
183 WaitForSingleObject(child_, INFINITE);
186 GetExitCodeProcess(child_, &exit_code);
197 return pipe_ == NULL;
204 HANDLE SubprocessSet::ioport_;
207 ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
209 Win32Fatal(
"CreateIoCompletionPort");
210 if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
211 Win32Fatal(
"SetConsoleCtrlHandler");
217 SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
218 CloseHandle(ioport_);
221 BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
222 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
223 if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
224 Win32Fatal(
"PostQueuedCompletionStatus");
233 if (!subprocess->
Start(
this, command)) {
237 if (subprocess->child_)
247 OVERLAPPED* overlapped;
249 if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
250 &overlapped, INFINITE)) {
251 if (GetLastError() != ERROR_BROKEN_PIPE)
252 Win32Fatal(
"GetQueuedCompletionStatus");
259 subproc->OnPipeReady();
261 if (subproc->Done()) {
262 vector<Subprocess*>::iterator end =
282 for (vector<Subprocess*>::iterator i =
running_.begin();
286 if ((*i)->child_ && !(*i)->use_console_) {
287 if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
288 GetProcessId((*i)->child_))) {
289 Win32Fatal(
"GenerateConsoleCtrlEvent");
293 for (vector<Subprocess*>::iterator i =
running_.begin();
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Subprocess * NextFinished()
vector< Subprocess * > running_
ExitStatus Finish()
Returns ExitSuccess on successful process exit, ExitInterrupted if the process was interrupted...
Subprocess * Add(const string &command, bool use_console=false)
Subprocess wraps a single async subprocess.
Subprocess(bool use_console)
int ReadFile(const string &path, string *contents, string *err)
Read a file to a string (in text mode: with CRLF conversion on Windows).
bool Start(struct SubprocessSet *set, const string &command)
void Fatal(const char *msg,...)
Log a fatal message and exit.
const string & GetOutput() const
queue< Subprocess * > finished_