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