| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 1 | //===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===// | 
| John Criswell | b576c94 | 2003-10-20 19:43:21 +0000 | [diff] [blame] | 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 | //===----------------------------------------------------------------------===// | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 9 | // | 
 | 10 | // This file contains functions used to do a variety of low-level, often | 
 | 11 | // system-specific, tasks. | 
 | 12 | // | 
 | 13 | //===----------------------------------------------------------------------===// | 
 | 14 |  | 
| Misha Brukman | 3d1b0c7 | 2003-08-07 21:28:50 +0000 | [diff] [blame] | 15 | #include "Support/SystemUtils.h" | 
| John Criswell | 7a73b80 | 2003-06-30 21:59:07 +0000 | [diff] [blame] | 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" | 
| Brian Gaeke | 8507ecb | 2004-04-02 21:26:04 +0000 | [diff] [blame] | 21 | #include "Config/config.h" | 
| Chris Lattner | 74b1f45 | 2004-01-10 19:15:14 +0000 | [diff] [blame] | 22 | #include <algorithm> | 
 | 23 | #include <fstream> | 
 | 24 | #include <iostream> | 
 | 25 | #include <cstdlib> | 
 | 26 | #include <cerrno> | 
| Chris Lattner | 2cdd21c | 2003-12-14 21:35:53 +0000 | [diff] [blame] | 27 | using namespace llvm; | 
| Brian Gaeke | d0fde30 | 2003-11-11 22:41:34 +0000 | [diff] [blame] | 28 |  | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 29 | /// isExecutableFile - This function returns true if the filename specified | 
 | 30 | /// exists and is executable. | 
 | 31 | /// | 
| Chris Lattner | 2cdd21c | 2003-12-14 21:35:53 +0000 | [diff] [blame] | 32 | bool llvm::isExecutableFile(const std::string &ExeFileName) { | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 |  | 
| Chris Lattner | b234d46 | 2004-04-02 05:04:03 +0000 | [diff] [blame] | 48 | /// isStandardOutAConsole - Return true if we can tell that the standard output | 
 | 49 | /// stream goes to a terminal window or console. | 
 | 50 | bool llvm::isStandardOutAConsole() { | 
| Brian Gaeke | 8507ecb | 2004-04-02 21:26:04 +0000 | [diff] [blame] | 51 | #if HAVE_ISATTY | 
| Chris Lattner | b234d46 | 2004-04-02 05:04:03 +0000 | [diff] [blame] | 52 |   return isatty(1); | 
| Brian Gaeke | 8507ecb | 2004-04-02 21:26:04 +0000 | [diff] [blame] | 53 | #endif | 
 | 54 |   // If we don't have isatty, just return false. | 
 | 55 |   return false; | 
| Chris Lattner | b234d46 | 2004-04-02 05:04:03 +0000 | [diff] [blame] | 56 | } | 
 | 57 |  | 
 | 58 |  | 
| Misha Brukman | f7066c7 | 2003-08-07 21:34:25 +0000 | [diff] [blame] | 59 | /// FindExecutable - Find a named executable, giving the argv[0] of program | 
| Misha Brukman | 44f8a34 | 2003-09-29 22:40:07 +0000 | [diff] [blame] | 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. | 
| Misha Brukman | f7066c7 | 2003-08-07 21:34:25 +0000 | [diff] [blame] | 64 | ///  | 
| Chris Lattner | 2cdd21c | 2003-12-14 21:35:53 +0000 | [diff] [blame] | 65 | std::string llvm::FindExecutable(const std::string &ExeName, | 
 | 66 |                                  const std::string &ProgramPath) { | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 |   // | 
| Misha Brukman | 35d402f | 2003-08-07 21:33:33 +0000 | [diff] [blame] | 71 |   std::string Result = ProgramPath; | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 |  | 
| Misha Brukman | 35d402f | 2003-08-07 21:33:33 +0000 | [diff] [blame] | 80 |   // Okay, if the path to the program didn't tell us anything, try using the | 
 | 81 |   // PATH environment variable. | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 82 |   const char *PathStr = getenv("PATH"); | 
 | 83 |   if (PathStr == 0) return ""; | 
 | 84 |  | 
| Misha Brukman | bc0e998 | 2003-07-14 17:20:40 +0000 | [diff] [blame] | 85 |   // Now we have a colon separated list of directories to search... try them... | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 |  | 
 | 109 | static 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 " | 
| Misha Brukman | 44f8a34 | 2003-09-29 22:40:07 +0000 | [diff] [blame] | 116 |               << (FD == 0 ? "input" : "output") << "!\n"; | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 | 
| Misha Brukman | 950971d | 2003-09-16 15:31:46 +0000 | [diff] [blame] | 126 | /// redirected, with a timeout specified on the command line.  This terminates | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 | /// | 
| Chris Lattner | 2cdd21c | 2003-12-14 21:35:53 +0000 | [diff] [blame] | 130 | int 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) { | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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); | 
| Chris Lattner | bf3d2e2 | 2004-04-16 05:35:58 +0000 | [diff] [blame] | 145 |     if (StdOutFile != StdErrFile) | 
 | 146 |       RedirectFD(StdErrFile, 2); | 
 | 147 |     else | 
 | 148 |       dup2(1, 2); | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 149 |  | 
 | 150 |     execv(ProgramPath.c_str(), (char *const *)Args); | 
| Brian Gaeke | 53e557d | 2003-10-15 20:46:58 +0000 | [diff] [blame] | 151 |     std::cerr << "Error executing program: '" << ProgramPath; | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 152 |     for (; *Args; ++Args) | 
 | 153 |       std::cerr << " " << *Args; | 
| Brian Gaeke | 53e557d | 2003-10-15 20:46:58 +0000 | [diff] [blame] | 154 |     std::cerr << "'\n"; | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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) { | 
| Misha Brukman | 44f8a34 | 2003-09-29 22:40:07 +0000 | [diff] [blame] | 168 |         std::cout << | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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"; | 
| Misha Brukman | 44f8a34 | 2003-09-29 22:40:07 +0000 | [diff] [blame] | 173 |         FirstTimeout = false; | 
| Chris Lattner | 4a10645 | 2002-12-23 23:50:16 +0000 | [diff] [blame] | 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 | } | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 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 | // | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 219 | //  This function does not use $PATH to find programs. | 
 | 220 | // | 
