|
Ninja
|
00001 // Copyright 2011 Google Inc. All Rights Reserved. 00002 // 00003 // Licensed under the Apache License, Version 2.0 (the "License"); 00004 // you may not use this file except in compliance with the License. 00005 // You may obtain a copy of the License at 00006 // 00007 // http://www.apache.org/licenses/LICENSE-2.0 00008 // 00009 // Unless required by applicable law or agreed to in writing, software 00010 // distributed under the License is distributed on an "AS IS" BASIS, 00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 // See the License for the specific language governing permissions and 00013 // limitations under the License. 00014 00015 #include "subprocess.h" 00016 00017 #include <stdio.h> 00018 #include <windows.h> 00019 00020 #include <algorithm> 00021 00022 #include "util.h" 00023 00024 namespace { 00025 00026 void Win32Fatal(const char* function) { 00027 DWORD err = GetLastError(); 00028 00029 char* msg_buf; 00030 FormatMessageA( 00031 FORMAT_MESSAGE_ALLOCATE_BUFFER | 00032 FORMAT_MESSAGE_FROM_SYSTEM | 00033 FORMAT_MESSAGE_IGNORE_INSERTS, 00034 NULL, 00035 err, 00036 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 00037 (char*)&msg_buf, 00038 0, 00039 NULL); 00040 Fatal("%s: %s", function, msg_buf); 00041 LocalFree(msg_buf); 00042 } 00043 00044 } // anonymous namespace 00045 00046 Subprocess::Subprocess() : child_(NULL) , overlapped_() { 00047 } 00048 00049 Subprocess::~Subprocess() { 00050 // Reap child if forgotten. 00051 if (child_) 00052 Finish(); 00053 } 00054 00055 HANDLE Subprocess::SetupPipe(HANDLE ioport) { 00056 char pipe_name[100]; 00057 snprintf(pipe_name, sizeof(pipe_name), 00058 "\\\\.\\pipe\\ninja_pid%u_sp%p", GetProcessId(GetCurrentProcess()), this); 00059 00060 pipe_ = ::CreateNamedPipeA(pipe_name, 00061 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 00062 PIPE_TYPE_BYTE, 00063 PIPE_UNLIMITED_INSTANCES, 00064 0, 0, INFINITE, NULL); 00065 if (pipe_ == INVALID_HANDLE_VALUE) 00066 Win32Fatal("CreateNamedPipe"); 00067 00068 if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0)) 00069 Win32Fatal("CreateIoCompletionPort"); 00070 00071 memset(&overlapped_, 0, sizeof(overlapped_)); 00072 if (!ConnectNamedPipe(pipe_, &overlapped_) && 00073 GetLastError() != ERROR_IO_PENDING) { 00074 Win32Fatal("ConnectNamedPipe"); 00075 } 00076 00077 // Get the write end of the pipe as a handle inheritable across processes. 00078 HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0, 00079 NULL, OPEN_EXISTING, 0, NULL); 00080 HANDLE output_write_child; 00081 if (!DuplicateHandle(GetCurrentProcess(), output_write_handle, 00082 GetCurrentProcess(), &output_write_child, 00083 0, TRUE, DUPLICATE_SAME_ACCESS)) { 00084 Win32Fatal("DuplicateHandle"); 00085 } 00086 CloseHandle(output_write_handle); 00087 00088 return output_write_child; 00089 } 00090 00091 bool Subprocess::Start(SubprocessSet* set, const string& command) { 00092 HANDLE child_pipe = SetupPipe(set->ioport_); 00093 00094 STARTUPINFOA startup_info = {}; 00095 startup_info.cb = sizeof(STARTUPINFO); 00096 startup_info.dwFlags = STARTF_USESTDHANDLES; 00097 startup_info.hStdOutput = child_pipe; 00098 // TODO: what does this hook up stdin to? 00099 startup_info.hStdInput = NULL; 00100 // TODO: is it ok to reuse pipe like this? 00101 startup_info.hStdError = child_pipe; 00102 00103 PROCESS_INFORMATION process_info; 00104 00105 // Do not prepend 'cmd /c' on Windows, this breaks command 00106 // lines greater than 8,191 chars. 00107 if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL, 00108 /* inherit handles */ TRUE, 0, 00109 NULL, NULL, 00110 &startup_info, &process_info)) { 00111 Win32Fatal("CreateProcess"); 00112 } 00113 00114 // Close pipe channel only used by the child. 00115 if (child_pipe) 00116 CloseHandle(child_pipe); 00117 00118 CloseHandle(process_info.hThread); 00119 child_ = process_info.hProcess; 00120 00121 return true; 00122 } 00123 00124 void Subprocess::OnPipeReady() { 00125 DWORD bytes; 00126 if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) { 00127 if (GetLastError() == ERROR_BROKEN_PIPE) { 00128 CloseHandle(pipe_); 00129 pipe_ = NULL; 00130 return; 00131 } 00132 Win32Fatal("GetOverlappedResult"); 00133 } 00134 00135 if (bytes) 00136 buf_.append(overlapped_buf_, bytes); 00137 00138 memset(&overlapped_, 0, sizeof(overlapped_)); 00139 if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_), 00140 &bytes, &overlapped_)) { 00141 if (GetLastError() == ERROR_BROKEN_PIPE) { 00142 CloseHandle(pipe_); 00143 pipe_ = NULL; 00144 return; 00145 } 00146 if (GetLastError() != ERROR_IO_PENDING) 00147 Win32Fatal("ReadFile"); 00148 } 00149 00150 // Even if we read any bytes in the readfile call, we'll enter this 00151 // function again later and get them at that point. 00152 } 00153 00154 bool Subprocess::Finish() { 00155 // TODO: add error handling for all of these. 00156 WaitForSingleObject(child_, INFINITE); 00157 00158 DWORD exit_code = 0; 00159 GetExitCodeProcess(child_, &exit_code); 00160 00161 CloseHandle(child_); 00162 child_ = NULL; 00163 00164 return exit_code == 0; 00165 } 00166 00167 bool Subprocess::Done() const { 00168 return pipe_ == NULL; 00169 } 00170 00171 const string& Subprocess::GetOutput() const { 00172 return buf_; 00173 } 00174 00175 SubprocessSet::SubprocessSet() { 00176 ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); 00177 if (!ioport_) 00178 Win32Fatal("CreateIoCompletionPort"); 00179 } 00180 00181 SubprocessSet::~SubprocessSet() { 00182 CloseHandle(ioport_); 00183 } 00184 00185 void SubprocessSet::Add(Subprocess* subprocess) { 00186 running_.push_back(subprocess); 00187 } 00188 00189 void SubprocessSet::DoWork() { 00190 DWORD bytes_read; 00191 Subprocess* subproc; 00192 OVERLAPPED* overlapped; 00193 00194 if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc, 00195 &overlapped, INFINITE)) { 00196 if (GetLastError() != ERROR_BROKEN_PIPE) 00197 Win32Fatal("GetQueuedCompletionStatus"); 00198 } 00199 00200 subproc->OnPipeReady(); 00201 00202 if (subproc->Done()) { 00203 vector<Subprocess*>::iterator end = 00204 std::remove(running_.begin(), running_.end(), subproc); 00205 if (running_.end() != end) { 00206 finished_.push(subproc); 00207 running_.resize(end - running_.begin()); 00208 } 00209 } 00210 } 00211 00212 Subprocess* SubprocessSet::NextFinished() { 00213 if (finished_.empty()) 00214 return NULL; 00215 Subprocess* subproc = finished_.front(); 00216 finished_.pop(); 00217 return subproc; 00218 }
1.7.5.1