Ninja
subprocess-win32.cc
Go to the documentation of this file.
00001 // Copyright 2012 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 
00019 #include <algorithm>
00020 
00021 #include "util.h"
00022 
00023 Subprocess::Subprocess() : child_(NULL) , overlapped_(), is_reading_(false) {
00024 }
00025 
00026 Subprocess::~Subprocess() {
00027   if (pipe_) {
00028     if (!CloseHandle(pipe_))
00029       Win32Fatal("CloseHandle");
00030   }
00031   // Reap child if forgotten.
00032   if (child_)
00033     Finish();
00034 }
00035 
00036 HANDLE Subprocess::SetupPipe(HANDLE ioport) {
00037   char pipe_name[100];
00038   snprintf(pipe_name, sizeof(pipe_name),
00039            "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this);
00040 
00041   pipe_ = ::CreateNamedPipeA(pipe_name,
00042                              PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
00043                              PIPE_TYPE_BYTE,
00044                              PIPE_UNLIMITED_INSTANCES,
00045                              0, 0, INFINITE, NULL);
00046   if (pipe_ == INVALID_HANDLE_VALUE)
00047     Win32Fatal("CreateNamedPipe");
00048 
00049   if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))
00050     Win32Fatal("CreateIoCompletionPort");
00051 
00052   memset(&overlapped_, 0, sizeof(overlapped_));
00053   if (!ConnectNamedPipe(pipe_, &overlapped_) &&
00054       GetLastError() != ERROR_IO_PENDING) {
00055     Win32Fatal("ConnectNamedPipe");
00056   }
00057 
00058   // Get the write end of the pipe as a handle inheritable across processes.
00059   HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0,
00060                                           NULL, OPEN_EXISTING, 0, NULL);
00061   HANDLE output_write_child;
00062   if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
00063                        GetCurrentProcess(), &output_write_child,
00064                        0, TRUE, DUPLICATE_SAME_ACCESS)) {
00065     Win32Fatal("DuplicateHandle");
00066   }
00067   CloseHandle(output_write_handle);
00068 
00069   return output_write_child;
00070 }
00071 
00072 bool Subprocess::Start(SubprocessSet* set, const string& command) {
00073   HANDLE child_pipe = SetupPipe(set->ioport_);
00074 
00075   SECURITY_ATTRIBUTES security_attributes;
00076   memset(&security_attributes, 0, sizeof(SECURITY_ATTRIBUTES));
00077   security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
00078   security_attributes.bInheritHandle = TRUE;
00079   // Must be inheritable so subprocesses can dup to children.
00080   HANDLE nul = CreateFile("NUL", GENERIC_READ,
00081           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
00082           &security_attributes, OPEN_EXISTING, 0, NULL);
00083   if (nul == INVALID_HANDLE_VALUE)
00084     Fatal("couldn't open nul");
00085 
00086   STARTUPINFOA startup_info;
00087   memset(&startup_info, 0, sizeof(startup_info));
00088   startup_info.cb = sizeof(STARTUPINFO);
00089   startup_info.dwFlags = STARTF_USESTDHANDLES;
00090   startup_info.hStdInput = nul;
00091   startup_info.hStdOutput = child_pipe;
00092   startup_info.hStdError = child_pipe;
00093 
00094   PROCESS_INFORMATION process_info;
00095   memset(&process_info, 0, sizeof(process_info));
00096 
00097   // Do not prepend 'cmd /c' on Windows, this breaks command
00098   // lines greater than 8,191 chars.
00099   if (!CreateProcessA(NULL, (char*)command.c_str(), NULL, NULL,
00100                       /* inherit handles */ TRUE, CREATE_NEW_PROCESS_GROUP,
00101                       NULL, NULL,
00102                       &startup_info, &process_info)) {
00103     DWORD error = GetLastError();
00104     if (error == ERROR_FILE_NOT_FOUND) {
00105       // File (program) not found error is treated as a normal build
00106       // action failure.
00107       if (child_pipe)
00108         CloseHandle(child_pipe);
00109       CloseHandle(pipe_);
00110       CloseHandle(nul);
00111       pipe_ = NULL;
00112       // child_ is already NULL;
00113       buf_ = "CreateProcess failed: The system cannot find the file "
00114           "specified.\n";
00115       return true;
00116     } else {
00117       Win32Fatal("CreateProcess");    // pass all other errors to Win32Fatal
00118     }
00119   }
00120 
00121   // Close pipe channel only used by the child.
00122   if (child_pipe)
00123     CloseHandle(child_pipe);
00124   CloseHandle(nul);
00125 
00126   CloseHandle(process_info.hThread);
00127   child_ = process_info.hProcess;
00128 
00129   return true;
00130 }
00131 
00132 void Subprocess::OnPipeReady() {
00133   DWORD bytes;
00134   if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
00135     if (GetLastError() == ERROR_BROKEN_PIPE) {
00136       CloseHandle(pipe_);
00137       pipe_ = NULL;
00138       return;
00139     }
00140     Win32Fatal("GetOverlappedResult");
00141   }
00142 
00143   if (is_reading_ && bytes)
00144     buf_.append(overlapped_buf_, bytes);
00145 
00146   memset(&overlapped_, 0, sizeof(overlapped_));
00147   is_reading_ = true;
00148   if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
00149                   &bytes, &overlapped_)) {
00150     if (GetLastError() == ERROR_BROKEN_PIPE) {
00151       CloseHandle(pipe_);
00152       pipe_ = NULL;
00153       return;
00154     }
00155     if (GetLastError() != ERROR_IO_PENDING)
00156       Win32Fatal("ReadFile");
00157   }
00158 
00159   // Even if we read any bytes in the readfile call, we'll enter this
00160   // function again later and get them at that point.
00161 }
00162 
00163 ExitStatus Subprocess::Finish() {
00164   if (!child_)
00165     return ExitFailure;
00166 
00167   // TODO: add error handling for all of these.
00168   WaitForSingleObject(child_, INFINITE);
00169 
00170   DWORD exit_code = 0;
00171   GetExitCodeProcess(child_, &exit_code);
00172 
00173   CloseHandle(child_);
00174   child_ = NULL;
00175 
00176   return exit_code == 0              ? ExitSuccess :
00177          exit_code == CONTROL_C_EXIT ? ExitInterrupted :
00178                                        ExitFailure;
00179 }
00180 
00181 bool Subprocess::Done() const {
00182   return pipe_ == NULL;
00183 }
00184 
00185 const string& Subprocess::GetOutput() const {
00186   return buf_;
00187 }
00188 
00189 HANDLE SubprocessSet::ioport_;
00190 
00191 SubprocessSet::SubprocessSet() {
00192   ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
00193   if (!ioport_)
00194     Win32Fatal("CreateIoCompletionPort");
00195   if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
00196     Win32Fatal("SetConsoleCtrlHandler");
00197 }
00198 
00199 SubprocessSet::~SubprocessSet() {
00200   Clear();
00201 
00202   SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
00203   CloseHandle(ioport_);
00204 }
00205 
00206 BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
00207   if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
00208     if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
00209       Win32Fatal("PostQueuedCompletionStatus");
00210     return TRUE;
00211   }
00212 
00213   return FALSE;
00214 }
00215 
00216 Subprocess *SubprocessSet::Add(const string& command) {
00217   Subprocess *subprocess = new Subprocess;
00218   if (!subprocess->Start(this, command)) {
00219     delete subprocess;
00220     return 0;
00221   }
00222   if (subprocess->child_)
00223     running_.push_back(subprocess);
00224   else
00225     finished_.push(subprocess);
00226   return subprocess;
00227 }
00228 
00229 bool SubprocessSet::DoWork() {
00230   DWORD bytes_read;
00231   Subprocess* subproc;
00232   OVERLAPPED* overlapped;
00233 
00234   if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
00235                                  &overlapped, INFINITE)) {
00236     if (GetLastError() != ERROR_BROKEN_PIPE)
00237       Win32Fatal("GetQueuedCompletionStatus");
00238   }
00239 
00240   if (!subproc) // A NULL subproc indicates that we were interrupted and is
00241                 // delivered by NotifyInterrupted above.
00242     return true;
00243 
00244   subproc->OnPipeReady();
00245 
00246   if (subproc->Done()) {
00247     vector<Subprocess*>::iterator end =
00248         std::remove(running_.begin(), running_.end(), subproc);
00249     if (running_.end() != end) {
00250       finished_.push(subproc);
00251       running_.resize(end - running_.begin());
00252     }
00253   }
00254 
00255   return false;
00256 }
00257 
00258 Subprocess* SubprocessSet::NextFinished() {
00259   if (finished_.empty())
00260     return NULL;
00261   Subprocess* subproc = finished_.front();
00262   finished_.pop();
00263   return subproc;
00264 }
00265 
00266 void SubprocessSet::Clear() {
00267   for (vector<Subprocess*>::iterator i = running_.begin();
00268        i != running_.end(); ++i) {
00269     if ((*i)->child_) {
00270       if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
00271                                     GetProcessId((*i)->child_))) {
00272         Win32Fatal("GenerateConsoleCtrlEvent");
00273       }
00274     }
00275   }
00276   for (vector<Subprocess*>::iterator i = running_.begin();
00277        i != running_.end(); ++i)
00278     delete *i;
00279   running_.clear();
00280 }