| Chris Lattner | 2cdd21c | 2003-12-14 21:35:53 +0000 | [diff] [blame] | 221 | int llvm::ExecWait(const char * const old_argv[], | 
 | 222 |                    const char * const old_envp[]) { | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 223 |   // Child process ID | 
 | 224 |   register int child; | 
 | 225 |  | 
 | 226 |   // Status from child process when it exits | 
 | 227 |   int status; | 
 | 228 |   | 
 | 229 |   // | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 230 |   // Create local versions of the parameters that can be passed into execve() | 
 | 231 |   // without creating const problems. | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 232 |   // | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 233 |   char ** const argv = (char ** const) old_argv; | 
 | 234 |   char ** const envp = (char ** const) old_envp; | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 235 |  | 
 | 236 |   // | 
 | 237 |   // Create a child process. | 
 | 238 |   // | 
 | 239 |   switch (child=fork()) | 
 | 240 |   { | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 241 |     // | 
 | 242 |     // An error occured:  Return to the caller. | 
 | 243 |     // | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 244 |     case -1: | 
 | 245 |       return 1; | 
 | 246 |       break; | 
 | 247 |  | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 248 |     // | 
 | 249 |     // Child process: Execute the program. | 
 | 250 |     // | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 251 |     case 0: | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 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); | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 259 |       break; | 
 | 260 |  | 
| John Criswell | e5b3e15 | 2003-09-17 19:02:49 +0000 | [diff] [blame] | 261 |     // | 
 | 262 |     // Parent process: Break out of the switch to do our processing. | 
 | 263 |     // | 
| John Criswell | 5afb5f6 | 2003-09-17 15:13:59 +0000 | [diff] [blame] | 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 | } |