Program.inc revision a0d8f28a9cbebfd3ddb32dfe3e0a286ede1678c5
1//===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file provides the Win32 specific implementation of the Program class.
11//
12//===----------------------------------------------------------------------===//
13
14#include "Windows.h"
15#include <cstdio>
16#include <fcntl.h>
17#include <io.h>
18#include <malloc.h>
19
20//===----------------------------------------------------------------------===//
21//=== WARNING: Implementation here must contain only Win32 specific code
22//===          and must not be UNIX code
23//===----------------------------------------------------------------------===//
24
25namespace {
26  struct Win32ProcessInfo {
27    HANDLE hProcess;
28    DWORD  dwProcessId;
29  };
30}
31
32namespace llvm {
33using namespace sys;
34
35Program::Program() : Data_(0) {}
36
37Program::~Program() {
38  if (Data_) {
39    Win32ProcessInfo* wpi = reinterpret_cast<Win32ProcessInfo*>(Data_);
40    CloseHandle(wpi->hProcess);
41    delete wpi;
42    Data_ = 0;
43  }
44}
45
46// This function just uses the PATH environment variable to find the program.
47Path
48Program::FindProgramByName(const std::string& progName) {
49
50  // Check some degenerate cases
51  if (progName.length() == 0) // no program
52    return Path();
53  Path temp;
54  if (!temp.set(progName)) // invalid name
55    return Path();
56  // Return paths with slashes verbatim.
57  if (progName.find('\\') != std::string::npos ||
58      progName.find('/') != std::string::npos)
59    return temp;
60
61  // At this point, the file name is valid and does not contain slashes.
62  // Let Windows search for it.
63  char buffer[MAX_PATH];
64  char *dummy = NULL;
65  DWORD len = SearchPath(NULL, progName.c_str(), ".exe", MAX_PATH,
66                         buffer, &dummy);
67
68  // See if it wasn't found.
69  if (len == 0)
70    return Path();
71
72  // See if we got the entire path.
73  if (len < MAX_PATH)
74    return Path(buffer);
75
76  // Buffer was too small; grow and retry.
77  while (true) {
78    char *b = reinterpret_cast<char *>(_alloca(len+1));
79    DWORD len2 = SearchPath(NULL, progName.c_str(), ".exe", len+1, b, &dummy);
80
81    // It is unlikely the search failed, but it's always possible some file
82    // was added or removed since the last search, so be paranoid...
83    if (len2 == 0)
84      return Path();
85    else if (len2 <= len)
86      return Path(b);
87
88    len = len2;
89  }
90}
91
92static HANDLE RedirectIO(const Path *path, int fd, std::string* ErrMsg) {
93  HANDLE h;
94  if (path == 0) {
95    DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
96                    GetCurrentProcess(), &h,
97                    0, TRUE, DUPLICATE_SAME_ACCESS);
98    return h;
99  }
100
101  const char *fname;
102  if (path->isEmpty())
103    fname = "NUL";
104  else
105    fname = path->c_str();
106
107  SECURITY_ATTRIBUTES sa;
108  sa.nLength = sizeof(sa);
109  sa.lpSecurityDescriptor = 0;
110  sa.bInheritHandle = TRUE;
111
112  h = CreateFile(fname, fd ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ,
113                 &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS,
114                 FILE_ATTRIBUTE_NORMAL, NULL);
115  if (h == INVALID_HANDLE_VALUE) {
116    MakeErrMsg(ErrMsg, std::string(fname) + ": Can't open file for " +
117        (fd ? "input: " : "output: "));
118  }
119
120  return h;
121}
122
123/// ArgNeedsQuotes - Check whether argument needs to be quoted when calling
124/// CreateProcess.
125static bool ArgNeedsQuotes(const char *Str) {
126  return Str[0] == '\0' || strpbrk(Str, "\t \"&\'()*<>\\`^|") != 0;
127}
128
129
130/// ArgLenWithQuotes - Check whether argument needs to be quoted when calling
131/// CreateProcess and returns length of quoted arg with escaped quotes
132static unsigned int ArgLenWithQuotes(const char *Str) {
133  unsigned int len = ArgNeedsQuotes(Str) ? 2 : 0;
134
135  while (*Str != '\0') {
136    if (*Str == '\"')
137      ++len;
138
139    ++len;
140    ++Str;
141  }
142
143  return len;
144}
145
146
147bool
148Program::Execute(const Path& path,
149                 const char** args,
150                 const char** envp,
151                 const Path** redirects,
152                 unsigned memoryLimit,
153                 std::string* ErrMsg) {
154  if (Data_) {
155    Win32ProcessInfo* wpi = reinterpret_cast<Win32ProcessInfo*>(Data_);
156    CloseHandle(wpi->hProcess);
157    delete wpi;
158    Data_ = 0;
159  }
160
161  if (!path.canExecute()) {
162    if (ErrMsg)
163      *ErrMsg = "program not executable";
164    return false;
165  }
166
167  // Windows wants a command line, not an array of args, to pass to the new
168  // process.  We have to concatenate them all, while quoting the args that
169  // have embedded spaces (or are empty).
170
171  // First, determine the length of the command line.
172  unsigned len = 0;
173  for (unsigned i = 0; args[i]; i++) {
174    len += ArgLenWithQuotes(args[i]) + 1;
175  }
176
177  // Now build the command line.
178  char *command = reinterpret_cast<char *>(_alloca(len+1));
179  char *p = command;
180
181  for (unsigned i = 0; args[i]; i++) {
182    const char *arg = args[i];
183
184    bool needsQuoting = ArgNeedsQuotes(arg);
185    if (needsQuoting)
186      *p++ = '"';
187
188    while (*arg != '\0') {
189      if (*arg == '\"')
190        *p++ = '\\';
191
192      *p++ = *arg++;
193    }
194
195    if (needsQuoting)
196      *p++ = '"';
197    *p++ = ' ';
198  }
199
200  *p = 0;
201
202  // The pointer to the environment block for the new process.
203  char *envblock = 0;
204
205  if (envp) {
206    // An environment block consists of a null-terminated block of
207    // null-terminated strings. Convert the array of environment variables to
208    // an environment block by concatenating them.
209
210    // First, determine the length of the environment block.
211    len = 0;
212    for (unsigned i = 0; envp[i]; i++)
213      len += strlen(envp[i]) + 1;
214
215    // Now build the environment block.
216    envblock = reinterpret_cast<char *>(_alloca(len+1));
217    p = envblock;
218
219    for (unsigned i = 0; envp[i]; i++) {
220      const char *ev = envp[i];
221      size_t len = strlen(ev) + 1;
222      memcpy(p, ev, len);
223      p += len;
224    }
225
226    *p = 0;
227  }
228
229  // Create a child process.
230  STARTUPINFO si;
231  memset(&si, 0, sizeof(si));
232  si.cb = sizeof(si);
233  si.hStdInput = INVALID_HANDLE_VALUE;
234  si.hStdOutput = INVALID_HANDLE_VALUE;
235  si.hStdError = INVALID_HANDLE_VALUE;
236
237  if (redirects) {
238    si.dwFlags = STARTF_USESTDHANDLES;
239
240    si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg);
241    if (si.hStdInput == INVALID_HANDLE_VALUE) {
242      MakeErrMsg(ErrMsg, "can't redirect stdin");
243      return false;
244    }
245    si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg);
246    if (si.hStdOutput == INVALID_HANDLE_VALUE) {
247      CloseHandle(si.hStdInput);
248      MakeErrMsg(ErrMsg, "can't redirect stdout");
249      return false;
250    }
251    if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) {
252      // If stdout and stderr should go to the same place, redirect stderr
253      // to the handle already open for stdout.
254      DuplicateHandle(GetCurrentProcess(), si.hStdOutput,
255                      GetCurrentProcess(), &si.hStdError,
256                      0, TRUE, DUPLICATE_SAME_ACCESS);
257    } else {
258      // Just redirect stderr
259      si.hStdError = RedirectIO(redirects[2], 2, ErrMsg);
260      if (si.hStdError == INVALID_HANDLE_VALUE) {
261        CloseHandle(si.hStdInput);
262        CloseHandle(si.hStdOutput);
263        MakeErrMsg(ErrMsg, "can't redirect stderr");
264        return false;
265      }
266    }
267  }
268
269  PROCESS_INFORMATION pi;
270  memset(&pi, 0, sizeof(pi));
271
272  fflush(stdout);
273  fflush(stderr);
274  BOOL rc = CreateProcess(path.c_str(), command, NULL, NULL, TRUE, 0,
275                          envblock, NULL, &si, &pi);
276  DWORD err = GetLastError();
277
278  // Regardless of whether the process got created or not, we are done with
279  // the handles we created for it to inherit.
280  CloseHandle(si.hStdInput);
281  CloseHandle(si.hStdOutput);
282  CloseHandle(si.hStdError);
283
284  // Now return an error if the process didn't get created.
285  if (!rc) {
286    SetLastError(err);
287    MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") +
288               path.str() + "'");
289    return false;
290  }
291  Win32ProcessInfo* wpi = new Win32ProcessInfo;
292  wpi->hProcess = pi.hProcess;
293  wpi->dwProcessId = pi.dwProcessId;
294  Data_ = wpi;
295
296  // Make sure these get closed no matter what.
297  ScopedCommonHandle hThread(pi.hThread);
298
299  // Assign the process to a job if a memory limit is defined.
300  ScopedJobHandle hJob;
301  if (memoryLimit != 0) {
302    hJob = CreateJobObject(0, 0);
303    bool success = false;
304    if (hJob) {
305      JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
306      memset(&jeli, 0, sizeof(jeli));
307      jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
308      jeli.ProcessMemoryLimit = uintptr_t(memoryLimit) * 1048576;
309      if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
310                                  &jeli, sizeof(jeli))) {
311        if (AssignProcessToJobObject(hJob, pi.hProcess))
312          success = true;
313      }
314    }
315    if (!success) {
316      SetLastError(GetLastError());
317      MakeErrMsg(ErrMsg, std::string("Unable to set memory limit"));
318      TerminateProcess(pi.hProcess, 1);
319      WaitForSingleObject(pi.hProcess, INFINITE);
320      return false;
321    }
322  }
323
324  return true;
325}
326
327int
328Program::Wait(const Path &path,
329              unsigned secondsToWait,
330              std::string* ErrMsg) {
331  if (Data_ == 0) {
332    MakeErrMsg(ErrMsg, "Process not started!");
333    return -1;
334  }
335
336  Win32ProcessInfo* wpi = reinterpret_cast<Win32ProcessInfo*>(Data_);
337  HANDLE hProcess = wpi->hProcess;
338
339  // Wait for the process to terminate.
340  DWORD millisecondsToWait = INFINITE;
341  if (secondsToWait > 0)
342    millisecondsToWait = secondsToWait * 1000;
343
344  if (WaitForSingleObject(hProcess, millisecondsToWait) == WAIT_TIMEOUT) {
345    if (!TerminateProcess(hProcess, 1)) {
346      MakeErrMsg(ErrMsg, "Failed to terminate timed-out program.");
347      // -2 indicates a crash or timeout as opposed to failure to execute.
348      return -2;
349    }
350    WaitForSingleObject(hProcess, INFINITE);
351  }
352
353  // Get its exit status.
354  DWORD status;
355  BOOL rc = GetExitCodeProcess(hProcess, &status);
356  DWORD err = GetLastError();
357
358  if (!rc) {
359    SetLastError(err);
360    MakeErrMsg(ErrMsg, "Failed getting status for program.");
361    // -2 indicates a crash or timeout as opposed to failure to execute.
362    return -2;
363  }
364
365  if (!status)
366    return 0;
367
368  // Pass 10(Warning) and 11(Error) to the callee as negative value.
369  if ((status & 0xBFFF0000U) == 0x80000000U)
370    return (int)status;
371
372  if (status & 0xFF)
373    return status & 0x7FFFFFFF;
374
375  return 1;
376}
377
378error_code Program::ChangeStdinToBinary(){
379  int result = _setmode( _fileno(stdin), _O_BINARY );
380  if (result == -1)
381    return error_code(errno, generic_category());
382  return make_error_code(errc::success);
383}
384
385error_code Program::ChangeStdoutToBinary(){
386  int result = _setmode( _fileno(stdout), _O_BINARY );
387  if (result == -1)
388    return error_code(errno, generic_category());
389  return make_error_code(errc::success);
390}
391
392error_code Program::ChangeStderrToBinary(){
393  int result = _setmode( _fileno(stderr), _O_BINARY );
394  if (result == -1)
395    return error_code(errno, generic_category());
396  return make_error_code(errc::success);
397}
398
399}
400