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