mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-19 11:02:59 +02:00
Remove duplication in Program::Execute{And,No}Wait.
Implemented by moving the code out of static functions into methods of Program class. llvm-svn: 76340
This commit is contained in:
parent
fc0fe7ea20
commit
583e23d726
@ -19,6 +19,9 @@
|
|||||||
namespace llvm {
|
namespace llvm {
|
||||||
namespace sys {
|
namespace sys {
|
||||||
|
|
||||||
|
// TODO: Add operations to communicate with the process, redirect its I/O,
|
||||||
|
// etc.
|
||||||
|
|
||||||
/// This class provides an abstraction for programs that are executable by the
|
/// This class provides an abstraction for programs that are executable by the
|
||||||
/// operating system. It provides a platform generic way to find executable
|
/// operating system. It provides a platform generic way to find executable
|
||||||
/// programs from the path and to execute them in various ways. The sys::Path
|
/// programs from the path and to execute them in various ways. The sys::Path
|
||||||
@ -26,104 +29,112 @@ namespace sys {
|
|||||||
/// @since 1.4
|
/// @since 1.4
|
||||||
/// @brief An abstraction for finding and executing programs.
|
/// @brief An abstraction for finding and executing programs.
|
||||||
class Program {
|
class Program {
|
||||||
|
unsigned Pid_;
|
||||||
|
|
||||||
|
// Noncopyable.
|
||||||
|
Program(const Program& other);
|
||||||
|
Program& operator=(const Program& other);
|
||||||
|
|
||||||
/// @name Methods
|
/// @name Methods
|
||||||
/// @{
|
/// @{
|
||||||
public:
|
public:
|
||||||
/// This static constructor (factory) will attempt to locate a program in
|
|
||||||
/// the operating system's file system using some pre-determined set of
|
|
||||||
/// locations to search (e.g. the PATH on Unix).
|
|
||||||
/// @returns A Path object initialized to the path of the program or a
|
|
||||||
/// Path object that is empty (invalid) if the program could not be found.
|
|
||||||
/// @throws nothing
|
|
||||||
/// @brief Construct a Program by finding it by name.
|
|
||||||
static Path FindProgramByName(const std::string& name);
|
|
||||||
|
|
||||||
/// This function executes the program using the \p arguments provided and
|
Program() : Pid_(0)
|
||||||
/// waits for the program to exit. This function will block the current
|
{}
|
||||||
/// program until the invoked program exits. The invoked program will
|
|
||||||
/// inherit the stdin, stdout, and stderr file descriptors, the
|
/// This function executes the program using the \p arguments provided. The
|
||||||
/// environment and other configuration settings of the invoking program.
|
/// invoked program will inherit the stdin, stdout, and stderr file
|
||||||
/// If Path::executable() does not return true when this function is
|
/// descriptors, the environment and other configuration settings of the
|
||||||
/// called then a std::string is thrown.
|
/// invoking program. If Path::executable() does not return true when this
|
||||||
/// @returns an integer result code indicating the status of the program.
|
/// function is called then a std::string is thrown.
|
||||||
/// A zero or positive value indicates the result code of the program. A
|
/// @returns false in case of error, true otherwise.
|
||||||
/// negative value is the signal number on which it terminated.
|
/// @see FindProgramByName
|
||||||
/// @see FindProgrambyName
|
/// @brief Executes the program with the given set of \p args.
|
||||||
/// @brief Executes the program with the given set of \p args.
|
bool Execute
|
||||||
static int ExecuteAndWait(
|
( const Path& path, ///< sys::Path object providing the path of the
|
||||||
const Path& path, ///< sys::Path object providing the path of the
|
///< program to be executed. It is presumed this is the result of
|
||||||
///< program to be executed. It is presumed this is the result of
|
///< the FindProgramByName method.
|
||||||
///< the FindProgramByName method.
|
const char** args, ///< A vector of strings that are passed to the
|
||||||
const char** args, ///< A vector of strings that are passed to the
|
///< program. The first element should be the name of the program.
|
||||||
///< program. The first element should be the name of the program.
|
///< The list *must* be terminated by a null char* entry.
|
||||||
///< The list *must* be terminated by a null char* entry.
|
const char ** env = 0, ///< An optional vector of strings to use for
|
||||||
const char ** env = 0, ///< An optional vector of strings to use for
|
///< the program's environment. If not provided, the current program's
|
||||||
///< the program's environment. If not provided, the current program's
|
///< environment will be used.
|
||||||
///< environment will be used.
|
const sys::Path** redirects = 0, ///< An optional array of pointers to
|
||||||
const sys::Path** redirects = 0, ///< An optional array of pointers to
|
///< Paths. If the array is null, no redirection is done. The array
|
||||||
///< Paths. If the array is null, no redirection is done. The array
|
///< should have a size of at least three. If the pointer in the array
|
||||||
///< should have a size of at least three. If the pointer in the array
|
///< are not null, then the inferior process's stdin(0), stdout(1),
|
||||||
///< are not null, then the inferior process's stdin(0), stdout(1),
|
///< and stderr(2) will be redirected to the corresponding Paths.
|
||||||
///< and stderr(2) will be redirected to the corresponding Paths.
|
///< When an empty Path is passed in, the corresponding file
|
||||||
///< When an empty Path is passed in, the corresponding file
|
///< descriptor will be disconnected (ie, /dev/null'd) in a portable
|
||||||
///< descriptor will be disconnected (ie, /dev/null'd) in a portable
|
///< way.
|
||||||
///< way.
|
unsigned memoryLimit = 0, ///< If non-zero, this specifies max. amount
|
||||||
unsigned secondsToWait = 0, ///< If non-zero, this specifies the amount
|
///< of memory can be allocated by process. If memory usage will be
|
||||||
///< of time to wait for the child process to exit. If the time
|
///< higher limit, the child is killed and this call returns. If zero
|
||||||
///< expires, the child is killed and this call returns. If zero,
|
///< - no memory limit.
|
||||||
///< this function will wait until the child finishes or forever if
|
std::string* ErrMsg = 0 ///< If non-zero, provides a pointer to a string
|
||||||
///< it doesn't.
|
///< instance in which error messages will be returned. If the string
|
||||||
unsigned memoryLimit = 0, ///< If non-zero, this specifies max. amount
|
///< is non-empty upon return an error occurred while invoking the
|
||||||
///< of memory can be allocated by process. If memory usage will be
|
///< program.
|
||||||
///< higher limit, the child is killed and this call returns. If zero
|
|
||||||
///< - no memory limit.
|
|
||||||
std::string* ErrMsg = 0 ///< If non-zero, provides a pointer to a string
|
|
||||||
///< instance in which error messages will be returned. If the string
|
|
||||||
///< is non-empty upon return an error occurred while invoking the
|
|
||||||
///< program.
|
|
||||||
);
|
);
|
||||||
// These methods change the specified standard stream (stdin or stdout) to
|
|
||||||
// binary mode. They return true if an error occurred
|
/// This function waits for the program to exit. This function will block
|
||||||
static bool ChangeStdinToBinary();
|
/// the current program until the invoked program exits.
|
||||||
static bool ChangeStdoutToBinary();
|
/// @returns an integer result code indicating the status of the program.
|
||||||
|
/// A zero or positive value indicates the result code of the program. A
|
||||||
|
/// negative value is the signal number on which it terminated.
|
||||||
|
/// @see Execute
|
||||||
|
/// @brief Waits for the program to exit.
|
||||||
|
int Wait
|
||||||
|
( unsigned secondsToWait = 0, ///< If non-zero, this specifies the amount
|
||||||
|
///< of time to wait for the child process to exit. If the time
|
||||||
|
///< expires, the child is killed and this call returns. If zero,
|
||||||
|
///< this function will wait until the child finishes or forever if
|
||||||
|
///< it doesn't.
|
||||||
|
std::string* ErrMsg = 0 ///< If non-zero, provides a pointer to a string
|
||||||
|
///< instance in which error messages will be returned. If the string
|
||||||
|
///< is non-empty upon return an error occurred while invoking the
|
||||||
|
///< program.
|
||||||
|
);
|
||||||
|
|
||||||
|
/// This static constructor (factory) will attempt to locate a program in
|
||||||
|
/// the operating system's file system using some pre-determined set of
|
||||||
|
/// locations to search (e.g. the PATH on Unix).
|
||||||
|
/// @returns A Path object initialized to the path of the program or a
|
||||||
|
/// Path object that is empty (invalid) if the program could not be found.
|
||||||
|
/// @throws nothing
|
||||||
|
/// @brief Construct a Program by finding it by name.
|
||||||
|
static Path FindProgramByName(const std::string& name);
|
||||||
|
|
||||||
|
// These methods change the specified standard stream (stdin or stdout) to
|
||||||
|
// binary mode. They return true if an error occurred
|
||||||
|
static bool ChangeStdinToBinary();
|
||||||
|
static bool ChangeStdoutToBinary();
|
||||||
|
|
||||||
|
/// A convenience function equivalent to Program prg; prg.Execute(..);
|
||||||
|
/// prg.Wait(..);
|
||||||
|
/// @throws nothing
|
||||||
|
/// @see Execute, Wait
|
||||||
|
static int ExecuteAndWait(const Path& path,
|
||||||
|
const char** args,
|
||||||
|
const char ** env = 0,
|
||||||
|
const sys::Path** redirects = 0,
|
||||||
|
unsigned secondsToWait = 0,
|
||||||
|
unsigned memoryLimit = 0,
|
||||||
|
std::string* ErrMsg = 0);
|
||||||
|
|
||||||
|
/// A convenience function equivalent to Program prg; prg.Execute(..);
|
||||||
|
/// @throws nothing
|
||||||
|
/// @see Execute
|
||||||
|
static void ExecuteNoWait(const Path& path,
|
||||||
|
const char** args,
|
||||||
|
const char ** env = 0,
|
||||||
|
const sys::Path** redirects = 0,
|
||||||
|
unsigned memoryLimit = 0,
|
||||||
|
std::string* ErrMsg = 0);
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// This function executes the program using the \p arguments provided and
|
|
||||||
/// waits for the program to exit. This function will block the current
|
|
||||||
/// program until the invoked program exits. The invoked program will
|
|
||||||
/// inherit the stdin, stdout, and stderr file descriptors, the
|
|
||||||
/// environment and other configuration settings of the invoking program.
|
|
||||||
/// If Path::executable() does not return true when this function is
|
|
||||||
/// called then a std::string is thrown.
|
|
||||||
/// @returns an integer result code indicating the status of the program.
|
|
||||||
/// A zero or positive value indicates the result code of the program. A
|
|
||||||
/// negative value is the signal number on which it terminated.
|
|
||||||
/// @see FindProgrambyName
|
|
||||||
/// @brief Executes the program with the given set of \p args.
|
|
||||||
static void ExecuteNoWait(
|
|
||||||
const Path& path, ///< sys::Path object providing the path of the
|
|
||||||
///< program to be executed. It is presumed this is the result of
|
|
||||||
///< the FindProgramByName method.
|
|
||||||
const char** args, ///< A vector of strings that are passed to the
|
|
||||||
///< program. The first element should be the name of the program.
|
|
||||||
///< The list *must* be terminated by a null char* entry.
|
|
||||||
const char ** env = 0, ///< An optional vector of strings to use for
|
|
||||||
///< the program's environment. If not provided, the current program's
|
|
||||||
///< environment will be used.
|
|
||||||
const sys::Path** redirects = 0, ///< An optional array of pointers to
|
|
||||||
///< Paths. If the array is null, no redirection is done. The array
|
|
||||||
///< should have a size of at least three. If the pointer in the array
|
|
||||||
///< are not null, then the inferior process's stdin(0), stdout(1),
|
|
||||||
///< and stderr(2) will be redirected to the corresponding Paths.
|
|
||||||
unsigned memoryLimit = 0, ///< If non-zero, this specifies max. amount
|
|
||||||
///< of memory can be allocated by process. If memory usage will be
|
|
||||||
///< higher limit, the child is killed and this call returns. If zero -
|
|
||||||
///< no memory limit.
|
|
||||||
std::string* ErrMsg = 0 ///< If non-zero, provides a pointer to a string
|
|
||||||
///< instance in which error messages will be returned. If the string
|
|
||||||
///< is non-empty upon return an error occurred while invoking the
|
|
||||||
///< program.
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,33 @@ using namespace sys;
|
|||||||
//=== independent code.
|
//=== independent code.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
int
|
||||||
|
Program::ExecuteAndWait(const Path& path,
|
||||||
|
const char** args,
|
||||||
|
const char** envp,
|
||||||
|
const Path** redirects,
|
||||||
|
unsigned secondsToWait,
|
||||||
|
unsigned memoryLimit,
|
||||||
|
std::string* ErrMsg) {
|
||||||
|
Program prg;
|
||||||
|
if (prg.Execute(path, args, envp, redirects, memoryLimit, ErrMsg))
|
||||||
|
return prg.Wait(secondsToWait, ErrMsg);
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Program::ExecuteNoWait(const Path& path,
|
||||||
|
const char** args,
|
||||||
|
const char** envp,
|
||||||
|
const Path** redirects,
|
||||||
|
unsigned memoryLimit,
|
||||||
|
std::string* ErrMsg) {
|
||||||
|
Program prg;
|
||||||
|
prg.Execute(path, args, envp, redirects, memoryLimit, ErrMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include the platform-specific parts of this class.
|
// Include the platform-specific parts of this class.
|
||||||
|
@ -142,49 +142,47 @@ static void SetMemoryLimits (unsigned size)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
Program::ExecuteAndWait(const Path& path,
|
Program::Execute(const Path& path,
|
||||||
const char** args,
|
const char** args,
|
||||||
const char** envp,
|
const char** envp,
|
||||||
const Path** redirects,
|
const Path** redirects,
|
||||||
unsigned secondsToWait,
|
unsigned memoryLimit,
|
||||||
unsigned memoryLimit,
|
std::string* ErrMsg)
|
||||||
std::string* ErrMsg)
|
|
||||||
{
|
{
|
||||||
if (!path.canExecute()) {
|
if (!path.canExecute()) {
|
||||||
if (ErrMsg)
|
if (ErrMsg)
|
||||||
*ErrMsg = path.toString() + " is not executable";
|
*ErrMsg = path.toString() + " is not executable";
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SYS_WAIT_H
|
|
||||||
// Create a child process.
|
// Create a child process.
|
||||||
int child = fork();
|
int child = fork();
|
||||||
switch (child) {
|
switch (child) {
|
||||||
// An error occured: Return to the caller.
|
// An error occured: Return to the caller.
|
||||||
case -1:
|
case -1:
|
||||||
MakeErrMsg(ErrMsg, "Couldn't fork");
|
MakeErrMsg(ErrMsg, "Couldn't fork");
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
// Child process: Execute the program.
|
// Child process: Execute the program.
|
||||||
case 0: {
|
case 0: {
|
||||||
// Redirect file descriptors...
|
// Redirect file descriptors...
|
||||||
if (redirects) {
|
if (redirects) {
|
||||||
// Redirect stdin
|
// Redirect stdin
|
||||||
if (RedirectIO(redirects[0], 0, ErrMsg)) { return -1; }
|
if (RedirectIO(redirects[0], 0, ErrMsg)) { return false; }
|
||||||
// Redirect stdout
|
// Redirect stdout
|
||||||
if (RedirectIO(redirects[1], 1, ErrMsg)) { return -1; }
|
if (RedirectIO(redirects[1], 1, ErrMsg)) { return false; }
|
||||||
if (redirects[1] && redirects[2] &&
|
if (redirects[1] && redirects[2] &&
|
||||||
*(redirects[1]) == *(redirects[2])) {
|
*(redirects[1]) == *(redirects[2])) {
|
||||||
// If stdout and stderr should go to the same place, redirect stderr
|
// If stdout and stderr should go to the same place, redirect stderr
|
||||||
// to the FD already open for stdout.
|
// to the FD already open for stdout.
|
||||||
if (-1 == dup2(1,2)) {
|
if (-1 == dup2(1,2)) {
|
||||||
MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout");
|
MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Just redirect stderr
|
// Just redirect stderr
|
||||||
if (RedirectIO(redirects[2], 2, ErrMsg)) { return -1; }
|
if (RedirectIO(redirects[2], 2, ErrMsg)) { return false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +212,23 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
fsync(1);
|
fsync(1);
|
||||||
fsync(2);
|
fsync(2);
|
||||||
|
|
||||||
|
Pid_ = child;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Program::Wait(unsigned secondsToWait,
|
||||||
|
std::string* ErrMsg)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SYS_WAIT_H
|
||||||
struct sigaction Act, Old;
|
struct sigaction Act, Old;
|
||||||
|
|
||||||
|
if (Pid_ == 0) {
|
||||||
|
MakeErrMsg(ErrMsg, "Process not started!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Install a timeout handler.
|
// Install a timeout handler.
|
||||||
if (secondsToWait) {
|
if (secondsToWait) {
|
||||||
Timeout = false;
|
Timeout = false;
|
||||||
@ -229,6 +242,7 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
|
|
||||||
// Parent process: Wait for the child process to terminate.
|
// Parent process: Wait for the child process to terminate.
|
||||||
int status;
|
int status;
|
||||||
|
int child = this->Pid_;
|
||||||
while (wait(&status) != child)
|
while (wait(&status) != child)
|
||||||
if (secondsToWait && errno == EINTR) {
|
if (secondsToWait && errno == EINTR) {
|
||||||
// Kill the child.
|
// Kill the child.
|
||||||
@ -274,78 +288,6 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Program::ExecuteNoWait(const Path& path,
|
|
||||||
const char** args,
|
|
||||||
const char** envp,
|
|
||||||
const Path** redirects,
|
|
||||||
unsigned memoryLimit,
|
|
||||||
std::string* ErrMsg)
|
|
||||||
{
|
|
||||||
if (!path.canExecute()) {
|
|
||||||
if (ErrMsg)
|
|
||||||
*ErrMsg = path.toString() + " is not executable";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a child process.
|
|
||||||
int child = fork();
|
|
||||||
switch (child) {
|
|
||||||
// An error occured: Return to the caller.
|
|
||||||
case -1:
|
|
||||||
MakeErrMsg(ErrMsg, "Couldn't fork");
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Child process: Execute the program.
|
|
||||||
case 0: {
|
|
||||||
// Redirect file descriptors...
|
|
||||||
if (redirects) {
|
|
||||||
// Redirect stdin
|
|
||||||
if (RedirectIO(redirects[0], 0, ErrMsg)) { return; }
|
|
||||||
// Redirect stdout
|
|
||||||
if (RedirectIO(redirects[1], 1, ErrMsg)) { return; }
|
|
||||||
if (redirects[1] && redirects[2] &&
|
|
||||||
*(redirects[1]) == *(redirects[2])) {
|
|
||||||
// If stdout and stderr should go to the same place, redirect stderr
|
|
||||||
// to the FD already open for stdout.
|
|
||||||
if (-1 == dup2(1,2)) {
|
|
||||||
MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Just redirect stderr
|
|
||||||
if (RedirectIO(redirects[2], 2, ErrMsg)) { return; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set memory limits
|
|
||||||
if (memoryLimit!=0) {
|
|
||||||
SetMemoryLimits(memoryLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute!
|
|
||||||
if (envp != 0)
|
|
||||||
execve (path.c_str(), (char**)args, (char**)envp);
|
|
||||||
else
|
|
||||||
execv (path.c_str(), (char**)args);
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Program::ChangeStdinToBinary(){
|
bool Program::ChangeStdinToBinary(){
|
||||||
// Do nothing, as Unix doesn't differentiate between text and binary.
|
// Do nothing, as Unix doesn't differentiate between text and binary.
|
||||||
return false;
|
return false;
|
||||||
|
@ -109,18 +109,17 @@ static HANDLE RedirectIO(const Path *path, int fd, std::string* ErrMsg) {
|
|||||||
DWORD cbJobObjectInfoLength);
|
DWORD cbJobObjectInfoLength);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int
|
bool
|
||||||
Program::ExecuteAndWait(const Path& path,
|
Program::Execute(const Path& path,
|
||||||
const char** args,
|
const char** args,
|
||||||
const char** envp,
|
const char** envp,
|
||||||
const Path** redirects,
|
const Path** redirects,
|
||||||
unsigned secondsToWait,
|
unsigned memoryLimit,
|
||||||
unsigned memoryLimit,
|
std::string* ErrMsg) {
|
||||||
std::string* ErrMsg) {
|
|
||||||
if (!path.canExecute()) {
|
if (!path.canExecute()) {
|
||||||
if (ErrMsg)
|
if (ErrMsg)
|
||||||
*ErrMsg = "program not executable";
|
*ErrMsg = "program not executable";
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Windows wants a command line, not an array of args, to pass to the new
|
// Windows wants a command line, not an array of args, to pass to the new
|
||||||
@ -195,13 +194,13 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg);
|
si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg);
|
||||||
if (si.hStdInput == INVALID_HANDLE_VALUE) {
|
if (si.hStdInput == INVALID_HANDLE_VALUE) {
|
||||||
MakeErrMsg(ErrMsg, "can't redirect stdin");
|
MakeErrMsg(ErrMsg, "can't redirect stdin");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg);
|
si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg);
|
||||||
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
|
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
|
||||||
CloseHandle(si.hStdInput);
|
CloseHandle(si.hStdInput);
|
||||||
MakeErrMsg(ErrMsg, "can't redirect stdout");
|
MakeErrMsg(ErrMsg, "can't redirect stdout");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) {
|
if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) {
|
||||||
// If stdout and stderr should go to the same place, redirect stderr
|
// If stdout and stderr should go to the same place, redirect stderr
|
||||||
@ -216,7 +215,7 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
CloseHandle(si.hStdInput);
|
CloseHandle(si.hStdInput);
|
||||||
CloseHandle(si.hStdOutput);
|
CloseHandle(si.hStdOutput);
|
||||||
MakeErrMsg(ErrMsg, "can't redirect stderr");
|
MakeErrMsg(ErrMsg, "can't redirect stderr");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,8 +241,9 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
SetLastError(err);
|
SetLastError(err);
|
||||||
MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") +
|
MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") +
|
||||||
path.toString() + "'");
|
path.toString() + "'");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
Pid_ = pi.dwProcessId;
|
||||||
|
|
||||||
// Make sure these get closed no matter what.
|
// Make sure these get closed no matter what.
|
||||||
AutoHandle hProcess(pi.hProcess);
|
AutoHandle hProcess(pi.hProcess);
|
||||||
@ -270,204 +270,50 @@ Program::ExecuteAndWait(const Path& path,
|
|||||||
MakeErrMsg(ErrMsg, std::string("Unable to set memory limit"));
|
MakeErrMsg(ErrMsg, std::string("Unable to set memory limit"));
|
||||||
TerminateProcess(pi.hProcess, 1);
|
TerminateProcess(pi.hProcess, 1);
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for it to terminate.
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Program::Wait(unsigned secondsToWait,
|
||||||
|
std::string* ErrMsg) {
|
||||||
|
if (Pid_ == 0) {
|
||||||
|
MakeErrMsg(ErrMsg, "Process not started!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoHandle hProcess = OpenProcess(SYNCHRONIZE, FALSE, Pid_);
|
||||||
|
|
||||||
|
// Wait for the process to terminate.
|
||||||
DWORD millisecondsToWait = INFINITE;
|
DWORD millisecondsToWait = INFINITE;
|
||||||
if (secondsToWait > 0)
|
if (secondsToWait > 0)
|
||||||
millisecondsToWait = secondsToWait * 1000;
|
millisecondsToWait = secondsToWait * 1000;
|
||||||
|
|
||||||
if (WaitForSingleObject(pi.hProcess, millisecondsToWait) == WAIT_TIMEOUT) {
|
if (WaitForSingleObject(hProcess, millisecondsToWait) == WAIT_TIMEOUT) {
|
||||||
if (!TerminateProcess(pi.hProcess, 1)) {
|
if (!TerminateProcess(hProcess, 1)) {
|
||||||
MakeErrMsg(ErrMsg, std::string("Failed to terminate timed-out program '")
|
MakeErrMsg(ErrMsg, "Failed to terminate timed-out program.");
|
||||||
+ path.toString() + "'");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
WaitForSingleObject(hProcess, INFINITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get its exit status.
|
// Get its exit status.
|
||||||
DWORD status;
|
DWORD status;
|
||||||
rc = GetExitCodeProcess(pi.hProcess, &status);
|
BOOL rc = GetExitCodeProcess(hProcess, &status);
|
||||||
err = GetLastError();
|
DWORD err = GetLastError();
|
||||||
|
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
SetLastError(err);
|
SetLastError(err);
|
||||||
MakeErrMsg(ErrMsg, std::string("Failed getting status for program '") +
|
MakeErrMsg(ErrMsg, "Failed getting status for program.");
|
||||||
path.toString() + "'");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Program::ExecuteNoWait(const Path& path,
|
|
||||||
const char** args,
|
|
||||||
const char** envp,
|
|
||||||
const Path** redirects,
|
|
||||||
unsigned memoryLimit,
|
|
||||||
std::string* ErrMsg) {
|
|
||||||
if (!path.canExecute()) {
|
|
||||||
if (ErrMsg)
|
|
||||||
*ErrMsg = "program not executable";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows wants a command line, not an array of args, to pass to the new
|
|
||||||
// process. We have to concatenate them all, while quoting the args that
|
|
||||||
// have embedded spaces.
|
|
||||||
|
|
||||||
// First, determine the length of the command line.
|
|
||||||
unsigned len = 0;
|
|
||||||
for (unsigned i = 0; args[i]; i++) {
|
|
||||||
len += strlen(args[i]) + 1;
|
|
||||||
if (strchr(args[i], ' '))
|
|
||||||
len += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now build the command line.
|
|
||||||
char *command = reinterpret_cast<char *>(_alloca(len+1));
|
|
||||||
char *p = command;
|
|
||||||
|
|
||||||
for (unsigned i = 0; args[i]; i++) {
|
|
||||||
const char *arg = args[i];
|
|
||||||
size_t len = strlen(arg);
|
|
||||||
bool needsQuoting = strchr(arg, ' ') != 0;
|
|
||||||
if (needsQuoting)
|
|
||||||
*p++ = '"';
|
|
||||||
memcpy(p, arg, len);
|
|
||||||
p += len;
|
|
||||||
if (needsQuoting)
|
|
||||||
*p++ = '"';
|
|
||||||
*p++ = ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = 0;
|
|
||||||
|
|
||||||
// The pointer to the environment block for the new process.
|
|
||||||
char *envblock = 0;
|
|
||||||
|
|
||||||
if (envp) {
|
|
||||||
// An environment block consists of a null-terminated block of
|
|
||||||
// null-terminated strings. Convert the array of environment variables to
|
|
||||||
// an environment block by concatenating them.
|
|
||||||
|
|
||||||
// First, determine the length of the environment block.
|
|
||||||
len = 0;
|
|
||||||
for (unsigned i = 0; envp[i]; i++)
|
|
||||||
len += strlen(envp[i]) + 1;
|
|
||||||
|
|
||||||
// Now build the environment block.
|
|
||||||
envblock = reinterpret_cast<char *>(_alloca(len+1));
|
|
||||||
p = envblock;
|
|
||||||
|
|
||||||
for (unsigned i = 0; envp[i]; i++) {
|
|
||||||
const char *ev = envp[i];
|
|
||||||
size_t len = strlen(ev) + 1;
|
|
||||||
memcpy(p, ev, len);
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a child process.
|
|
||||||
STARTUPINFO si;
|
|
||||||
memset(&si, 0, sizeof(si));
|
|
||||||
si.cb = sizeof(si);
|
|
||||||
si.hStdInput = INVALID_HANDLE_VALUE;
|
|
||||||
si.hStdOutput = INVALID_HANDLE_VALUE;
|
|
||||||
si.hStdError = INVALID_HANDLE_VALUE;
|
|
||||||
|
|
||||||
if (redirects) {
|
|
||||||
si.dwFlags = STARTF_USESTDHANDLES;
|
|
||||||
|
|
||||||
si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg);
|
|
||||||
if (si.hStdInput == INVALID_HANDLE_VALUE) {
|
|
||||||
MakeErrMsg(ErrMsg, "can't redirect stdin");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg);
|
|
||||||
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(si.hStdInput);
|
|
||||||
MakeErrMsg(ErrMsg, "can't redirect stdout");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) {
|
|
||||||
// If stdout and stderr should go to the same place, redirect stderr
|
|
||||||
// to the handle already open for stdout.
|
|
||||||
DuplicateHandle(GetCurrentProcess(), si.hStdOutput,
|
|
||||||
GetCurrentProcess(), &si.hStdError,
|
|
||||||
0, TRUE, DUPLICATE_SAME_ACCESS);
|
|
||||||
} else {
|
|
||||||
// Just redirect stderr
|
|
||||||
si.hStdError = RedirectIO(redirects[2], 2, ErrMsg);
|
|
||||||
if (si.hStdError == INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(si.hStdInput);
|
|
||||||
CloseHandle(si.hStdOutput);
|
|
||||||
MakeErrMsg(ErrMsg, "can't redirect stderr");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PROCESS_INFORMATION pi;
|
|
||||||
memset(&pi, 0, sizeof(pi));
|
|
||||||
|
|
||||||
fflush(stdout);
|
|
||||||
fflush(stderr);
|
|
||||||
BOOL rc = CreateProcess(path.c_str(), command, NULL, NULL, TRUE, 0,
|
|
||||||
envblock, NULL, &si, &pi);
|
|
||||||
DWORD err = GetLastError();
|
|
||||||
|
|
||||||
// Regardless of whether the process got created or not, we are done with
|
|
||||||
// the handles we created for it to inherit.
|
|
||||||
CloseHandle(si.hStdInput);
|
|
||||||
CloseHandle(si.hStdOutput);
|
|
||||||
CloseHandle(si.hStdError);
|
|
||||||
|
|
||||||
// Now return an error if the process didn't get created.
|
|
||||||
if (!rc)
|
|
||||||
{
|
|
||||||
SetLastError(err);
|
|
||||||
MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") +
|
|
||||||
path.toString() + "'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure these get closed no matter what.
|
|
||||||
AutoHandle hProcess(pi.hProcess);
|
|
||||||
AutoHandle hThread(pi.hThread);
|
|
||||||
|
|
||||||
// Assign the process to a job if a memory limit is defined.
|
|
||||||
AutoHandle hJob(0);
|
|
||||||
if (memoryLimit != 0) {
|
|
||||||
hJob = CreateJobObject(0, 0);
|
|
||||||
bool success = false;
|
|
||||||
if (hJob != 0) {
|
|
||||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
|
|
||||||
memset(&jeli, 0, sizeof(jeli));
|
|
||||||
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
|
|
||||||
jeli.ProcessMemoryLimit = uintptr_t(memoryLimit) * 1048576;
|
|
||||||
if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
|
|
||||||
&jeli, sizeof(jeli))) {
|
|
||||||
if (AssignProcessToJobObject(hJob, pi.hProcess))
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
SetLastError(GetLastError());
|
|
||||||
MakeErrMsg(ErrMsg, std::string("Unable to set memory limit"));
|
|
||||||
TerminateProcess(pi.hProcess, 1);
|
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Program::ChangeStdinToBinary(){
|
bool Program::ChangeStdinToBinary(){
|
||||||
int result = _setmode( _fileno(stdin), _O_BINARY );
|
int result = _setmode( _fileno(stdin), _O_BINARY );
|
||||||
return result == -1;
|
return result == -1;
|
||||||
|
Loading…
Reference in New Issue
Block a user