xref: /third_party/ninja/src/subprocess-win32.cc (revision 695b41ee)
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
24using namespace std;
25
26Subprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),
27                                           is_reading_(false),
28                                           use_console_(use_console) {
29}
30
31Subprocess::~Subprocess() {
32  if (pipe_) {
33    if (!CloseHandle(pipe_))
34      Win32Fatal("CloseHandle");
35  }
36  // Reap child if forgotten.
37  if (child_)
38    Finish();
39}
40
41HANDLE Subprocess::SetupPipe(HANDLE ioport) {
42  char pipe_name[100];
43  snprintf(pipe_name, sizeof(pipe_name),
44           "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this);
45
46  pipe_ = ::CreateNamedPipeA(pipe_name,
47                             PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
48                             PIPE_TYPE_BYTE,
49                             PIPE_UNLIMITED_INSTANCES,
50                             0, 0, INFINITE, NULL);
51  if (pipe_ == INVALID_HANDLE_VALUE)
52    Win32Fatal("CreateNamedPipe");
53
54  if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))
55    Win32Fatal("CreateIoCompletionPort");
56
57  memset(&overlapped_, 0, sizeof(overlapped_));
58  if (!ConnectNamedPipe(pipe_, &overlapped_) &&
59      GetLastError() != ERROR_IO_PENDING) {
60    Win32Fatal("ConnectNamedPipe");
61  }
62
63  // Get the write end of the pipe as a handle inheritable across processes.
64  HANDLE output_write_handle =
65      CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
66  HANDLE output_write_child;
67  if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
68                       GetCurrentProcess(), &output_write_child,
69                       0, TRUE, DUPLICATE_SAME_ACCESS)) {
70    Win32Fatal("DuplicateHandle");
71  }
72  CloseHandle(output_write_handle);
73
74  return output_write_child;
75}
76
77bool Subprocess::Start(SubprocessSet* set, const string& command) {
78  HANDLE child_pipe = SetupPipe(set->ioport_);
79
80  SECURITY_ATTRIBUTES security_attributes;
81  memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES));
82  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
83  security_attributes.bInheritHandle = TRUE;
84  // Must be inheritable so subprocesses can dup to children.
85  HANDLE nul =
86      CreateFileA("NUL", GENERIC_READ,
87                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
88                  &security_attributes, OPEN_EXISTING, 0, NULL);
89  if (nul == INVALID_HANDLE_VALUE)
90    Fatal("couldn't open nul");
91
92  STARTUPINFOA startup_info;
93  memset(&startup_info, 0, sizeof(startup_info));
94  startup_info.cb = sizeof(STARTUPINFO);
95  if (!use_console_) {
96    startup_info.dwFlags = STARTF_USESTDHANDLES;
97    startup_info.hStdInput = nul;
98    startup_info.hStdOutput = child_pipe;
99    startup_info.hStdError = child_pipe;
100  }
101  // In the console case, child_pipe is still inherited by the child and closed
102  // when the subprocess finishes, which then notifies ninja.
103
104  PROCESS_INFORMATION process_info;
105  memset(&process_info, 0, sizeof(process_info));
106
107  // Ninja handles ctrl-c, except for subprocesses in console pools.
108  DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;
109
110  // Do not prepend 'cmd /c' on Windows, this breaks command
111  // lines greater than 8,191 chars.
112  if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
113                      /* inherit handles */ TRUE, process_flags,
114                      NULL, NULL,
115                      &startup_info, &process_info)) {
116    DWORD error = GetLastError();
117    if (error == ERROR_FILE_NOT_FOUND) {
118      // File (program) not found error is treated as a normal build
119      // action failure.
120      if (child_pipe)
121        CloseHandle(child_pipe);
122      CloseHandle(pipe_);
123      CloseHandle(nul);
124      pipe_ = NULL;
125      // child_ is already NULL;
126      buf_ = "CreateProcess failed: The system cannot find the file "
127          "specified.\n";
128      return true;
129    } else {
130      fprintf(stderr, "\nCreateProcess failed. Command attempted:\n\"%s\"\n",
131              command.c_str());
132      const char* hint = NULL;
133      // ERROR_INVALID_PARAMETER means the command line was formatted
134      // incorrectly. This can be caused by a command line being too long or
135      // leading whitespace in the command. Give extra context for this case.
136      if (error == ERROR_INVALID_PARAMETER) {
137        if (command.length() > 0 && (command[0] == ' ' || command[0] == '\t'))
138          hint = "command contains leading whitespace";
139        else
140          hint = "is the command line too long?";
141      }
142      Win32Fatal("CreateProcess", hint);
143    }
144  }
145
146  // Close pipe channel only used by the child.
147  if (child_pipe)
148    CloseHandle(child_pipe);
149  CloseHandle(nul);
150
151  CloseHandle(process_info.hThread);
152  child_ = process_info.hProcess;
153
154  return true;
155}
156
157void Subprocess::OnPipeReady() {
158  DWORD bytes;
159  if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
160    if (GetLastError() == ERROR_BROKEN_PIPE) {
161      CloseHandle(pipe_);
162      pipe_ = NULL;
163      return;
164    }
165    Win32Fatal("GetOverlappedResult");
166  }
167
168  if (is_reading_ && bytes)
169    buf_.append(overlapped_buf_, bytes);
170
171  memset(&overlapped_, 0, sizeof(overlapped_));
172  is_reading_ = true;
173  if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
174                  &bytes, &overlapped_)) {
175    if (GetLastError() == ERROR_BROKEN_PIPE) {
176      CloseHandle(pipe_);
177      pipe_ = NULL;
178      return;
179    }
180    if (GetLastError() != ERROR_IO_PENDING)
181      Win32Fatal("ReadFile");
182  }
183
184  // Even if we read any bytes in the readfile call, we'll enter this
185  // function again later and get them at that point.
186}
187
188ExitStatus Subprocess::Finish() {
189  if (!child_)
190    return ExitFailure;
191
192  // TODO: add error handling for all of these.
193  WaitForSingleObject(child_, INFINITE);
194
195  DWORD exit_code = 0;
196  GetExitCodeProcess(child_, &exit_code);
197
198  CloseHandle(child_);
199  child_ = NULL;
200
201  return exit_code == 0              ? ExitSuccess :
202         exit_code == CONTROL_C_EXIT ? ExitInterrupted :
203                                       ExitFailure;
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
216SubprocessSet::SubprocessSet() {
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
224SubprocessSet::~SubprocessSet() {
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
254bool 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
283Subprocess* SubprocessSet::NextFinished() {
284  if (finished_.empty())
285    return NULL;
286  Subprocess* subproc = finished_.front();
287  finished_.pop();
288  return subproc;
289}
290
291void 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}
308