1695b41eeSopenharmony_ci// Copyright 2012 Google Inc. All Rights Reserved.
2695b41eeSopenharmony_ci//
3695b41eeSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4695b41eeSopenharmony_ci// you may not use this file except in compliance with the License.
5695b41eeSopenharmony_ci// You may obtain a copy of the License at
6695b41eeSopenharmony_ci//
7695b41eeSopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8695b41eeSopenharmony_ci//
9695b41eeSopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10695b41eeSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11695b41eeSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12695b41eeSopenharmony_ci// See the License for the specific language governing permissions and
13695b41eeSopenharmony_ci// limitations under the License.
14695b41eeSopenharmony_ci
15695b41eeSopenharmony_ci#include "subprocess.h"
16695b41eeSopenharmony_ci
17695b41eeSopenharmony_ci#include <assert.h>
18695b41eeSopenharmony_ci#include <stdio.h>
19695b41eeSopenharmony_ci
20695b41eeSopenharmony_ci#include <algorithm>
21695b41eeSopenharmony_ci
22695b41eeSopenharmony_ci#include "util.h"
23695b41eeSopenharmony_ci
24695b41eeSopenharmony_ciusing namespace std;
25695b41eeSopenharmony_ci
26695b41eeSopenharmony_ciSubprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),
27695b41eeSopenharmony_ci                                           is_reading_(false),
28695b41eeSopenharmony_ci                                           use_console_(use_console) {
29695b41eeSopenharmony_ci}
30695b41eeSopenharmony_ci
31695b41eeSopenharmony_ciSubprocess::~Subprocess() {
32695b41eeSopenharmony_ci  if (pipe_) {
33695b41eeSopenharmony_ci    if (!CloseHandle(pipe_))
34695b41eeSopenharmony_ci      Win32Fatal("CloseHandle");
35695b41eeSopenharmony_ci  }
36695b41eeSopenharmony_ci  // Reap child if forgotten.
37695b41eeSopenharmony_ci  if (child_)
38695b41eeSopenharmony_ci    Finish();
39695b41eeSopenharmony_ci}
40695b41eeSopenharmony_ci
41695b41eeSopenharmony_ciHANDLE Subprocess::SetupPipe(HANDLE ioport) {
42695b41eeSopenharmony_ci  char pipe_name[100];
43695b41eeSopenharmony_ci  snprintf(pipe_name, sizeof(pipe_name),
44695b41eeSopenharmony_ci           "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this);
45695b41eeSopenharmony_ci
46695b41eeSopenharmony_ci  pipe_ = ::CreateNamedPipeA(pipe_name,
47695b41eeSopenharmony_ci                             PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
48695b41eeSopenharmony_ci                             PIPE_TYPE_BYTE,
49695b41eeSopenharmony_ci                             PIPE_UNLIMITED_INSTANCES,
50695b41eeSopenharmony_ci                             0, 0, INFINITE, NULL);
51695b41eeSopenharmony_ci  if (pipe_ == INVALID_HANDLE_VALUE)
52695b41eeSopenharmony_ci    Win32Fatal("CreateNamedPipe");
53695b41eeSopenharmony_ci
54695b41eeSopenharmony_ci  if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))
55695b41eeSopenharmony_ci    Win32Fatal("CreateIoCompletionPort");
56695b41eeSopenharmony_ci
57695b41eeSopenharmony_ci  memset(&overlapped_, 0, sizeof(overlapped_));
58695b41eeSopenharmony_ci  if (!ConnectNamedPipe(pipe_, &overlapped_) &&
59695b41eeSopenharmony_ci      GetLastError() != ERROR_IO_PENDING) {
60695b41eeSopenharmony_ci    Win32Fatal("ConnectNamedPipe");
61695b41eeSopenharmony_ci  }
62695b41eeSopenharmony_ci
63695b41eeSopenharmony_ci  // Get the write end of the pipe as a handle inheritable across processes.
64695b41eeSopenharmony_ci  HANDLE output_write_handle =
65695b41eeSopenharmony_ci      CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
66695b41eeSopenharmony_ci  HANDLE output_write_child;
67695b41eeSopenharmony_ci  if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
68695b41eeSopenharmony_ci                       GetCurrentProcess(), &output_write_child,
69695b41eeSopenharmony_ci                       0, TRUE, DUPLICATE_SAME_ACCESS)) {
70695b41eeSopenharmony_ci    Win32Fatal("DuplicateHandle");
71695b41eeSopenharmony_ci  }
72695b41eeSopenharmony_ci  CloseHandle(output_write_handle);
73695b41eeSopenharmony_ci
74695b41eeSopenharmony_ci  return output_write_child;
75695b41eeSopenharmony_ci}
76695b41eeSopenharmony_ci
77695b41eeSopenharmony_cibool Subprocess::Start(SubprocessSet* set, const string& command) {
78695b41eeSopenharmony_ci  HANDLE child_pipe = SetupPipe(set->ioport_);
79695b41eeSopenharmony_ci
80695b41eeSopenharmony_ci  SECURITY_ATTRIBUTES security_attributes;
81695b41eeSopenharmony_ci  memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES));
82695b41eeSopenharmony_ci  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
83695b41eeSopenharmony_ci  security_attributes.bInheritHandle = TRUE;
84695b41eeSopenharmony_ci  // Must be inheritable so subprocesses can dup to children.
85695b41eeSopenharmony_ci  HANDLE nul =
86695b41eeSopenharmony_ci      CreateFileA("NUL", GENERIC_READ,
87695b41eeSopenharmony_ci                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
88695b41eeSopenharmony_ci                  &security_attributes, OPEN_EXISTING, 0, NULL);
89695b41eeSopenharmony_ci  if (nul == INVALID_HANDLE_VALUE)
90695b41eeSopenharmony_ci    Fatal("couldn't open nul");
91695b41eeSopenharmony_ci
92695b41eeSopenharmony_ci  STARTUPINFOA startup_info;
93695b41eeSopenharmony_ci  memset(&startup_info, 0, sizeof(startup_info));
94695b41eeSopenharmony_ci  startup_info.cb = sizeof(STARTUPINFO);
95695b41eeSopenharmony_ci  if (!use_console_) {
96695b41eeSopenharmony_ci    startup_info.dwFlags = STARTF_USESTDHANDLES;
97695b41eeSopenharmony_ci    startup_info.hStdInput = nul;
98695b41eeSopenharmony_ci    startup_info.hStdOutput = child_pipe;
99695b41eeSopenharmony_ci    startup_info.hStdError = child_pipe;
100695b41eeSopenharmony_ci  }
101695b41eeSopenharmony_ci  // In the console case, child_pipe is still inherited by the child and closed
102695b41eeSopenharmony_ci  // when the subprocess finishes, which then notifies ninja.
103695b41eeSopenharmony_ci
104695b41eeSopenharmony_ci  PROCESS_INFORMATION process_info;
105695b41eeSopenharmony_ci  memset(&process_info, 0, sizeof(process_info));
106695b41eeSopenharmony_ci
107695b41eeSopenharmony_ci  // Ninja handles ctrl-c, except for subprocesses in console pools.
108695b41eeSopenharmony_ci  DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;
109695b41eeSopenharmony_ci
110695b41eeSopenharmony_ci  // Do not prepend 'cmd /c' on Windows, this breaks command
111695b41eeSopenharmony_ci  // lines greater than 8,191 chars.
112695b41eeSopenharmony_ci  if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
113695b41eeSopenharmony_ci                      /* inherit handles */ TRUE, process_flags,
114695b41eeSopenharmony_ci                      NULL, NULL,
115695b41eeSopenharmony_ci                      &startup_info, &process_info)) {
116695b41eeSopenharmony_ci    DWORD error = GetLastError();
117695b41eeSopenharmony_ci    if (error == ERROR_FILE_NOT_FOUND) {
118695b41eeSopenharmony_ci      // File (program) not found error is treated as a normal build
119695b41eeSopenharmony_ci      // action failure.
120695b41eeSopenharmony_ci      if (child_pipe)
121695b41eeSopenharmony_ci        CloseHandle(child_pipe);
122695b41eeSopenharmony_ci      CloseHandle(pipe_);
123695b41eeSopenharmony_ci      CloseHandle(nul);
124695b41eeSopenharmony_ci      pipe_ = NULL;
125695b41eeSopenharmony_ci      // child_ is already NULL;
126695b41eeSopenharmony_ci      buf_ = "CreateProcess failed: The system cannot find the file "
127695b41eeSopenharmony_ci          "specified.\n";
128695b41eeSopenharmony_ci      return true;
129695b41eeSopenharmony_ci    } else {
130695b41eeSopenharmony_ci      fprintf(stderr, "\nCreateProcess failed. Command attempted:\n\"%s\"\n",
131695b41eeSopenharmony_ci              command.c_str());
132695b41eeSopenharmony_ci      const char* hint = NULL;
133695b41eeSopenharmony_ci      // ERROR_INVALID_PARAMETER means the command line was formatted
134695b41eeSopenharmony_ci      // incorrectly. This can be caused by a command line being too long or
135695b41eeSopenharmony_ci      // leading whitespace in the command. Give extra context for this case.
136695b41eeSopenharmony_ci      if (error == ERROR_INVALID_PARAMETER) {
137695b41eeSopenharmony_ci        if (command.length() > 0 && (command[0] == ' ' || command[0] == '\t'))
138695b41eeSopenharmony_ci          hint = "command contains leading whitespace";
139695b41eeSopenharmony_ci        else
140695b41eeSopenharmony_ci          hint = "is the command line too long?";
141695b41eeSopenharmony_ci      }
142695b41eeSopenharmony_ci      Win32Fatal("CreateProcess", hint);
143695b41eeSopenharmony_ci    }
144695b41eeSopenharmony_ci  }
145695b41eeSopenharmony_ci
146695b41eeSopenharmony_ci  // Close pipe channel only used by the child.
147695b41eeSopenharmony_ci  if (child_pipe)
148695b41eeSopenharmony_ci    CloseHandle(child_pipe);
149695b41eeSopenharmony_ci  CloseHandle(nul);
150695b41eeSopenharmony_ci
151695b41eeSopenharmony_ci  CloseHandle(process_info.hThread);
152695b41eeSopenharmony_ci  child_ = process_info.hProcess;
153695b41eeSopenharmony_ci
154695b41eeSopenharmony_ci  return true;
155695b41eeSopenharmony_ci}
156695b41eeSopenharmony_ci
157695b41eeSopenharmony_civoid Subprocess::OnPipeReady() {
158695b41eeSopenharmony_ci  DWORD bytes;
159695b41eeSopenharmony_ci  if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
160695b41eeSopenharmony_ci    if (GetLastError() == ERROR_BROKEN_PIPE) {
161695b41eeSopenharmony_ci      CloseHandle(pipe_);
162695b41eeSopenharmony_ci      pipe_ = NULL;
163695b41eeSopenharmony_ci      return;
164695b41eeSopenharmony_ci    }
165695b41eeSopenharmony_ci    Win32Fatal("GetOverlappedResult");
166695b41eeSopenharmony_ci  }
167695b41eeSopenharmony_ci
168695b41eeSopenharmony_ci  if (is_reading_ && bytes)
169695b41eeSopenharmony_ci    buf_.append(overlapped_buf_, bytes);
170695b41eeSopenharmony_ci
171695b41eeSopenharmony_ci  memset(&overlapped_, 0, sizeof(overlapped_));
172695b41eeSopenharmony_ci  is_reading_ = true;
173695b41eeSopenharmony_ci  if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
174695b41eeSopenharmony_ci                  &bytes, &overlapped_)) {
175695b41eeSopenharmony_ci    if (GetLastError() == ERROR_BROKEN_PIPE) {
176695b41eeSopenharmony_ci      CloseHandle(pipe_);
177695b41eeSopenharmony_ci      pipe_ = NULL;
178695b41eeSopenharmony_ci      return;
179695b41eeSopenharmony_ci    }
180695b41eeSopenharmony_ci    if (GetLastError() != ERROR_IO_PENDING)
181695b41eeSopenharmony_ci      Win32Fatal("ReadFile");
182695b41eeSopenharmony_ci  }
183695b41eeSopenharmony_ci
184695b41eeSopenharmony_ci  // Even if we read any bytes in the readfile call, we'll enter this
185695b41eeSopenharmony_ci  // function again later and get them at that point.
186695b41eeSopenharmony_ci}
187695b41eeSopenharmony_ci
188695b41eeSopenharmony_ciExitStatus Subprocess::Finish() {
189695b41eeSopenharmony_ci  if (!child_)
190695b41eeSopenharmony_ci    return ExitFailure;
191695b41eeSopenharmony_ci
192695b41eeSopenharmony_ci  // TODO: add error handling for all of these.
193695b41eeSopenharmony_ci  WaitForSingleObject(child_, INFINITE);
194695b41eeSopenharmony_ci
195695b41eeSopenharmony_ci  DWORD exit_code = 0;
196695b41eeSopenharmony_ci  GetExitCodeProcess(child_, &exit_code);
197695b41eeSopenharmony_ci
198695b41eeSopenharmony_ci  CloseHandle(child_);
199695b41eeSopenharmony_ci  child_ = NULL;
200695b41eeSopenharmony_ci
201695b41eeSopenharmony_ci  return exit_code == 0              ? ExitSuccess :
202695b41eeSopenharmony_ci         exit_code == CONTROL_C_EXIT ? ExitInterrupted :
203695b41eeSopenharmony_ci                                       ExitFailure;
204695b41eeSopenharmony_ci}
205695b41eeSopenharmony_ci
206695b41eeSopenharmony_cibool Subprocess::Done() const {
207695b41eeSopenharmony_ci  return pipe_ == NULL;
208695b41eeSopenharmony_ci}
209695b41eeSopenharmony_ci
210695b41eeSopenharmony_ciconst string& Subprocess::GetOutput() const {
211695b41eeSopenharmony_ci  return buf_;
212695b41eeSopenharmony_ci}
213695b41eeSopenharmony_ci
214695b41eeSopenharmony_ciHANDLE SubprocessSet::ioport_;
215695b41eeSopenharmony_ci
216695b41eeSopenharmony_ciSubprocessSet::SubprocessSet() {
217695b41eeSopenharmony_ci  ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
218695b41eeSopenharmony_ci  if (!ioport_)
219695b41eeSopenharmony_ci    Win32Fatal("CreateIoCompletionPort");
220695b41eeSopenharmony_ci  if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
221695b41eeSopenharmony_ci    Win32Fatal("SetConsoleCtrlHandler");
222695b41eeSopenharmony_ci}
223695b41eeSopenharmony_ci
224695b41eeSopenharmony_ciSubprocessSet::~SubprocessSet() {
225695b41eeSopenharmony_ci  Clear();
226695b41eeSopenharmony_ci
227695b41eeSopenharmony_ci  SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
228695b41eeSopenharmony_ci  CloseHandle(ioport_);
229695b41eeSopenharmony_ci}
230695b41eeSopenharmony_ci
231695b41eeSopenharmony_ciBOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
232695b41eeSopenharmony_ci  if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
233695b41eeSopenharmony_ci    if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
234695b41eeSopenharmony_ci      Win32Fatal("PostQueuedCompletionStatus");
235695b41eeSopenharmony_ci    return TRUE;
236695b41eeSopenharmony_ci  }
237695b41eeSopenharmony_ci
238695b41eeSopenharmony_ci  return FALSE;
239695b41eeSopenharmony_ci}
240695b41eeSopenharmony_ci
241695b41eeSopenharmony_ciSubprocess *SubprocessSet::Add(const string& command, bool use_console) {
242695b41eeSopenharmony_ci  Subprocess *subprocess = new Subprocess(use_console);
243695b41eeSopenharmony_ci  if (!subprocess->Start(this, command)) {
244695b41eeSopenharmony_ci    delete subprocess;
245695b41eeSopenharmony_ci    return 0;
246695b41eeSopenharmony_ci  }
247695b41eeSopenharmony_ci  if (subprocess->child_)
248695b41eeSopenharmony_ci    running_.push_back(subprocess);
249695b41eeSopenharmony_ci  else
250695b41eeSopenharmony_ci    finished_.push(subprocess);
251695b41eeSopenharmony_ci  return subprocess;
252695b41eeSopenharmony_ci}
253695b41eeSopenharmony_ci
254695b41eeSopenharmony_cibool SubprocessSet::DoWork() {
255695b41eeSopenharmony_ci  DWORD bytes_read;
256695b41eeSopenharmony_ci  Subprocess* subproc;
257695b41eeSopenharmony_ci  OVERLAPPED* overlapped;
258695b41eeSopenharmony_ci
259695b41eeSopenharmony_ci  if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
260695b41eeSopenharmony_ci                                 &overlapped, INFINITE)) {
261695b41eeSopenharmony_ci    if (GetLastError() != ERROR_BROKEN_PIPE)
262695b41eeSopenharmony_ci      Win32Fatal("GetQueuedCompletionStatus");
263695b41eeSopenharmony_ci  }
264695b41eeSopenharmony_ci
265695b41eeSopenharmony_ci  if (!subproc) // A NULL subproc indicates that we were interrupted and is
266695b41eeSopenharmony_ci                // delivered by NotifyInterrupted above.
267695b41eeSopenharmony_ci    return true;
268695b41eeSopenharmony_ci
269695b41eeSopenharmony_ci  subproc->OnPipeReady();
270695b41eeSopenharmony_ci
271695b41eeSopenharmony_ci  if (subproc->Done()) {
272695b41eeSopenharmony_ci    vector<Subprocess*>::iterator end =
273695b41eeSopenharmony_ci        remove(running_.begin(), running_.end(), subproc);
274695b41eeSopenharmony_ci    if (running_.end() != end) {
275695b41eeSopenharmony_ci      finished_.push(subproc);
276695b41eeSopenharmony_ci      running_.resize(end - running_.begin());
277695b41eeSopenharmony_ci    }
278695b41eeSopenharmony_ci  }
279695b41eeSopenharmony_ci
280695b41eeSopenharmony_ci  return false;
281695b41eeSopenharmony_ci}
282695b41eeSopenharmony_ci
283695b41eeSopenharmony_ciSubprocess* SubprocessSet::NextFinished() {
284695b41eeSopenharmony_ci  if (finished_.empty())
285695b41eeSopenharmony_ci    return NULL;
286695b41eeSopenharmony_ci  Subprocess* subproc = finished_.front();
287695b41eeSopenharmony_ci  finished_.pop();
288695b41eeSopenharmony_ci  return subproc;
289695b41eeSopenharmony_ci}
290695b41eeSopenharmony_ci
291695b41eeSopenharmony_civoid SubprocessSet::Clear() {
292695b41eeSopenharmony_ci  for (vector<Subprocess*>::iterator i = running_.begin();
293695b41eeSopenharmony_ci       i != running_.end(); ++i) {
294695b41eeSopenharmony_ci    // Since the foreground process is in our process group, it will receive a
295695b41eeSopenharmony_ci    // CTRL_C_EVENT or CTRL_BREAK_EVENT at the same time as us.
296695b41eeSopenharmony_ci    if ((*i)->child_ && !(*i)->use_console_) {
297695b41eeSopenharmony_ci      if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
298695b41eeSopenharmony_ci                                    GetProcessId((*i)->child_))) {
299695b41eeSopenharmony_ci        Win32Fatal("GenerateConsoleCtrlEvent");
300695b41eeSopenharmony_ci      }
301695b41eeSopenharmony_ci    }
302695b41eeSopenharmony_ci  }
303695b41eeSopenharmony_ci  for (vector<Subprocess*>::iterator i = running_.begin();
304695b41eeSopenharmony_ci       i != running_.end(); ++i)
305695b41eeSopenharmony_ci    delete *i;
306695b41eeSopenharmony_ci  running_.clear();
307695b41eeSopenharmony_ci}
308