SystemUtils.cpp revision bf3d2e2683b41f8bc179fea943b462bc88a56f2f
1//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file was developed by the LLVM research group and is distributed under
6// the University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file contains functions used to do a variety of low-level, often
11// system-specific, tasks.
12//
13//===----------------------------------------------------------------------===//
14
15#include "Support/SystemUtils.h"
16#include "Config/sys/types.h"
17#include "Config/sys/stat.h"
18#include "Config/fcntl.h"
19#include "Config/sys/wait.h"
20#include "Config/unistd.h"
21#include "Config/config.h"
22#include <algorithm>
23#include <fstream>
24#include <iostream>
25#include <cstdlib>
26#include <cerrno>
27using namespace llvm;
28
29/// isExecutableFile - This function returns true if the filename specified
30/// exists and is executable.
31///
32bool llvm::isExecutableFile(const std::string &ExeFileName) {
33  struct stat Buf;
34  if (stat(ExeFileName.c_str(), &Buf))
35    return false;  // Must not be executable!
36
37  if (!(Buf.st_mode & S_IFREG))
38    return false;                    // Not a regular file?
39
40  if (Buf.st_uid == getuid())        // Owner of file?
41    return Buf.st_mode & S_IXUSR;
42  else if (Buf.st_gid == getgid())   // In group of file?
43    return Buf.st_mode & S_IXGRP;
44  else                               // Unrelated to file?
45    return Buf.st_mode & S_IXOTH;
46}
47
48/// isStandardOutAConsole - Return true if we can tell that the standard output
49/// stream goes to a terminal window or console.
50bool llvm::isStandardOutAConsole() {
51#if HAVE_ISATTY
52  return isatty(1);
53#endif
54  // If we don't have isatty, just return false.
55  return false;
56}
57
58
59/// FindExecutable - Find a named executable, giving the argv[0] of program
60/// being executed. This allows us to find another LLVM tool if it is built
61/// into the same directory, but that directory is neither the current
62/// directory, nor in the PATH.  If the executable cannot be found, return an
63/// empty string.
64///
65std::string llvm::FindExecutable(const std::string &ExeName,
66                                 const std::string &ProgramPath) {
67  // First check the directory that bugpoint is in.  We can do this if
68  // BugPointPath contains at least one / character, indicating that it is a
69  // relative path to bugpoint itself.
70  //
71  std::string Result = ProgramPath;
72  while (!Result.empty() && Result[Result.size()-1] != '/')
73    Result.erase(Result.size()-1, 1);
74
75  if (!Result.empty()) {
76    Result += ExeName;
77    if (isExecutableFile(Result)) return Result; // Found it?
78  }
79
80  // Okay, if the path to the program didn't tell us anything, try using the
81  // PATH environment variable.
82  const char *PathStr = getenv("PATH");
83  if (PathStr == 0) return "";
84
85  // Now we have a colon separated list of directories to search... try them...
86  unsigned PathLen = strlen(PathStr);
87  while (PathLen) {
88    // Find the first colon...
89    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
90
91    // Check to see if this first directory contains the executable...
92    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
93    if (isExecutableFile(FilePath))
94      return FilePath;                    // Found the executable!
95
96    // Nope it wasn't in this directory, check the next range!
97    PathLen -= Colon-PathStr;
98    PathStr = Colon;
99    while (*PathStr == ':') {   // Advance past colons
100      PathStr++;
101      PathLen--;
102    }
103  }
104
105  // If we fell out, we ran out of directories in PATH to search, return failure
106  return "";
107}
108
109static void RedirectFD(const std::string &File, int FD) {
110  if (File.empty()) return;  // Noop
111
112  // Open the file
113  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
114  if (InFD == -1) {
115    std::cerr << "Error opening file '" << File << "' for "
116              << (FD == 0 ? "input" : "output") << "!\n";
117    exit(1);
118  }
119
120  dup2(InFD, FD);   // Install it as the requested FD
121  close(InFD);      // Close the original FD
122}
123
124/// RunProgramWithTimeout - This function executes the specified program, with
125/// the specified null-terminated argument array, with the stdin/out/err fd's
126/// redirected, with a timeout specified on the command line.  This terminates
127/// the calling program if there is an error executing the specified program.
128/// It returns the return value of the program, or -1 if a timeout is detected.
129///
130int llvm::RunProgramWithTimeout(const std::string &ProgramPath,
131                                const char **Args,
132                                const std::string &StdInFile,
133                                const std::string &StdOutFile,
134                                const std::string &StdErrFile) {
135  // FIXME: install sigalarm handler here for timeout...
136
137  int Child = fork();
138  switch (Child) {
139  case -1:
140    std::cerr << "ERROR forking!\n";
141    exit(1);
142  case 0:               // Child
143    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
144    RedirectFD(StdOutFile, 1);
145    if (StdOutFile != StdErrFile)
146      RedirectFD(StdErrFile, 2);
147    else
148      dup2(1, 2);
149
150    execv(ProgramPath.c_str(), (char *const *)Args);
151    std::cerr << "Error executing program: '" << ProgramPath;
152    for (; *Args; ++Args)
153      std::cerr << " " << *Args;
154    std::cerr << "'\n";
155    exit(1);
156
157  default: break;
158  }
159
160  // Make sure all output has been written while waiting
161  std::cout << std::flush;
162
163  int Status;
164  if (wait(&Status) != Child) {
165    if (errno == EINTR) {
166      static bool FirstTimeout = true;
167      if (FirstTimeout) {
168        std::cout <<
169 "*** Program execution timed out!  This mechanism is designed to handle\n"
170 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
171 "    can be used to change the timeout threshold or disable it completely\n"
172 "    (with -timeout=0).  This message is only displayed once.\n";
173        FirstTimeout = false;
174      }
175      return -1;   // Timeout detected
176    }
177
178    std::cerr << "Error waiting for child process!\n";
179    exit(1);
180  }
181  return Status;
182}
183
184
185//
186// Function: ExecWait ()
187//
188// Description:
189//  This function executes a program with the specified arguments and
190//  environment.  It then waits for the progarm to termiante and then returns
191//  to the caller.
192//
193// Inputs:
194//  argv - The arguments to the program as an array of C strings.  The first
195//         argument should be the name of the program to execute, and the
196//         last argument should be a pointer to NULL.
197//
198//  envp - The environment passes to the program as an array of C strings in
199//         the form of "name=value" pairs.  The last element should be a
200//         pointer to NULL.
201//
202// Outputs:
203//  None.
204//
205// Return value:
206//  0 - No errors.
207//  1 - The program could not be executed.
208//  1 - The program returned a non-zero exit status.
209//  1 - The program terminated abnormally.
210//
211// Notes:
212//  The program will inherit the stdin, stdout, and stderr file descriptors
213//  as well as other various configuration settings (umask).
214//
215//  This function should not print anything to stdout/stderr on its own.  It is
216//  a generic library function.  The caller or executed program should report
217//  errors in the way it sees fit.
218//
219//  This function does not use $PATH to find programs.
220//
221int llvm::ExecWait(const char * const old_argv[],
222                   const char * const old_envp[]) {
223  // Child process ID
224  register int child;
225
226  // Status from child process when it exits
227  int status;
228
229  //
230  // Create local versions of the parameters that can be passed into execve()
231  // without creating const problems.
232  //
233  char ** const argv = (char ** const) old_argv;
234  char ** const envp = (char ** const) old_envp;
235
236  //
237  // Create a child process.
238  //
239  switch (child=fork())
240  {
241    //
242    // An error occured:  Return to the caller.
243    //
244    case -1:
245      return 1;
246      break;
247
248    //
249    // Child process: Execute the program.
250    //
251    case 0:
252      execve (argv[0], argv, envp);
253
254      //
255      // If the execve() failed, we should exit and let the parent pick up
256      // our non-zero exit status.
257      //
258      exit (1);
259      break;
260
261    //
262    // Parent process: Break out of the switch to do our processing.
263    //
264    default:
265      break;
266  }
267
268  //
269  // Parent process: Wait for the child process to termiante.
270  //
271  if ((wait (&status)) == -1)
272  {
273    return 1;
274  }
275
276  //
277  // If the program exited normally with a zero exit status, return success!
278  //
279  if (WIFEXITED (status) && (WEXITSTATUS(status) == 0))
280  {
281    return 0;
282  }
283
284  //
285  // Otherwise, return failure.
286  //
287  return 1;
288}
289