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