SystemUtils.cpp revision f7066c76d268161ecbaad2e7b161c2003818d071
1//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
2//
3// This file contains functions used to do a variety of low-level, often
4// system-specific, tasks.
5//
6//===----------------------------------------------------------------------===//
7
8#include "Support/SystemUtils.h"
9#include <algorithm>
10#include <fstream>
11#include <iostream>
12#include <cstdlib>
13#include "Config/sys/types.h"
14#include "Config/sys/stat.h"
15#include "Config/fcntl.h"
16#include "Config/sys/wait.h"
17#include "Config/unistd.h"
18#include "Config/errno.h"
19
20/// isExecutableFile - This function returns true if the filename specified
21/// exists and is executable.
22///
23bool isExecutableFile(const std::string &ExeFileName) {
24  struct stat Buf;
25  if (stat(ExeFileName.c_str(), &Buf))
26    return false;  // Must not be executable!
27
28  if (!(Buf.st_mode & S_IFREG))
29    return false;                    // Not a regular file?
30
31  if (Buf.st_uid == getuid())        // Owner of file?
32    return Buf.st_mode & S_IXUSR;
33  else if (Buf.st_gid == getgid())   // In group of file?
34    return Buf.st_mode & S_IXGRP;
35  else                               // Unrelated to file?
36    return Buf.st_mode & S_IXOTH;
37}
38
39
40/// FindExecutable - Find a named executable, giving the argv[0] of program
41/// being executed. This allows us to find another LLVM tool if it is built into
42/// the same directory, but that directory is neither the current directory, nor
43/// in the PATH.  If the executable cannot be found, return an empty string.
44///
45std::string FindExecutable(const std::string &ExeName,
46			   const std::string &ProgramPath) {
47  // First check the directory that bugpoint is in.  We can do this if
48  // BugPointPath contains at least one / character, indicating that it is a
49  // relative path to bugpoint itself.
50  //
51  std::string Result = ProgramPath;
52  while (!Result.empty() && Result[Result.size()-1] != '/')
53    Result.erase(Result.size()-1, 1);
54
55  if (!Result.empty()) {
56    Result += ExeName;
57    if (isExecutableFile(Result)) return Result; // Found it?
58  }
59
60  // Okay, if the path to the program didn't tell us anything, try using the
61  // PATH environment variable.
62  const char *PathStr = getenv("PATH");
63  if (PathStr == 0) return "";
64
65  // Now we have a colon separated list of directories to search... try them...
66  unsigned PathLen = strlen(PathStr);
67  while (PathLen) {
68    // Find the first colon...
69    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
70
71    // Check to see if this first directory contains the executable...
72    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
73    if (isExecutableFile(FilePath))
74      return FilePath;                    // Found the executable!
75
76    // Nope it wasn't in this directory, check the next range!
77    PathLen -= Colon-PathStr;
78    PathStr = Colon;
79    while (*PathStr == ':') {   // Advance past colons
80      PathStr++;
81      PathLen--;
82    }
83  }
84
85  // If we fell out, we ran out of directories in PATH to search, return failure
86  return "";
87}
88
89static void RedirectFD(const std::string &File, int FD) {
90  if (File.empty()) return;  // Noop
91
92  // Open the file
93  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
94  if (InFD == -1) {
95    std::cerr << "Error opening file '" << File << "' for "
96	      << (FD == 0 ? "input" : "output") << "!\n";
97    exit(1);
98  }
99
100  dup2(InFD, FD);   // Install it as the requested FD
101  close(InFD);      // Close the original FD
102}
103
104/// RunProgramWithTimeout - This function executes the specified program, with
105/// the specified null-terminated argument array, with the stdin/out/err fd's
106/// redirected, with a timeout specified on the commandline.  This terminates
107/// the calling program if there is an error executing the specified program.
108/// It returns the return value of the program, or -1 if a timeout is detected.
109///
110int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args,
111			  const std::string &StdInFile,
112			  const std::string &StdOutFile,
113			  const std::string &StdErrFile) {
114
115  // FIXME: install sigalarm handler here for timeout...
116
117  int Child = fork();
118  switch (Child) {
119  case -1:
120    std::cerr << "ERROR forking!\n";
121    exit(1);
122  case 0:               // Child
123    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
124    RedirectFD(StdOutFile, 1);
125    RedirectFD(StdErrFile, 2);
126
127    execv(ProgramPath.c_str(), (char *const *)Args);
128    std::cerr << "Error executing program '" << ProgramPath;
129    for (; *Args; ++Args)
130      std::cerr << " " << *Args;
131    exit(1);
132
133  default: break;
134  }
135
136  // Make sure all output has been written while waiting
137  std::cout << std::flush;
138
139  int Status;
140  if (wait(&Status) != Child) {
141    if (errno == EINTR) {
142      static bool FirstTimeout = true;
143      if (FirstTimeout) {
144	std::cout <<
145 "*** Program execution timed out!  This mechanism is designed to handle\n"
146 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
147 "    can be used to change the timeout threshold or disable it completely\n"
148 "    (with -timeout=0).  This message is only displayed once.\n";
149	FirstTimeout = false;
150      }
151      return -1;   // Timeout detected
152    }
153
154    std::cerr << "Error waiting for child process!\n";
155    exit(1);
156  }
157  return Status;
158}
159