• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 using namespace std;
25 
Subprocess(bool use_console)26 Subprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),
27                                            is_reading_(false),
28                                            use_console_(use_console) {
29 }
30 
~Subprocess()31 Subprocess::~Subprocess() {
32   if (pipe_) {
33     if (!CloseHandle(pipe_))
34       Win32Fatal("CloseHandle");
35   }
36   // Reap child if forgotten.
37   if (child_)
38     Finish();
39 }
40 
SetupPipe(HANDLE ioport)41 HANDLE 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 
Start(SubprocessSet * set,const string & command)77 bool 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 
OnPipeReady()157 void 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 
Finish()188 ExitStatus 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 
Done() const206 bool Subprocess::Done() const {
207   return pipe_ == NULL;
208 }
209 
GetOutput() const210 const string& Subprocess::GetOutput() const {
211   return buf_;
212 }
213 
214 HANDLE SubprocessSet::ioport_;
215 
SubprocessSet()216 SubprocessSet::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 
~SubprocessSet()224 SubprocessSet::~SubprocessSet() {
225   Clear();
226 
227   SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
228   CloseHandle(ioport_);
229 }
230 
NotifyInterrupted(DWORD dwCtrlType)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 
Add(const string & command,bool use_console)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 
DoWork()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 
NextFinished()283 Subprocess* SubprocessSet::NextFinished() {
284   if (finished_.empty())
285     return NULL;
286   Subprocess* subproc = finished_.front();
287   finished_.pop();
288   return subproc;
289 }
290 
Clear()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 }
308