SystemUtils.cpp revision 2eb9a257c86d6194ec572d8556f86af97452bebe
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 "SystemUtils.h" 9#include <algorithm> 10#include <fstream> 11#include <iostream> 12#include <cstdlib> 13#include <sys/types.h> 14#include <sys/stat.h> 15#include <fcntl.h> 16#include <sys/wait.h> 17#include <unistd.h> 18#include <errno.h> 19 20/// removeFile - Delete the specified file 21/// 22void removeFile(const std::string &Filename) { 23 unlink(Filename.c_str()); 24} 25 26/// getUniqueFilename - Return a filename with the specified prefix. If the 27/// file does not exist yet, return it, otherwise add a suffix to make it 28/// unique. 29/// 30std::string getUniqueFilename(const std::string &FilenameBase) { 31 if (!std::ifstream(FilenameBase.c_str())) 32 return FilenameBase; // Couldn't open the file? Use it! 33 34 // Create a pattern for mkstemp... 35 char *FNBuffer = new char[FilenameBase.size()+8]; 36 strcpy(FNBuffer, FilenameBase.c_str()); 37 strcpy(FNBuffer+FilenameBase.size(), "-XXXXXX"); 38 39 // Agree on a temporary file name to use.... 40 int TempFD; 41 if ((TempFD = mkstemp(FNBuffer)) == -1) { 42 std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current " 43 << " directory!\n"; 44 exit(1); 45 } 46 47 // We don't need to hold the temp file descriptor... we will trust that noone 48 // will overwrite/delete the file while we are working on it... 49 close(TempFD); 50 std::string Result(FNBuffer); 51 delete[] FNBuffer; 52 return Result; 53} 54 55/// isExecutableFile - This function returns true if the filename specified 56/// exists and is executable. 57/// 58bool isExecutableFile(const std::string &ExeFileName) { 59 struct stat Buf; 60 if (stat(ExeFileName.c_str(), &Buf)) 61 return false; // Must not be executable! 62 63 if (!(Buf.st_mode & S_IFREG)) 64 return false; // Not a regular file? 65 66 if (Buf.st_uid == getuid()) // Owner of file? 67 return Buf.st_mode & S_IXUSR; 68 else if (Buf.st_gid == getgid()) // In group of file? 69 return Buf.st_mode & S_IXGRP; 70 else // Unrelated to file? 71 return Buf.st_mode & S_IXOTH; 72} 73 74 75// FindExecutable - Find a named executable, giving the argv[0] of bugpoint. 76// This assumes the executable is in the same directory as bugpoint itself. 77// If the executable cannot be found, return an empty string. 78// 79std::string FindExecutable(const std::string &ExeName, 80 const std::string &BugPointPath) { 81 // First check the directory that bugpoint is in. We can do this if 82 // BugPointPath contains at least one / character, indicating that it is a 83 // relative path to bugpoint itself. 84 // 85 std::string Result = BugPointPath; 86 while (!Result.empty() && Result[Result.size()-1] != '/') 87 Result.erase(Result.size()-1, 1); 88 89 if (!Result.empty()) { 90 Result += ExeName; 91 if (isExecutableFile(Result)) return Result; // Found it? 92 } 93 94 // Okay, if the path to bugpoint didn't tell us anything, try using the PATH 95 // environment variable. 96 const char *PathStr = getenv("PATH"); 97 if (PathStr == 0) return ""; 98 99 // Now we have a colon seperated list of directories to search... try them... 100 unsigned PathLen = strlen(PathStr); 101 while (PathLen) { 102 // Find the first colon... 103 const char *Colon = std::find(PathStr, PathStr+PathLen, ':'); 104 105 // Check to see if this first directory contains the executable... 106 std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName; 107 if (isExecutableFile(FilePath)) 108 return FilePath; // Found the executable! 109 110 // Nope it wasn't in this directory, check the next range! 111 PathLen -= Colon-PathStr; 112 PathStr = Colon; 113 while (*PathStr == ':') { // Advance past colons 114 PathStr++; 115 PathLen--; 116 } 117 } 118 119 // If we fell out, we ran out of directories in PATH to search, return failure 120 return ""; 121} 122 123static void RedirectFD(const std::string &File, int FD) { 124 if (File.empty()) return; // Noop 125 126 // Open the file 127 int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666); 128 if (InFD == -1) { 129 std::cerr << "Error opening file '" << File << "' for " 130 << (FD == 0 ? "input" : "output") << "!\n"; 131 exit(1); 132 } 133 134 dup2(InFD, FD); // Install it as the requested FD 135 close(InFD); // Close the original FD 136} 137 138/// RunProgramWithTimeout - This function executes the specified program, with 139/// the specified null-terminated argument array, with the stdin/out/err fd's 140/// redirected, with a timeout specified on the commandline. This terminates 141/// the calling program if there is an error executing the specified program. 142/// It returns the return value of the program, or -1 if a timeout is detected. 143/// 144int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args, 145 const std::string &StdInFile, 146 const std::string &StdOutFile, 147 const std::string &StdErrFile) { 148 149 // FIXME: install sigalarm handler here for timeout... 150 151 int Child = fork(); 152 switch (Child) { 153 case -1: 154 std::cerr << "ERROR forking!\n"; 155 exit(1); 156 case 0: // Child 157 RedirectFD(StdInFile, 0); // Redirect file descriptors... 158 RedirectFD(StdOutFile, 1); 159 RedirectFD(StdErrFile, 2); 160 161 execv(ProgramPath.c_str(), (char *const *)Args); 162 std::cerr << "Error executing program '" << ProgramPath; 163 for (; *Args; ++Args) 164 std::cerr << " " << *Args; 165 exit(1); 166 167 default: break; 168 } 169 170 // Make sure all output has been written while waiting 171 std::cout << std::flush; 172 173 int Status; 174 if (wait(&Status) != Child) { 175 if (errno == EINTR) { 176 static bool FirstTimeout = true; 177 if (FirstTimeout) { 178 std::cout << 179 "*** Program execution timed out! This mechanism is designed to handle\n" 180 " programs stuck in infinite loops gracefully. The -timeout option\n" 181 " can be used to change the timeout threshold or disable it completely\n" 182 " (with -timeout=0). This message is only displayed once.\n"; 183 FirstTimeout = false; 184 } 185 return -1; // Timeout detected 186 } 187 188 std::cerr << "Error waiting for child process!\n"; 189 exit(1); 190 } 191 return Status; 192} 193