b23888e2b05a71a2b05af4cb7d45fbb4d2d30792
[oota-llvm.git] / lib / Support / SystemUtils.cpp
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 ///
23 bool 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 bugpoint.
41 // This assumes the executable is in the same directory as bugpoint itself.
42 // If the executable cannot be found, return an empty string.
43 //
44 std::string FindExecutable(const std::string &ExeName,
45                            const std::string &BugPointPath) {
46   // First check the directory that bugpoint is in.  We can do this if
47   // BugPointPath contains at least one / character, indicating that it is a
48   // relative path to bugpoint itself.
49   //
50   std::string Result = BugPointPath;
51   while (!Result.empty() && Result[Result.size()-1] != '/')
52     Result.erase(Result.size()-1, 1);
53
54   if (!Result.empty()) {
55     Result += ExeName;
56     if (isExecutableFile(Result)) return Result; // Found it?
57   }
58
59   // Okay, if the path to bugpoint didn't tell us anything, try using the PATH
60   // environment variable.
61   const char *PathStr = getenv("PATH");
62   if (PathStr == 0) return "";
63
64   // Now we have a colon separated list of directories to search... try them...
65   unsigned PathLen = strlen(PathStr);
66   while (PathLen) {
67     // Find the first colon...
68     const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
69     
70     // Check to see if this first directory contains the executable...
71     std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
72     if (isExecutableFile(FilePath))
73       return FilePath;                    // Found the executable!
74    
75     // Nope it wasn't in this directory, check the next range!
76     PathLen -= Colon-PathStr;
77     PathStr = Colon;
78     while (*PathStr == ':') {   // Advance past colons
79       PathStr++;
80       PathLen--;
81     }
82   }
83
84   // If we fell out, we ran out of directories in PATH to search, return failure
85   return "";
86 }
87
88 static void RedirectFD(const std::string &File, int FD) {
89   if (File.empty()) return;  // Noop
90
91   // Open the file
92   int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
93   if (InFD == -1) {
94     std::cerr << "Error opening file '" << File << "' for "
95               << (FD == 0 ? "input" : "output") << "!\n";
96     exit(1);
97   }
98
99   dup2(InFD, FD);   // Install it as the requested FD
100   close(InFD);      // Close the original FD
101 }
102
103 /// RunProgramWithTimeout - This function executes the specified program, with
104 /// the specified null-terminated argument array, with the stdin/out/err fd's
105 /// redirected, with a timeout specified on the commandline.  This terminates
106 /// the calling program if there is an error executing the specified program.
107 /// It returns the return value of the program, or -1 if a timeout is detected.
108 ///
109 int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args,
110                           const std::string &StdInFile,
111                           const std::string &StdOutFile,
112                           const std::string &StdErrFile) {
113
114   // FIXME: install sigalarm handler here for timeout...
115
116   int Child = fork();
117   switch (Child) {
118   case -1:
119     std::cerr << "ERROR forking!\n";
120     exit(1);
121   case 0:               // Child
122     RedirectFD(StdInFile, 0);      // Redirect file descriptors...
123     RedirectFD(StdOutFile, 1);
124     RedirectFD(StdErrFile, 2);
125
126     execv(ProgramPath.c_str(), (char *const *)Args);
127     std::cerr << "Error executing program '" << ProgramPath;
128     for (; *Args; ++Args)
129       std::cerr << " " << *Args;
130     exit(1);
131
132   default: break;
133   }
134
135   // Make sure all output has been written while waiting
136   std::cout << std::flush;
137
138   int Status;
139   if (wait(&Status) != Child) {
140     if (errno == EINTR) {
141       static bool FirstTimeout = true;
142       if (FirstTimeout) {
143         std::cout <<
144  "*** Program execution timed out!  This mechanism is designed to handle\n"
145  "    programs stuck in infinite loops gracefully.  The -timeout option\n"
146  "    can be used to change the timeout threshold or disable it completely\n"
147  "    (with -timeout=0).  This message is only displayed once.\n";
148         FirstTimeout = false;
149       }
150       return -1;   // Timeout detected
151     }
152
153     std::cerr << "Error waiting for child process!\n";
154     exit(1);
155   }
156   return Status;
157 }