1 //===- SystemUtils.cpp - Utilities for low-level system tasks -------------===// 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 "llvm/Support/SystemUtils.h" 16 #include "llvm/System/Program.h" 17 #include <unistd.h> 18 #include <wait.h> 19 #include <sys/fcntl.h> 20 #include <algorithm> 21 #include <cerrno> 22 #include <cstdlib> 23 #include <fstream> 24 #include <iostream> 25 #include <signal.h> 26 using namespace llvm; 27 28 /// isStandardOutAConsole - Return true if we can tell that the standard output 29 /// stream goes to a terminal window or console. 30 bool llvm::isStandardOutAConsole() { 31 #if HAVE_ISATTY 32 return isatty(1); 33 #endif 34 // If we don't have isatty, just return false. 35 return false; 36 } 37 38 39 /// FindExecutable - Find a named executable, giving the argv[0] of program 40 /// being executed. This allows us to find another LLVM tool if it is built 41 /// into the same directory, but that directory is neither the current 42 /// directory, nor in the PATH. If the executable cannot be found, return an 43 /// empty string. 44 /// 45 #undef FindExecutable // needed on windows :( 46 sys::Path llvm::FindExecutable(const std::string &ExeName, 47 const std::string &ProgramPath) { 48 // First check the directory that the calling program is in. We can do this 49 // if ProgramPath contains at least one / character, indicating that it is a 50 // relative path to bugpoint itself. 51 // 52 sys::Path Result ( ProgramPath ); 53 Result.elideFile(); 54 55 if (!Result.isEmpty()) { 56 Result.appendFile(ExeName); 57 if (Result.executable()) return Result; 58 } 59 60 return sys::Program::FindProgramByName(ExeName); 61 } 62 63 static void RedirectFD(const std::string &File, int FD) { 64 if (File.empty()) return; // Noop 65 66 // Open the file 67 int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666); 68 if (InFD == -1) { 69 std::cerr << "Error opening file '" << File << "' for " 70 << (FD == 0 ? "input" : "output") << "!\n"; 71 exit(1); 72 } 73 74 dup2(InFD, FD); // Install it as the requested FD 75 close(InFD); // Close the original FD 76 } 77 78 static bool Timeout = false; 79 static void TimeOutHandler(int Sig) { 80 Timeout = true; 81 } 82 83 /// RunProgramWithTimeout - This function executes the specified program, with 84 /// the specified null-terminated argument array, with the stdin/out/err fd's 85 /// redirected, with a timeout specified by the last argument. This terminates 86 /// the calling program if there is an error executing the specified program. 87 /// It returns the return value of the program, or -1 if a timeout is detected. 88 /// 89 int llvm::RunProgramWithTimeout(const std::string &ProgramPath, 90 const char **Args, 91 const std::string &StdInFile, 92 const std::string &StdOutFile, 93 const std::string &StdErrFile, 94 unsigned NumSeconds) { 95 #ifdef HAVE_SYS_WAIT_H 96 int Child = fork(); 97 switch (Child) { 98 case -1: 99 std::cerr << "ERROR forking!\n"; 100 exit(1); 101 case 0: // Child 102 RedirectFD(StdInFile, 0); // Redirect file descriptors... 103 RedirectFD(StdOutFile, 1); 104 if (StdOutFile != StdErrFile) 105 RedirectFD(StdErrFile, 2); 106 else 107 dup2(1, 2); 108 109 execv(ProgramPath.c_str(), (char *const *)Args); 110 std::cerr << "Error executing program: '" << ProgramPath; 111 for (; *Args; ++Args) 112 std::cerr << " " << *Args; 113 std::cerr << "'\n"; 114 exit(1); 115 116 default: break; 117 } 118 119 // Make sure all output has been written while waiting 120 std::cout << std::flush; 121 122 // Install a timeout handler. 123 Timeout = false; 124 struct sigaction Act, Old; 125 Act.sa_sigaction = 0; 126 Act.sa_handler = TimeOutHandler; 127 sigemptyset(&Act.sa_mask); 128 Act.sa_flags = 0; 129 sigaction(SIGALRM, &Act, &Old); 130 131 // Set the timeout if one is set. 132 if (NumSeconds) 133 alarm(NumSeconds); 134 135 int Status; 136 while (wait(&Status) != Child) 137 if (errno == EINTR) { 138 if (Timeout) { 139 // Kill the child. 140 kill(Child, SIGKILL); 141 142 if (wait(&Status) != Child) 143 std::cerr << "Something funny happened waiting for the child!\n"; 144 145 alarm(0); 146 sigaction(SIGALRM, &Old, 0); 147 return -1; // Timeout detected 148 } else { 149 std::cerr << "Error waiting for child process!\n"; 150 exit(1); 151 } 152 } 153 154 alarm(0); 155 sigaction(SIGALRM, &Old, 0); 156 return Status; 157 158 #else 159 std::cerr << "RunProgramWithTimeout not implemented on this platform!\n"; 160 return -1; 161 #endif 162 } 163 164 165 // ExecWait - executes a program with the specified arguments and environment. 166 // It then waits for the progarm to termiante and then returns to the caller. 167 // 168 // Inputs: 169 // argv - The arguments to the program as an array of C strings. The first 170 // argument should be the name of the program to execute, and the 171 // last argument should be a pointer to NULL. 172 // 173 // envp - The environment passes to the program as an array of C strings in 174 // the form of "name=value" pairs. The last element should be a 175 // pointer to NULL. 176 // 177 // Outputs: 178 // None. 179 // 180 // Return value: 181 // 0 - No errors. 182 // 1 - The program could not be executed. 183 // 1 - The program returned a non-zero exit status. 184 // 1 - The program terminated abnormally. 185 // 186 // Notes: 187 // The program will inherit the stdin, stdout, and stderr file descriptors 188 // as well as other various configuration settings (umask). 189 // 190 // This function should not print anything to stdout/stderr on its own. It is 191 // a generic library function. The caller or executed program should report 192 // errors in the way it sees fit. 193 // 194 // This function does not use $PATH to find programs. 195 // 196 int llvm::ExecWait(const char * const old_argv[], 197 const char * const old_envp[]) { 198 #ifdef HAVE_SYS_WAIT_H 199 // Create local versions of the parameters that can be passed into execve() 200 // without creating const problems. 201 char ** const argv = (char ** const) old_argv; 202 char ** const envp = (char ** const) old_envp; 203 204 // Create a child process. 205 switch (fork()) { 206 // An error occured: Return to the caller. 207 case -1: 208 return 1; 209 break; 210 211 // Child process: Execute the program. 212 case 0: 213 execve (argv[0], argv, envp); 214 // If the execve() failed, we should exit and let the parent pick up 215 // our non-zero exit status. 216 exit (1); 217 218 // Parent process: Break out of the switch to do our processing. 219 default: 220 break; 221 } 222 223 // Parent process: Wait for the child process to terminate. 224 int status; 225 if ((wait (&status)) == -1) 226 return 1; 227 228 // If the program exited normally with a zero exit status, return success! 229 if (WIFEXITED (status) && (WEXITSTATUS(status) == 0)) 230 return 0; 231 #else 232 std::cerr << "llvm::ExecWait not implemented on this platform!\n"; 233 #endif 234 235 // Otherwise, return failure. 236 return 1; 237 } 238