diff options
Diffstat (limited to 'llvm/lib/System/Unix/Program.inc')
-rw-r--r-- | llvm/lib/System/Unix/Program.inc | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/llvm/lib/System/Unix/Program.inc b/llvm/lib/System/Unix/Program.inc new file mode 100644 index 00000000000..1bd3eae0f97 --- /dev/null +++ b/llvm/lib/System/Unix/Program.inc @@ -0,0 +1,225 @@ +//===- llvm/System/Unix/Program.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencer and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific portion of the Program class. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#include <llvm/Config/config.h> +#include "Unix.h" +#include <iostream> +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#if HAVE_SIGNAL_H +#include <signal.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif + +extern char** environ; + +namespace llvm { +using namespace sys; + +// This function just uses the PATH environment variable to find the program. +Path +Program::FindProgramByName(const std::string& progName) { + + // Check some degenerate cases + if (progName.length() == 0) // no program + return Path(); + Path temp; + if (!temp.setFile(progName)) // invalid name + return Path(); + if (temp.executable()) // already executable as is + return temp; + + // At this point, the file name is valid and its not executable + + // Get the path. If its empty, we can't do anything to find it. + const char *PathStr = getenv("PATH"); + if (PathStr == 0) + return Path(); + + // Now we have a colon separated list of directories to search; try them. + unsigned PathLen = strlen(PathStr); + while (PathLen) { + // Find the first colon... + const char *Colon = std::find(PathStr, PathStr+PathLen, ':'); + + // Check to see if this first directory contains the executable... + Path FilePath; + if (FilePath.setDirectory(std::string(PathStr,Colon))) { + FilePath.appendFile(progName); + if (FilePath.executable()) + return FilePath; // Found the executable! + } + + // Nope it wasn't in this directory, check the next path in the list! + PathLen -= Colon-PathStr; + PathStr = Colon; + + // Advance past duplicate colons + while (*PathStr == ':') { + PathStr++; + PathLen--; + } + } + return Path(); +} + +static void RedirectFD(const std::string &File, int FD) { + if (File.empty()) return; // Noop + + // Open the file + int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666); + if (InFD == -1) { + ThrowErrno("Cannot open file '" + File + "' for " + + (FD == 0 ? "input" : "output") + "!\n"); + } + + dup2(InFD, FD); // Install it as the requested FD + close(InFD); // Close the original FD +} + +static bool Timeout = false; +static void TimeOutHandler(int Sig) { + Timeout = true; +} + +int +Program::ExecuteAndWait(const Path& path, + const char** args, + const char** envp, + const Path** redirects, + unsigned secondsToWait +) { + if (!path.executable()) + throw path.toString() + " is not executable"; + +#ifdef HAVE_SYS_WAIT_H + // Create a child process. + int child = fork(); + switch (child) { + // An error occured: Return to the caller. + case -1: + ThrowErrno(std::string("Couldn't execute program '") + path.toString() + + "'"); + break; + + // Child process: Execute the program. + case 0: { + // Redirect file descriptors... + if (redirects) { + if (redirects[0]) + if (redirects[0]->isEmpty()) + RedirectFD("/dev/null",0); + else + RedirectFD(redirects[0]->toString(), 0); + if (redirects[1]) + if (redirects[1]->isEmpty()) + RedirectFD("/dev/null",1); + else + RedirectFD(redirects[1]->toString(), 1); + if (redirects[1] && redirects[2] && + *(redirects[1]) != *(redirects[2])) { + if (redirects[2]->isEmpty()) + RedirectFD("/dev/null",2); + else + RedirectFD(redirects[2]->toString(), 2); + } else { + dup2(1, 2); + } + } + + // Set up the environment + char** env = environ; + if (envp != 0) + env = (char**) envp; + + // Execute! + execve (path.c_str(), (char** const)args, env); + // If the execve() failed, we should exit and let the parent pick up + // our non-zero exit status. + exit (errno); + } + + // Parent process: Break out of the switch to do our processing. + default: + break; + } + + // Make sure stderr and stdout have been flushed + std::cerr << std::flush; + std::cout << std::flush; + fsync(1); + fsync(2); + + struct sigaction Act, Old; + + // Install a timeout handler. + if (secondsToWait) { + Timeout = false; + Act.sa_sigaction = 0; + Act.sa_handler = TimeOutHandler; + sigemptyset(&Act.sa_mask); + Act.sa_flags = 0; + sigaction(SIGALRM, &Act, &Old); + alarm(secondsToWait); + } + + // Parent process: Wait for the child process to terminate. + int status; + while (wait(&status) != child) + if (secondsToWait && errno == EINTR) { + // Kill the child. + kill(child, SIGKILL); + + // Turn off the alarm and restore the signal handler + alarm(0); + sigaction(SIGALRM, &Old, 0); + + // Wait for child to die + if (wait(&status) != child) + ThrowErrno("Child timedout but wouldn't die"); + + return -1; // Timeout detected + } else { + ThrowErrno("Error waiting for child process"); + } + + // We exited normally without timeout, so turn off the timer. + if (secondsToWait) { + alarm(0); + sigaction(SIGALRM, &Old, 0); + } + + // If the program exited normally with a zero exit status, return success! + if (WIFEXITED (status)) + return WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + throw std::string("Program '") + path.toString() + + "' received terminating signal."; + +#else + throw std::string( + "Program::ExecuteAndWait not implemented on this platform!\n"); +#endif + return 0; +} + +} +// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab |