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 "subprocess.h"
16 
17 #include <assert.h>
18 #include <stdio.h>
19 
20 #include <algorithm>
21 
22 #include "util.h"
23 
24 Subprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),
25  is_reading_(false),
26  use_console_(use_console) {
27 }
28 
30  if (pipe_) {
31  if (!CloseHandle(pipe_))
32  Win32Fatal("CloseHandle");
33  }
34  // Reap child if forgotten.
35  if (child_)
36  Finish();
37 }
38 
39 HANDLE Subprocess::SetupPipe(HANDLE ioport) {
40  char pipe_name[100];
41  snprintf(pipe_name, sizeof(pipe_name),
42  "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this);
43 
44  pipe_ = ::CreateNamedPipeA(pipe_name,
45  PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
46  PIPE_TYPE_BYTE,
47  PIPE_UNLIMITED_INSTANCES,
48  0, 0, INFINITE, NULL);
49  if (pipe_ == INVALID_HANDLE_VALUE)
50  Win32Fatal("CreateNamedPipe");
51 
52  if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))
53  Win32Fatal("CreateIoCompletionPort");
54 
55  memset(&overlapped_, 0, sizeof(overlapped_));
56  if (!ConnectNamedPipe(pipe_, &overlapped_) &&
57  GetLastError() != ERROR_IO_PENDING) {
58  Win32Fatal("ConnectNamedPipe");
59  }
60 
61  // Get the write end of the pipe as a handle inheritable across processes.
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");
69  }
70  CloseHandle(output_write_handle);
71 
72  return output_write_child;
73 }
74 
75 bool Subprocess::Start(SubprocessSet* set, const string& command) {
76  HANDLE child_pipe = SetupPipe(set->ioport_);
77 
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;
82  // Must be inheritable so subprocesses can dup to children.
83  HANDLE nul =
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");
89 
90  STARTUPINFOA startup_info;
91  memset(&startup_info, 0, sizeof(startup_info));
92  startup_info.cb = sizeof(STARTUPINFO);
93  if (!use_console_) {
94  startup_info.dwFlags = STARTF_USESTDHANDLES;
95  startup_info.hStdInput = nul;
96  startup_info.hStdOutput = child_pipe;
97  startup_info.hStdError = child_pipe;
98  }
99  // In the console case, child_pipe is still inherited by the child and closed
100  // when the subprocess finishes, which then notifies ninja.
101 
102  PROCESS_INFORMATION process_info;
103  memset(&process_info, 0, sizeof(process_info));
104 
105  // Ninja handles ctrl-c, except for subprocesses in console pools.
106  DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;
107 
108  // Do not prepend 'cmd /c' on Windows, this breaks command
109  // lines greater than 8,191 chars.
110  if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
111  /* inherit handles */ TRUE, process_flags,
112  NULL, NULL,
113  &startup_info, &process_info)) {
114  DWORD error = GetLastError();
115  if (error == ERROR_FILE_NOT_FOUND) {
116  // File (program) not found error is treated as a normal build
117  // action failure.
118  if (child_pipe)
119  CloseHandle(child_pipe);
120  CloseHandle(pipe_);
121  CloseHandle(nul);
122  pipe_ = NULL;
123  // child_ is already NULL;
124  buf_ = "CreateProcess failed: The system cannot find the file "
125  "specified.\n";
126  return true;
127  } else if (error == ERROR_INVALID_PARAMETER) {
128  // This generally means that the command line was too long. Give extra
129  // context for this case.
130  Win32Fatal("CreateProcess", "is the command line too long?");
131  } else {
132  Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal
133  }
134  }
135 
136  // Close pipe channel only used by the child.
137  if (child_pipe)
138  CloseHandle(child_pipe);
139  CloseHandle(nul);
140 
141  CloseHandle(process_info.hThread);
142  child_ = process_info.hProcess;
143 
144  return true;
145 }
146 
148  DWORD bytes;
149  if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
150  if (GetLastError() == ERROR_BROKEN_PIPE) {
151  CloseHandle(pipe_);
152  pipe_ = NULL;
153  return;
154  }
155  Win32Fatal("GetOverlappedResult");
156  }
157 
158  if (is_reading_ && bytes)
159  buf_.append(overlapped_buf_, bytes);
160 
161  memset(&overlapped_, 0, sizeof(overlapped_));
162  is_reading_ = true;
163  if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
164  &bytes, &overlapped_)) {
165  if (GetLastError() == ERROR_BROKEN_PIPE) {
166  CloseHandle(pipe_);
167  pipe_ = NULL;
168  return;
169  }
170  if (GetLastError() != ERROR_IO_PENDING)
171  Win32Fatal("ReadFile");
172  }
173 
174  // Even if we read any bytes in the readfile call, we'll enter this
175  // function again later and get them at that point.
176 }
177 
179  if (!child_)
180  return ExitFailure;
181 
182  // TODO: add error handling for all of these.
183  WaitForSingleObject(child_, INFINITE);
184 
185  DWORD exit_code = 0;
186  GetExitCodeProcess(child_, &exit_code);
187 
188  CloseHandle(child_);
189  child_ = NULL;
190 
191  return exit_code == 0 ? ExitSuccess :
192  exit_code == CONTROL_C_EXIT ? ExitInterrupted :
193  ExitFailure;
194 }
195 
196 bool Subprocess::Done() const {
197  return pipe_ == NULL;
198 }
199 
200 const string& Subprocess::GetOutput() const {
201  return buf_;
202 }
203 
204 HANDLE SubprocessSet::ioport_;
205 
207  ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
208  if (!ioport_)
209  Win32Fatal("CreateIoCompletionPort");
210  if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
211  Win32Fatal("SetConsoleCtrlHandler");
212 }
213 
215  Clear();
216 
217  SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
218  CloseHandle(ioport_);
219 }
220 
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");
225  return TRUE;
226  }
227 
228  return FALSE;
229 }
230 
231 Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
232  Subprocess *subprocess = new Subprocess(use_console);
233  if (!subprocess->Start(this, command)) {
234  delete subprocess;
235  return 0;
236  }
237  if (subprocess->child_)
238  running_.push_back(subprocess);
239  else
240  finished_.push(subprocess);
241  return subprocess;
242 }
243 
244 bool SubprocessSet::DoWork() {
245  DWORD bytes_read;
246  Subprocess* subproc;
247  OVERLAPPED* overlapped;
248 
249  if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
250  &overlapped, INFINITE)) {
251  if (GetLastError() != ERROR_BROKEN_PIPE)
252  Win32Fatal("GetQueuedCompletionStatus");
253  }
254 
255  if (!subproc) // A NULL subproc indicates that we were interrupted and is
256  // delivered by NotifyInterrupted above.
257  return true;
258 
259  subproc->OnPipeReady();
260 
261  if (subproc->Done()) {
262  vector<Subprocess*>::iterator end =
263  remove(running_.begin(), running_.end(), subproc);
264  if (running_.end() != end) {
265  finished_.push(subproc);
266  running_.resize(end - running_.begin());
267  }
268  }
269 
270  return false;
271 }
272 
274  if (finished_.empty())
275  return NULL;
276  Subprocess* subproc = finished_.front();
277  finished_.pop();
278  return subproc;
279 }
280 
281 void SubprocessSet::Clear() {
282  for (vector<Subprocess*>::iterator i = running_.begin();
283  i != running_.end(); ++i) {
284  // Since the foreground process is in our process group, it will receive a
285  // CTRL_C_EVENT or CTRL_BREAK_EVENT at the same time as us.
286  if ((*i)->child_ && !(*i)->use_console_) {
287  if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
288  GetProcessId((*i)->child_))) {
289  Win32Fatal("GenerateConsoleCtrlEvent");
290  }
291  }
292  }
293  for (vector<Subprocess*>::iterator i = running_.begin();
294  i != running_.end(); ++i)
295  delete *i;
296  running_.clear();
297 }
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Definition: subprocess.h:83
bool use_console_
Definition: subprocess.h:75
Subprocess * NextFinished()
vector< Subprocess * > running_
Definition: subprocess.h:92
string buf_
Definition: subprocess.h:59
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.
Definition: subprocess.h:43
ExitStatus
Definition: exit_status.h:18
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).
Definition: util.cc:316
bool Start(struct SubprocessSet *set, const string &command)
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition: util.cc:57
const string & GetOutput() const
bool Done() const
queue< Subprocess * > finished_
Definition: subprocess.h:93