llvm-6502/lib/Support/SystemUtils.cpp
John Criswell 5afb5f6377 Added the ExecWait() function. It executes a program with the specified
arguments and environment.
Perhaps it should be merged with the RunProgramWithTimeout function, but I'd
want to allow it to inherit the parent process's stdin and stdout.
I'll save that for a rainy day...


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@8577 91177308-0d34-0410-b5e6-96231b3b80d8
2003-09-17 15:13:59 +00:00

257 lines
7.4 KiB
C++

//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
//
// This file contains functions used to do a variety of low-level, often
// system-specific, tasks.
//
//===----------------------------------------------------------------------===//
#include "Support/SystemUtils.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include "Config/sys/types.h"
#include "Config/sys/stat.h"
#include "Config/fcntl.h"
#include "Config/sys/wait.h"
#include "Config/unistd.h"
#include "Config/errno.h"
/// isExecutableFile - This function returns true if the filename specified
/// exists and is executable.
///
bool isExecutableFile(const std::string &ExeFileName) {
struct stat Buf;
if (stat(ExeFileName.c_str(), &Buf))
return false; // Must not be executable!
if (!(Buf.st_mode & S_IFREG))
return false; // Not a regular file?
if (Buf.st_uid == getuid()) // Owner of file?
return Buf.st_mode & S_IXUSR;
else if (Buf.st_gid == getgid()) // In group of file?
return Buf.st_mode & S_IXGRP;
else // Unrelated to file?
return Buf.st_mode & S_IXOTH;
}
/// FindExecutable - Find a named executable, giving the argv[0] of program
/// being executed. This allows us to find another LLVM tool if it is built into
/// the same directory, but that directory is neither the current directory, nor
/// in the PATH. If the executable cannot be found, return an empty string.
///
std::string FindExecutable(const std::string &ExeName,
const std::string &ProgramPath) {
// First check the directory that bugpoint is in. We can do this if
// BugPointPath contains at least one / character, indicating that it is a
// relative path to bugpoint itself.
//
std::string Result = ProgramPath;
while (!Result.empty() && Result[Result.size()-1] != '/')
Result.erase(Result.size()-1, 1);
if (!Result.empty()) {
Result += ExeName;
if (isExecutableFile(Result)) return Result; // Found it?
}
// Okay, if the path to the program didn't tell us anything, try using the
// PATH environment variable.
const char *PathStr = getenv("PATH");
if (PathStr == 0) return "";
// 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...
std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
if (isExecutableFile(FilePath))
return FilePath; // Found the executable!
// Nope it wasn't in this directory, check the next range!
PathLen -= Colon-PathStr;
PathStr = Colon;
while (*PathStr == ':') { // Advance past colons
PathStr++;
PathLen--;
}
}
// If we fell out, we ran out of directories in PATH to search, return failure
return "";
}
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) {
std::cerr << "Error opening file '" << File << "' for "
<< (FD == 0 ? "input" : "output") << "!\n";
exit(1);
}
dup2(InFD, FD); // Install it as the requested FD
close(InFD); // Close the original FD
}
/// RunProgramWithTimeout - This function executes the specified program, with
/// the specified null-terminated argument array, with the stdin/out/err fd's
/// redirected, with a timeout specified on the command line. This terminates
/// the calling program if there is an error executing the specified program.
/// It returns the return value of the program, or -1 if a timeout is detected.
///
int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args,
const std::string &StdInFile,
const std::string &StdOutFile,
const std::string &StdErrFile) {
// FIXME: install sigalarm handler here for timeout...
int Child = fork();
switch (Child) {
case -1:
std::cerr << "ERROR forking!\n";
exit(1);
case 0: // Child
RedirectFD(StdInFile, 0); // Redirect file descriptors...
RedirectFD(StdOutFile, 1);
RedirectFD(StdErrFile, 2);
execv(ProgramPath.c_str(), (char *const *)Args);
std::cerr << "Error executing program '" << ProgramPath;
for (; *Args; ++Args)
std::cerr << " " << *Args;
exit(1);
default: break;
}
// Make sure all output has been written while waiting
std::cout << std::flush;
int Status;
if (wait(&Status) != Child) {
if (errno == EINTR) {
static bool FirstTimeout = true;
if (FirstTimeout) {
std::cout <<
"*** Program execution timed out! This mechanism is designed to handle\n"
" programs stuck in infinite loops gracefully. The -timeout option\n"
" can be used to change the timeout threshold or disable it completely\n"
" (with -timeout=0). This message is only displayed once.\n";
FirstTimeout = false;
}
return -1; // Timeout detected
}
std::cerr << "Error waiting for child process!\n";
exit(1);
}
return Status;
}
//
// Function: ExecWait ()
//
// Description:
// This function executes a program with the specified arguments and
// environment. It then waits for the progarm to termiante and then returns
// to the caller.
//
// Inputs:
// argv - The arguments to the program as an array of C strings. The first
// argument should be the name of the program to execute, and the
// last argument should be a pointer to NULL.
//
// envp - The environment passes to the program as an array of C strings in
// the form of "name=value" pairs. The last element should be a
// pointer to NULL.
//
// Outputs:
// None.
//
// Return value:
// 0 - No errors.
// 1 - The program could not be executed.
// 1 - The program returned a non-zero exit status.
// 1 - The program terminated abnormally.
//
// Notes:
// The program will inherit the stdin, stdout, and stderr file descriptors
// as well as other various configuration settings (umask).
//
// This function should not print anything to stdout/stderr on its own. It is
// a generic library function. The caller or executed program should report
// errors in the way it sees fit.
//
int
ExecWait (char ** argv, char **envp)
{
// Child process ID
register int child;
// Status from child process when it exits
int status;
//
// Because UNIX is sometimes less than convenient, we need to use
// FindExecutable() to find the full pathname to the file to execute.
//
// This is becausse execvp() doesn't use PATH, and we want to look for
// programs in the LLVM binary directory first anyway.
//
std::string Program = FindExecutable (argv[0], "");
if (Program.empty())
{
return 1;
}
//
// Create a child process.
//
switch (child=fork())
{
case -1:
return 1;
break;
case 0:
execve (Program.c_str(), argv, envp);
return 1;
break;
default:
break;
}
//
// Parent process: Wait for the child process to termiante.
//
if ((wait (&status)) == -1)
{
return 1;
}
//
// If the program exited normally with a zero exit status, return success!
//
if (WIFEXITED (status) && (WEXITSTATUS(status) == 0))
{
return 0;
}
//
// Otherwise, return failure.
//
return 1;
}