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