Ninja
subprocess-win32.cc
Go to the documentation of this file.
1// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "exit_status.h"
16#include "subprocess.h"
17
18#include <assert.h>
19#include <stdio.h>
20
21#include <algorithm>
22
23#include "util.h"
24
25using namespace std;
26
27Subprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),
28 is_reading_(false),
29 use_console_(use_console) {
30}
31
33 if (pipe_) {
34 if (!CloseHandle(pipe_))
35 Win32Fatal("CloseHandle");
36 }
37 // Reap child if forgotten.
38 if (child_)
39 Finish();
40}
41
42HANDLE Subprocess::SetupPipe(HANDLE ioport) {
43 char pipe_name[100];
44 snprintf(pipe_name, sizeof(pipe_name),
45 "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this);
46
47 pipe_ = ::CreateNamedPipeA(pipe_name,
48 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
49 PIPE_TYPE_BYTE,
50 PIPE_UNLIMITED_INSTANCES,
51 0, 0, INFINITE, NULL);
52 if (pipe_ == INVALID_HANDLE_VALUE)
53 Win32Fatal("CreateNamedPipe");
54
55 if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))
56 Win32Fatal("CreateIoCompletionPort");
57
58 memset(&overlapped_, 0, sizeof(overlapped_));
59 if (!ConnectNamedPipe(pipe_, &overlapped_) &&
60 GetLastError() != ERROR_IO_PENDING) {
61 Win32Fatal("ConnectNamedPipe");
62 }
63
64 // Get the write end of the pipe as a handle inheritable across processes.
65 HANDLE output_write_handle =
66 CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
67 HANDLE output_write_child;
68 if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
69 GetCurrentProcess(), &output_write_child,
70 0, TRUE, DUPLICATE_SAME_ACCESS)) {
71 Win32Fatal("DuplicateHandle");
72 }
73 CloseHandle(output_write_handle);
74
75 return output_write_child;
76}
77
78bool Subprocess::Start(SubprocessSet* set, const string& command) {
79 HANDLE child_pipe = SetupPipe(set->ioport_);
80
81 SECURITY_ATTRIBUTES security_attributes;
82 memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES));
83 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
84 security_attributes.bInheritHandle = TRUE;
85 // Must be inheritable so subprocesses can dup to children.
86 HANDLE nul =
87 CreateFileA("NUL", GENERIC_READ,
88 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
89 &security_attributes, OPEN_EXISTING, 0, NULL);
90 if (nul == INVALID_HANDLE_VALUE)
91 Fatal("couldn't open nul");
92
93 STARTUPINFOA startup_info;
94 memset(&startup_info, 0, sizeof(startup_info));
95 startup_info.cb = sizeof(STARTUPINFO);
96 if (!use_console_) {
97 startup_info.dwFlags = STARTF_USESTDHANDLES;
98 startup_info.hStdInput = nul;
99 startup_info.hStdOutput = child_pipe;
100 startup_info.hStdError = child_pipe;
101 }
102 // In the console case, child_pipe is still inherited by the child and closed
103 // when the subprocess finishes, which then notifies ninja.
104
105 PROCESS_INFORMATION process_info;
106 memset(&process_info, 0, sizeof(process_info));
107
108 // Ninja handles ctrl-c, except for subprocesses in console pools.
109 DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;
110
111 // Do not prepend 'cmd /c' on Windows, this breaks command
112 // lines greater than 8,191 chars.
113 if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
114 /* inherit handles */ TRUE, process_flags,
115 NULL, NULL,
116 &startup_info, &process_info)) {
117 DWORD error = GetLastError();
118 if (error == ERROR_FILE_NOT_FOUND) {
119 // File (program) not found error is treated as a normal build
120 // action failure.
121 if (child_pipe)
122 CloseHandle(child_pipe);
123 CloseHandle(pipe_);
124 CloseHandle(nul);
125 pipe_ = NULL;
126 // child_ is already NULL;
127 buf_ = "CreateProcess failed: The system cannot find the file "
128 "specified.\n";
129 return true;
130 } else {
131 fprintf(stderr, "\nCreateProcess failed. Command attempted:\n\"%s\"\n",
132 command.c_str());
133 const char* hint = NULL;
134 // ERROR_INVALID_PARAMETER means the command line was formatted
135 // incorrectly. This can be caused by a command line being too long or
136 // leading whitespace in the command. Give extra context for this case.
137 if (error == ERROR_INVALID_PARAMETER) {
138 if (command.length() > 0 && (command[0] == ' ' || command[0] == '\t'))
139 hint = "command contains leading whitespace";
140 else
141 hint = "is the command line too long?";
142 }
143 Win32Fatal("CreateProcess", hint);
144 }
145 }
146
147 // Close pipe channel only used by the child.
148 if (child_pipe)
149 CloseHandle(child_pipe);
150 CloseHandle(nul);
151
152 CloseHandle(process_info.hThread);
153 child_ = process_info.hProcess;
154
155 return true;
156}
157
159 DWORD bytes;
160 if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
161 if (GetLastError() == ERROR_BROKEN_PIPE) {
162 CloseHandle(pipe_);
163 pipe_ = NULL;
164 return;
165 }
166 Win32Fatal("GetOverlappedResult");
167 }
168
169 if (is_reading_ && bytes)
170 buf_.append(overlapped_buf_, bytes);
171
172 memset(&overlapped_, 0, sizeof(overlapped_));
173 is_reading_ = true;
174 if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
175 &bytes, &overlapped_)) {
176 if (GetLastError() == ERROR_BROKEN_PIPE) {
177 CloseHandle(pipe_);
178 pipe_ = NULL;
179 return;
180 }
181 if (GetLastError() != ERROR_IO_PENDING)
182 Win32Fatal("ReadFile");
183 }
184
185 // Even if we read any bytes in the readfile call, we'll enter this
186 // function again later and get them at that point.
187}
188
190 if (!child_)
191 return ExitFailure;
192
193 // TODO: add error handling for all of these.
194 WaitForSingleObject(child_, INFINITE);
195
196 DWORD exit_code = 0;
197 GetExitCodeProcess(child_, &exit_code);
198
199 CloseHandle(child_);
200 child_ = NULL;
201
202 return exit_code == CONTROL_C_EXIT ? ExitInterrupted :
203 static_cast<ExitStatus>(exit_code);
204}
205
206bool Subprocess::Done() const {
207 return pipe_ == NULL;
208}
209
210const string& Subprocess::GetOutput() const {
211 return buf_;
212}
213
214HANDLE SubprocessSet::ioport_;
215
217 ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
218 if (!ioport_)
219 Win32Fatal("CreateIoCompletionPort");
220 if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
221 Win32Fatal("SetConsoleCtrlHandler");
222}
223
225 Clear();
226
227 SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
228 CloseHandle(ioport_);
229}
230
231BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
232 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
233 if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
234 Win32Fatal("PostQueuedCompletionStatus");
235 return TRUE;
236 }
237
238 return FALSE;
239}
240
241Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
242 Subprocess *subprocess = new Subprocess(use_console);
243 if (!subprocess->Start(this, command)) {
244 delete subprocess;
245 return 0;
246 }
247 if (subprocess->child_)
248 running_.push_back(subprocess);
249 else
250 finished_.push(subprocess);
251 return subprocess;
252}
253
255 DWORD bytes_read;
256 Subprocess* subproc;
257 OVERLAPPED* overlapped;
258
259 if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
260 &overlapped, INFINITE)) {
261 if (GetLastError() != ERROR_BROKEN_PIPE)
262 Win32Fatal("GetQueuedCompletionStatus");
263 }
264
265 if (!subproc) // A NULL subproc indicates that we were interrupted and is
266 // delivered by NotifyInterrupted above.
267 return true;
268
269 subproc->OnPipeReady();
270
271 if (subproc->Done()) {
272 vector<Subprocess*>::iterator end =
273 remove(running_.begin(), running_.end(), subproc);
274 if (running_.end() != end) {
275 finished_.push(subproc);
276 running_.resize(end - running_.begin());
277 }
278 }
279
280 return false;
281}
282
284 if (finished_.empty())
285 return NULL;
286 Subprocess* subproc = finished_.front();
287 finished_.pop();
288 return subproc;
289}
290
292 for (vector<Subprocess*>::iterator i = running_.begin();
293 i != running_.end(); ++i) {
294 // Since the foreground process is in our process group, it will receive a
295 // CTRL_C_EVENT or CTRL_BREAK_EVENT at the same time as us.
296 if ((*i)->child_ && !(*i)->use_console_) {
297 if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
298 GetProcessId((*i)->child_))) {
299 Win32Fatal("GenerateConsoleCtrlEvent");
300 }
301 }
302 }
303 for (vector<Subprocess*>::iterator i = running_.begin();
304 i != running_.end(); ++i)
305 delete *i;
306 running_.clear();
307}
ExitStatus
Definition exit_status.h:27
@ ExitInterrupted
Definition exit_status.h:30
@ ExitFailure
Definition exit_status.h:29
Definition hash_map.h:26
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Definition subprocess.h:101
std::queue< Subprocess * > finished_
Definition subprocess.h:111
Subprocess * NextFinished()
std::vector< Subprocess * > running_
Definition subprocess.h:110
Subprocess * Add(const std::string &command, bool use_console=false)
Subprocess wraps a single async subprocess.
Definition subprocess.h:42
bool Start(struct SubprocessSet *set, const std::string &command)
bool Done() const
Subprocess(bool use_console)
bool use_console_
Definition subprocess.h:93
ExitStatus Finish()
Returns ExitSuccess on successful process exit, ExitInterrupted if the process was interrupted,...
const std::string & GetOutput() const
std::string buf_
Definition subprocess.h:58
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition util.cc:67
int ReadFile(const string &path, string *contents, string *err)
Definition util.cc:415