1
0
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:
Mikhail Glushenkov 2009-07-18 21:43:12 +00:00
parent fc0fe7ea20
commit 583e23d726
4 changed files with 194 additions and 368 deletions

View File

@ -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.
);
}; };
} }
} }

View File

@ -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.

View File

@ -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;

View File

@ -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;