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