ENH: Merged changes from KWSys-MultiProcess-bp to KWSys-MultiProcess-b2t-1-mp to main tree. This introduces support for process pipelines.

This commit is contained in:
Brad King 2003-12-03 09:20:05 -05:00
parent be15d66e37
commit ad8bc4b1a4
4 changed files with 1212 additions and 520 deletions

View File

@ -30,6 +30,7 @@
#define kwsysProcess_New kwsys(Process_New)
#define kwsysProcess_Delete kwsys(Process_Delete)
#define kwsysProcess_SetCommand kwsys(Process_SetCommand)
#define kwsysProcess_AddCommand kwsys(Process_AddCommand)
#define kwsysProcess_SetTimeout kwsys(Process_SetTimeout)
#define kwsysProcess_SetWorkingDirectory kwsys(Process_SetWorkingDirectory)
#define kwsysProcess_Option_HideWindow kwsys(Process_Option_HideWindow)
@ -89,10 +90,21 @@ kwsysEXPORT void kwsysProcess_Delete(kwsysProcess* cp);
/**
* Set the command line to be executed. Argument is an array of
* pointers to the command and each argument. Ths array must end with
* a NULL pointer.
* a NULL pointer. Any previous command lines are removed. Returns
* 1 for success and 0 otherwise.
*/
kwsysEXPORT void kwsysProcess_SetCommand(kwsysProcess* cp,
char const* const* command);
kwsysEXPORT int kwsysProcess_SetCommand(kwsysProcess* cp,
char const* const* command);
/**
* Add a command line to be executed. Argument is an array of
* pointers to the command and each argument. Ths array must end with
* a NULL pointer. If this is not the first command added, its
* standard input will be connected to the standard output of the
* previous command. Returns 1 for success and 0 otherwise.
*/
kwsysEXPORT int kwsysProcess_AddCommand(kwsysProcess* cp,
char const* const* command);
/**
* Set the timeout for the child process. The timeout period begins
@ -280,6 +292,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_New
# undef kwsysProcess_Delete
# undef kwsysProcess_SetCommand
# undef kwsysProcess_AddCommand
# undef kwsysProcess_SetTimeout
# undef kwsysProcess_SetWorkingDirectory
# undef kwsysProcess_Option_HideWindow

View File

@ -27,6 +27,8 @@ PURPOSE. See the above copyright notices for more information.
#include <windows.h>
#include <stdio.h>
void ReportLastError(HANDLE errorPipe);
int main()
{
/* Process startup information for the real child. */
@ -49,6 +51,11 @@ int main()
/* Handle to the error reporting pipe provided by the parent. This
is parsed off the command line. */
HANDLE errorPipe = 0;
HANDLE errorPipeOrig = 0;
/* Handle to the event the parent uses to tell us to resume the child.
This is parsed off the command line. */
HANDLE resumeEvent = 0;
/* Handle to the event the parent uses to tell us to kill the child.
This is parsed off the command line. */
@ -75,7 +82,12 @@ int main()
/* Parse the error pipe handle. */
while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
sscanf(cmdLine, "%p", &errorPipe);
sscanf(cmdLine, "%p", &errorPipeOrig);
/* Parse the resume event handle. */
while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
sscanf(cmdLine, "%p", &resumeEvent);
/* Parse the kill event handle. */
while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
@ -91,6 +103,22 @@ int main()
while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
/* Create a non-inherited copy of the error pipe. We do not want
the child to get it. */
if(DuplicateHandle(GetCurrentProcess(), errorPipeOrig,
GetCurrentProcess(), &errorPipe,
0, FALSE, DUPLICATE_SAME_ACCESS))
{
/* Have a non-inherited duplicate. Close the inherited one. */
CloseHandle(errorPipeOrig);
}
else
{
/* Could not duplicate handle. Report the error. */
ReportLastError(errorPipeOrig);
return 1;
}
/* Create the subprocess. */
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
@ -100,28 +128,41 @@ int main()
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess(0, cmdLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
if(CreateProcess(0, cmdLine, 0, 0, TRUE, CREATE_SUSPENDED, 0, 0, &si, &pi))
{
/* Process created successfully. Close the error reporting pipe
to notify the parent of success. */
CloseHandle(errorPipe);
}
else
{
/* Error creating the process. Report the error to the parent
process through the special error reporting pipe. */
LPVOID lpMsgBuf;
DWORD n;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
WriteFile(errorPipe, lpMsgBuf, strlen(lpMsgBuf)+1, &n, 0);
LocalFree( lpMsgBuf );
ReportLastError(errorPipe);
return 1;
}
CloseHandle(pi.hThread);
/* Wait for resume or kill event from parent. */
waitHandles[0] = killEvent;
waitHandles[1] = resumeEvent;
waitResult = WaitForMultipleObjects(2, waitHandles, 0, INFINITE);
/* Check what happened. */
if(waitResult == WAIT_OBJECT_0)
{
/* We were asked to kill the child. */
TerminateProcess(pi.hProcess, 255);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 1;
}
else
{
/* We were asked to resume the child. */
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
}
/* Wait for subprocess to exit or for kill event from parent. */
waitHandles[0] = killEvent;
@ -137,20 +178,30 @@ int main()
CloseHandle(pi.hProcess);
return 1;
}
else if(GetExitCodeProcess(pi.hProcess, &retVal))
else
{
/* The child exited and we could get the return code. */
/* The child exited. Get the return code. */
GetExitCodeProcess(pi.hProcess, &retVal);
CloseHandle(pi.hProcess);
return retVal;
}
else
{
/* The child exited and we could not get the return code. Report
the problem to the parent process. */
DWORD n;
const char* msg = "Failed to get process return code.";
WriteFile(errorPipe, msg, strlen(msg)+1, &n, 0);
CloseHandle(pi.hProcess);
return -1;
}
}
void ReportLastError(HANDLE errorPipe)
{
LPVOID lpMsgBuf;
DWORD n;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
WriteFile(errorPipe, lpMsgBuf, strlen(lpMsgBuf)+1, &n, 0);
LocalFree( lpMsgBuf );
}

