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 
25 using namespace std;
26 
27 Subprocess::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 
42 HANDLE 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 
78 bool 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 
206 bool Subprocess::Done() const {
207  return pipe_ == NULL;
208 }
209 
210 const string& Subprocess::GetOutput() const {
211  return buf_;
212 }
213 
214 HANDLE 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 
231 BOOL 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 
241 Subprocess *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 
254 bool SubprocessSet::DoWork() {
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 
291 void SubprocessSet::Clear() {
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