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