View File

@ -33,6 +33,17 @@ conjunction with the timeout on the select call to implement a
timeout for program even when it closes stdout and stderr.
*/
/*
TODO:
We cannot create the pipeline of processes in suspended states. How
do we cleanup processes already started when one fails to load? Right
now we are just killing them, which is probably not the right thing to
do.
*/
#include <stdio.h> /* snprintf */
#include <stdlib.h> /* malloc, free */
#include <string.h> /* strdup, strerror, memset */
@ -46,23 +57,35 @@ timeout for program even when it closes stdout and stderr.
#include <signal.h> /* sigaction */
/* The number of pipes for the child's output. The standard stdout
and stderr pipes are the first two. One more pipe is used for the
child to report errors to the parent before the real process is
invoked. */
and stderr pipes are the first two. One more pipe is used to
detect when the child process has terminated. The third pipe is
not given to the child process, so it cannot close it until it
terminates. */
#define KWSYSPE_PIPE_COUNT 3
#define KWSYSPE_PIPE_STDOUT 0
#define KWSYSPE_PIPE_STDERR 1
#define KWSYSPE_PIPE_ERROR 2
#define KWSYSPE_PIPE_TERM 2
/* The maximum amount to read from a pipe at a time. */
#define KWSYSPE_PIPE_BUFFER_SIZE 1024
typedef struct timeval kwsysProcessTime;
typedef struct kwsysProcessCreateInformation_s
{
int stdin;
int stdout;
int stderr;
int term;
int error[2];
} kwsysProcessCreateInformation;
/*--------------------------------------------------------------------------*/
static void kwsysProcessInitialize(kwsysProcess* cp);
static int kwsysProcessInitialize(kwsysProcess* cp);
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
static void kwsysProcessCleanupDescriptor(int* pfd);
static int kwsysProcessCreate(kwsysProcess* cp, int index,
kwsysProcessCreateInformation* si, int* readEnd);
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
kwsysProcessTime* timeoutTime);
static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
@ -73,30 +96,28 @@ static kwsysProcessTime kwsysProcessTimeFromDouble(double d);
static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2);
static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2);
static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2);
static void kwsysProcessChildErrorExit(kwsysProcess* cp);
static void kwsysProcessChildErrorExit(int errorPipe);
static void kwsysProcessRestoreDefaultSignalHandlers();
/*--------------------------------------------------------------------------*/
/* Structure containing data used to implement the child's execution. */
struct kwsysProcess_s
{
/* The command line to execute. */
char** Command;
/* The command lines to execute. */
char*** Commands;
int NumberOfCommands;
/* Descriptors for the read ends of the child's output pipes. */
int PipeReadEnds[KWSYSPE_PIPE_COUNT];
/* Descriptors for the write ends of the child's output pipes. */
int PipeWriteEnds[KWSYSPE_PIPE_COUNT];
/* Buffer for pipe data. */
char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
/* Process ID returned by the fork. */
pid_t ForkPID;
/* Process IDs returned by the calls to fork. */
pid_t* ForkPIDs;
/* Flag for whether the child reported an error. */
int ChildError;
/* Flag for whether the children were terminated by a faild select. */
int SelectError;
/* The timeout length. */
double Timeout;
@ -140,7 +161,9 @@ struct kwsysProcess_s
/* Buffer for error message in case of failure. */
char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
int ErrorMessageLength;
/* The exit codes of each child process in the pipeline. */
int* CommandExitCodes;
};
/*--------------------------------------------------------------------------*/
@ -169,36 +192,109 @@ void kwsysProcess_Delete(kwsysProcess* cp)
/* Free memory. */
kwsysProcess_SetCommand(cp, 0);
kwsysProcess_SetWorkingDirectory(cp, 0);
if(cp->CommandExitCodes)
{
free(cp->CommandExitCodes);
}
free(cp);
}
/*--------------------------------------------------------------------------*/
void kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
{
if(cp->Command)
int i;
for(i=0; i < cp->NumberOfCommands; ++i)
{
char** c = cp->Command;
char** c = cp->Commands[i];
while(*c)
{
free(*c++);
}
free(cp->Command);
cp->Command = 0;
free(cp->Commands[i]);
}
cp->NumberOfCommands = 0;
if(cp->Commands)
{
free(cp->Commands);
cp->Commands = 0;
}
if(command)
{
char const* const* c = command;
int n = 0;
int i = 0;
while(*c++);
n = c - command - 1;
cp->Command = (char**)malloc((n+1)*sizeof(char*));
for(i=0; i < n; ++i)
{
cp->Command[i] = strdup(command[i]);
}
cp->Command[n] = 0;
return kwsysProcess_AddCommand(cp, command);
}
return 1;
}
/*--------------------------------------------------------------------------*/
int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command)
{
int newNumberOfCommands;
char*** newCommands;
/* Make sure we have a command to add. */
if(!command)
{
return 0;
}
/* Allocate a new array for command pointers. */
newNumberOfCommands = cp->NumberOfCommands + 1;
if(!(newCommands = (char***)malloc(sizeof(char**) * newNumberOfCommands)))
{
/* Out of memory. */
return 0;
}
/* Copy any existing commands into the new array. */
{
int i;
for(i=0; i < cp->NumberOfCommands; ++i)
{
newCommands[i] = cp->Commands[i];
}
}
/* Add the new command. */
{
char const* const* c = command;
int n = 0;
int i = 0;
while(*c++);
n = c - command - 1;
newCommands[cp->NumberOfCommands] = (char**)malloc((n+1)*sizeof(char*));
if(!newCommands[cp->NumberOfCommands])
{
/* Out of memory. */
free(newCommands);
return 0;
}
for(i=0; i < n; ++i)
{
newCommands[cp->NumberOfCommands][i] = strdup(command[i]);
if(!newCommands[cp->NumberOfCommands][i])
{
break;
}
}
if(i < n)
{
/* Out of memory. */
for(;i > 0; --i)
{
free(newCommands[cp->NumberOfCommands][i-1]);
}
free(newCommands);
return 0;
}
newCommands[cp->NumberOfCommands][n] = 0;
}
/* Successfully allocated new command array. Free the old array. */
free(cp->Commands);
cp->Commands = newCommands;
cp->NumberOfCommands = newNumberOfCommands;
return 1;
}
/*--------------------------------------------------------------------------*/
@ -289,6 +385,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
{
int i;
struct sigaction newSigChldAction;
kwsysProcessCreateInformation si = {-1, -1, -1, -1, {-1, -1}};
/* Do not execute a second copy simultaneously. */
if(cp->State == kwsysProcess_State_Executing)
@ -297,7 +394,12 @@ void kwsysProcess_Execute(kwsysProcess* cp)
}
/* Initialize the control structure for a new process. */
kwsysProcessInitialize(cp);
if(!kwsysProcessInitialize(cp))
{
strcpy(cp->ErrorMessage, "Out of memory");
cp->State = kwsysProcess_State_Error;
return;
}
/* We want no special handling of SIGCHLD. Repeat call until it is
not interrupted. */
@ -306,29 +408,37 @@ void kwsysProcess_Execute(kwsysProcess* cp)
while((sigaction(SIGCHLD, &newSigChldAction, &cp->OldSigChldAction) < 0) &&
(errno == EINTR));
/* Create pipes for subprocess output. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
/* Setup the stderr and termination pipes to be shared by all processes. */
for(i=KWSYSPE_PIPE_STDERR; i < KWSYSPE_PIPE_COUNT; ++i)
{
int p[2];
/* Create the pipe. */
int p[2];
if(pipe(p) < 0)
{
kwsysProcessCleanup(cp, 1);
return;
}
/* Set close-on-exec flag on the pipe's ends. */
if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
(fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
{
kwsysProcessCleanup(cp, 1);
return;
}
/* Store the pipe. */
cp->PipeReadEnds[i] = p[0];
cp->PipeWriteEnds[i] = p[1];
if(i == KWSYSPE_PIPE_STDERR)
{
si.stderr = p[1];
}
else
{
si.term = p[1];
}
/* Set close-on-exec flag on the pipe's ends. */
if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
(fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
{
kwsysProcessCleanup(cp, 1);
kwsysProcessCleanupDescriptor(&si.stderr);
kwsysProcessCleanupDescriptor(&si.term);
return;
}
}
/* The timeout period starts now. */
@ -336,62 +446,37 @@ void kwsysProcess_Execute(kwsysProcess* cp)
cp->TimeoutTime.tv_sec = -1;
cp->TimeoutTime.tv_usec = -1;
/* Fork off a child process. */
cp->ForkPID = fork();
if(cp->ForkPID < 0)
/* Create the pipeline of processes. */
{
int readEnd = 0;
for(i=0; i < cp->NumberOfCommands; ++i)
{
kwsysProcessCleanup(cp, 1);
return;
}
/* If this is the child process, run the real process. */
if(cp->ForkPID == 0)
{
/* We used to close stdin, but some programs do not like being run
without stdin. Just use whatever stdin the parent program is
using. */
/*close(0);*/
/* Setup the stdout/stderr pipes. */
dup2(cp->PipeWriteEnds[KWSYSPE_PIPE_STDOUT], 1);
dup2(cp->PipeWriteEnds[KWSYSPE_PIPE_STDERR], 2);
/* Clear the close-on-exec flag for stdout, stderr, and the child
error report pipe. All other pipe handles will be closed when
exec succeeds. */
fcntl(1, F_SETFD, 0);
fcntl(2, F_SETFD, 0);
fcntl(cp->PipeWriteEnds[KWSYSPE_PIPE_ERROR], F_SETFD, 0);
/* Restore all default signal handlers. */
kwsysProcessRestoreDefaultSignalHandlers();
/* Change to the working directory specified, if any. */
if(cp->WorkingDirectory)
if(!kwsysProcessCreate(cp, i, &si, &readEnd))
{
/* Some platforms specify that the chdir call may be
interrupted. Repeat the call until it finishes. */
int r;
while(((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR));
if(r < 0)
kwsysProcessCleanup(cp, 1);
/* Release resources that may have been allocated for this
process before an error occurred. */
kwsysProcessCleanupDescriptor(&readEnd);
if(i > 0)
{
/* Failure. Report error to parent and terminate. */
kwsysProcessChildErrorExit(cp);
kwsysProcessCleanupDescriptor(&si.stdin);
}
kwsysProcessCleanupDescriptor(&si.stdout);
kwsysProcessCleanupDescriptor(&si.stderr);
kwsysProcessCleanupDescriptor(&si.term);
kwsysProcessCleanupDescriptor(&si.error[0]);
kwsysProcessCleanupDescriptor(&si.error[1]);
return;
}
/* Execute the real process. If successful, this does not return. */
execvp(cp->Command[0], cp->Command);
/* Failure. Report error to parent and terminate. */
kwsysProcessChildErrorExit(cp);
}
/* Save a handle to the output pipe for the last process. */
cp->PipeReadEnds[KWSYSPE_PIPE_STDOUT] = readEnd;
}
/* The parent process does not need the pipe write ends. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
kwsysProcessCleanupDescriptor(&cp->PipeWriteEnds[i]);
}
/* The parent process does not need the output pipe write ends. */
kwsysProcessCleanupDescriptor(&si.stderr);
kwsysProcessCleanupDescriptor(&si.term);
/* All the pipes are now open. */
cp->PipesLeft = KWSYSPE_PIPE_COUNT;
@ -401,8 +486,8 @@ void kwsysProcess_Execute(kwsysProcess* cp)
}
/*--------------------------------------------------------------------------*/
int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* length,
double* userTimeout)
int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data,
int* length, double* userTimeout)
{
int i;
int max = -1;
@ -448,22 +533,9 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
if(n > 0)
{
/* We have data on this pipe. */
if(i == KWSYSPE_PIPE_ERROR)
if(i == KWSYSPE_PIPE_TERM)
{
/* This is data on the special error reporting pipe. The
child process failed to execute the program. */
cp->ChildError = 1;
if(n > KWSYSPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength)
{
n = KWSYSPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength;
}
if(n > 0)
{
memcpy(cp->ErrorMessage+cp->ErrorMessageLength,
cp->PipeBuffer, n);
cp->ErrorMessageLength += n;
cp->ErrorMessage[cp->ErrorMessageLength] = 0;
}
/* This is data on the special termination pipe. Ignore it. */
}
else if(pipes & (1 << i))
{
@ -548,10 +620,10 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
pipe buffer. */
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
/* Kill the child now. */
/* Kill the children now. */
kwsysProcess_Kill(cp);
cp->Killed = 0;
cp->ChildError = 1;
cp->SelectError = 1;
cp->PipesLeft = 0;
}
}
@ -586,7 +658,7 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
}
else
{
/* The process timeout has expired. Kill the child now. */
/* The process timeout has expired. Kill the children now. */
kwsysProcess_Kill(cp);
cp->Killed = 0;
cp->TimeoutExpired = 1;
@ -623,19 +695,36 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
}
}
/* Wait for the child to terminate. The process should have already
exited because KWSYSPE_PIPE_ERROR has been closed by this point.
Repeat the call until it is not interrupted. */
while(((result = waitpid(cp->ForkPID, &status, 0)) < 0) && (errno == EINTR));
if(result <= 0)
/* Wait for each child to terminate. The process should have
already exited because KWSYSPE_PIPE_TERM has been closed by this
point. Repeat the call until it is not interrupted. */
{
int i;
for(i=0; i < cp->NumberOfCommands; ++i)
{
/* Unexpected error. */
kwsysProcessCleanup(cp, 1);
while(((result = waitpid(cp->ForkPIDs[i],
&cp->CommandExitCodes[i], 0)) < 0) &&
(errno == EINTR));
if(result <= 0 && cp->State != kwsysProcess_State_Error)
{
/* Unexpected error. Report the first time this happens. */
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
cp->State = kwsysProcess_State_Error;
}
}
}
/* Check if there was an error in one of the waitpid calls. */
if(cp->State == kwsysProcess_State_Error)
{
/* The error message is already in its buffer. Tell
kwsysProcessCleanup to not create it. */
kwsysProcessCleanup(cp, 0);
return 1;
}
/* Check whether the child reported an error invoking the process. */
if(cp->ChildError)
if(cp->SelectError)
{
/* The error message is already in its buffer. Tell
kwsysProcessCleanup to not create it. */
@ -644,6 +733,9 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
return 1;
}
/* Use the status of the last process in the pipeline. */
status = cp->CommandExitCodes[cp->NumberOfCommands-1];
/* Determine the outcome. */
if(cp->Killed)
{
@ -703,29 +795,35 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
/*--------------------------------------------------------------------------*/
void kwsysProcess_Kill(kwsysProcess* cp)
{
int i;
/* Make sure we are executing a process. */
if(cp->State != kwsysProcess_State_Executing)
{
return;
}
/* Kill the child. */
/* Kill the children. */
cp->Killed = 1;
kill(cp->ForkPID, SIGKILL);
for(i=0; i < cp->NumberOfCommands; ++i)
{
if(cp->ForkPIDs[i])
{
kill(cp->ForkPIDs[i], SIGKILL);
}
}
}
/*--------------------------------------------------------------------------*/
/* Initialize a process control structure for kwsysProcess_Execute. */
static void kwsysProcessInitialize(kwsysProcess* cp)
static int kwsysProcessInitialize(kwsysProcess* cp)
{
int i;
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
cp->PipeReadEnds[i] = -1;
cp->PipeWriteEnds[i] = -1;
}
cp->ForkPID = -1;
cp->ChildError = 0;
cp->SelectError = 0;
cp->StartTime.tv_sec = -1;
cp->StartTime.tv_usec = -1;
cp->TimeoutTime.tv_sec = -1;
@ -739,7 +837,30 @@ static void kwsysProcessInitialize(kwsysProcess* cp)
cp->ExitCode = 1;
cp->ExitValue = 1;
cp->ErrorMessage[0] = 0;
cp->ErrorMessageLength = 0;
if(cp->ForkPIDs)
{
free(cp->ForkPIDs);
}
cp->ForkPIDs = (pid_t*)malloc(sizeof(pid_t)*cp->NumberOfCommands);
if(!cp->ForkPIDs)
{
return 0;
}
memset(cp->ForkPIDs, 0, sizeof(pid_t)*cp->NumberOfCommands);
if(cp->CommandExitCodes)
{
free(cp->CommandExitCodes);
}
cp->CommandExitCodes = (int*)malloc(sizeof(int)*cp->NumberOfCommands);
if(!cp->CommandExitCodes)
{
return 0;
}
memset(cp->CommandExitCodes, 0, sizeof(int)*cp->NumberOfCommands);
return 1;
}
/*--------------------------------------------------------------------------*/
@ -749,22 +870,46 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error)
{
int i;
/* If cleaning up due to an error, report the error message. */
if(error)
{
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
/* We are cleaning up due to an error. Report the error message
if one has not been provided already. */
if(cp->ErrorMessage[0] == 0)
{
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
}
/* Set the error state. */
cp->State = kwsysProcess_State_Error;
/* Kill any children already started. */
if(cp->ForkPIDs)
{
for(i=0; i < cp->NumberOfCommands; ++i)
{
if(cp->ForkPIDs[i])
{
kill(cp->ForkPIDs[i], SIGKILL);
}
}
}
}
/* Restore the SIGCHLD handler. */
while((sigaction(SIGCHLD, &cp->OldSigChldAction, 0) < 0) &&
(errno == EINTR));
/* Free memory. */
if(cp->ForkPIDs)
{
free(cp->ForkPIDs);
cp->ForkPIDs = 0;
}
/* Close pipe handles. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
kwsysProcessCleanupDescriptor(&cp->PipeWriteEnds[i]);
}
}
@ -781,6 +926,148 @@ static void kwsysProcessCleanupDescriptor(int* pfd)
}
}
/*--------------------------------------------------------------------------*/
int kwsysProcessCreate(kwsysProcess* cp, int index,
kwsysProcessCreateInformation* si, int* readEnd)
{
/* Setup the process's stdin. */
if(index > 0)
{
si->stdin = *readEnd;
*readEnd = 0;
}
else
{
si->stdin = 0;
}
/* Setup the process's stdout. */
{
/* Create the pipe. */
int p[2];
if(pipe(p) < 0)
{
return 0;
}
*readEnd = p[0];
si->stdout = p[1];
/* Set close-on-exec flag on the pipe's ends. */
if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
(fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
{
return 0;
}
}
/* Create the error reporting pipe. */
if(pipe(si->error) < 0)
{
return 0;
}
/* Set close-on-exec flag on the error pipe's write end. */
if(fcntl(si->error[1], F_SETFD, FD_CLOEXEC) < 0)
{
return 0;
}
/* Fork off a child process. */
cp->ForkPIDs[index] = fork();
if(cp->ForkPIDs[index] < 0)
{
return 0;
}
if(cp->ForkPIDs[index] == 0)
{
/* Close the read end of the error reporting pipe. */
close(si->error[0]);
/* Setup the stdin, stdout, and stderr pipes. */
if(index > 0)
{
dup2(si->stdin, 0);
}
dup2(si->stdout, 1);
dup2(si->stderr, 2);
/* Clear the close-on-exec flag for stdin, stdout, and stderr.
Also clear it for the termination pipe. All other pipe handles
will be closed when exec succeeds. */
fcntl(0, F_SETFD, 0);
fcntl(1, F_SETFD, 0);
fcntl(2, F_SETFD, 0);
fcntl(si->term, F_SETFD, 0);
/* Restore all default signal handlers. */
kwsysProcessRestoreDefaultSignalHandlers();
/* Change to the working directory specified, if any. */
if(cp->WorkingDirectory)
{
/* Some platforms specify that the chdir call may be
interrupted. Repeat the call until it finishes. */
int r;
while(((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR));
if(r < 0)
{
/* Failure. Report error to parent and terminate. */
kwsysProcessChildErrorExit(si->error[1]);
}
}
/* Execute the real process. If successful, this does not return. */
execvp(cp->Commands[index][0], cp->Commands[index]);
/* Failure. Report error to parent and terminate. */
kwsysProcessChildErrorExit(si->error[1]);
}
/* We are done with the error reporting pipe write end. */
kwsysProcessCleanupDescriptor(&si->error[1]);
/* Block until the child's exec call succeeds and closes the error
pipe or writes data to the pipe to report an error. */
{
int total = 0;
int n = 1;
/* Read the entire error message up to the length of our buffer. */
while(total < KWSYSPE_PIPE_BUFFER_SIZE && n > 0)
{
/* Keep trying to read until the operation is not interrupted. */
while(((n = read(si->error[0], cp->ErrorMessage+total,
KWSYSPE_PIPE_BUFFER_SIZE-total)) < 0) &&
(errno == EINTR));
if(n > 0)
{
total += n;
}
}
/* We are done with the error reporting pipe read end. */
kwsysProcessCleanupDescriptor(&si->error[0]);
if(total > 0)
{
/* The child failed to execute the process. */
return 0;
}
}
/* Successfully created this child process. */
if(index > 0)
{
/* The parent process does not need the input pipe read end. */
kwsysProcessCleanupDescriptor(&si->stdin);
}
/* The parent process does not need the output pipe write ends. */
kwsysProcessCleanupDescriptor(&si->stdout);
return 1;
}
/*--------------------------------------------------------------------------*/
/* Get the time at which either the process or user timeout will
expire. Returns 1 if the user timeout is first, and 0 otherwise. */
@ -905,14 +1192,14 @@ static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProc
/* When the child process encounters an error before its program is
invoked, this is called to report the error to the parent and
exit. */
static void kwsysProcessChildErrorExit(kwsysProcess* cp)
static void kwsysProcessChildErrorExit(int errorPipe)
{
/* Construct the error message. */
char buffer[KWSYSPE_PIPE_BUFFER_SIZE];
strncpy(buffer, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
/* Report the error to the parent through the special pipe. */
write(cp->PipeWriteEnds[KWSYSPE_PIPE_ERROR], buffer, strlen(buffer));
write(errorPipe, buffer, strlen(buffer));
/* Terminate without cleanup. */
_exit(1);

File diff suppressed because it is too large Load Diff