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:
parent
be15d66e37
commit
ad8bc4b1a4
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user