BUG: Reverting previous change until it can be fixed on Cygwin.
This commit is contained in:
parent
677fcd1174
commit
29b75dda97
@ -24,18 +24,17 @@
|
|||||||
|
|
||||||
Implementation for UNIX
|
Implementation for UNIX
|
||||||
|
|
||||||
On UNIX, a child process is forked to exec the program. Three output
|
On UNIX, a child process is forked to exec the program. Three
|
||||||
pipes are read by the parent process using a select call to block
|
output pipes from the child are read by the parent process using a
|
||||||
until data are ready. Two of the pipes are stdout and stderr for the
|
select call to block until data are ready. Two of the pipes are
|
||||||
child. The third is a special pipe populated by a signal handler to
|
stdout and stderr for the child. The third is a special error pipe
|
||||||
indicate that a child has terminated. This is used in conjunction
|
that has two purposes. First, if the child cannot exec the program,
|
||||||
with the timeout on the select call to implement a timeout for program
|
the error is reported through the error pipe. Second, the error
|
||||||
even when it closes stdout and stderr and at the same time avoiding
|
pipe is left open until the child exits. This is used in
|
||||||
races.
|
conjunction with the timeout on the select call to implement a
|
||||||
|
timeout for program even when it closes stdout and stderr.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
@ -69,7 +68,7 @@ do.
|
|||||||
#define KWSYSPE_PIPE_COUNT 3
|
#define KWSYSPE_PIPE_COUNT 3
|
||||||
#define KWSYSPE_PIPE_STDOUT 0
|
#define KWSYSPE_PIPE_STDOUT 0
|
||||||
#define KWSYSPE_PIPE_STDERR 1
|
#define KWSYSPE_PIPE_STDERR 1
|
||||||
#define KWSYSPE_PIPE_SIGNAL 2
|
#define KWSYSPE_PIPE_TERM 2
|
||||||
|
|
||||||
/* The maximum amount to read from a pipe at a time. */
|
/* The maximum amount to read from a pipe at a time. */
|
||||||
#define KWSYSPE_PIPE_BUFFER_SIZE 1024
|
#define KWSYSPE_PIPE_BUFFER_SIZE 1024
|
||||||
@ -90,6 +89,7 @@ typedef struct kwsysProcessCreateInformation_s
|
|||||||
int StdIn;
|
int StdIn;
|
||||||
int StdOut;
|
int StdOut;
|
||||||
int StdErr;
|
int StdErr;
|
||||||
|
int TermPipe;
|
||||||
int ErrorPipe[2];
|
int ErrorPipe[2];
|
||||||
} kwsysProcessCreateInformation;
|
} kwsysProcessCreateInformation;
|
||||||
|
|
||||||
@ -99,7 +99,6 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error);
|
|||||||
static void kwsysProcessCleanupDescriptor(int* pfd);
|
static void kwsysProcessCleanupDescriptor(int* pfd);
|
||||||
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
kwsysProcessCreateInformation* si, int* readEnd);
|
kwsysProcessCreateInformation* si, int* readEnd);
|
||||||
static void kwsysProcessDestroy(kwsysProcess* cp);
|
|
||||||
static int kwsysProcessSetupOutputPipeFile(int* p, const char* name);
|
static int kwsysProcessSetupOutputPipeFile(int* p, const char* name);
|
||||||
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
|
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
|
||||||
kwsysProcessTime* timeoutTime);
|
kwsysProcessTime* timeoutTime);
|
||||||
@ -118,10 +117,6 @@ static void kwsysProcessRestoreDefaultSignalHandlers(void);
|
|||||||
static pid_t kwsysProcessFork(kwsysProcess* cp,
|
static pid_t kwsysProcessFork(kwsysProcess* cp,
|
||||||
kwsysProcessCreateInformation* si);
|
kwsysProcessCreateInformation* si);
|
||||||
static void kwsysProcessKill(pid_t process_id);
|
static void kwsysProcessKill(pid_t process_id);
|
||||||
static int kwsysProcessesAdd(kwsysProcess* cp);
|
|
||||||
static void kwsysProcessesRemove(kwsysProcess* cp);
|
|
||||||
static void kwsysProcessesSignalHandler(int signum, siginfo_t* info,
|
|
||||||
void* ucontext);
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
/* Structure containing data used to implement the child's execution. */
|
/* Structure containing data used to implement the child's execution. */
|
||||||
@ -131,13 +126,9 @@ struct kwsysProcess_s
|
|||||||
char*** Commands;
|
char*** Commands;
|
||||||
int NumberOfCommands;
|
int NumberOfCommands;
|
||||||
|
|
||||||
/* Descriptors for the read ends of the child's output pipes and
|
/* Descriptors for the read ends of the child's output pipes. */
|
||||||
the signal pipe. */
|
|
||||||
int PipeReadEnds[KWSYSPE_PIPE_COUNT];
|
int PipeReadEnds[KWSYSPE_PIPE_COUNT];
|
||||||
|
|
||||||
/* Write descriptor for child termination signal pipe. */
|
|
||||||
int SignalPipe;
|
|
||||||
|
|
||||||
/* Buffer for pipe data. */
|
/* Buffer for pipe data. */
|
||||||
char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
|
char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
|
||||||
|
|
||||||
@ -168,15 +159,15 @@ struct kwsysProcess_s
|
|||||||
/* Flag for whether the timeout expired. */
|
/* Flag for whether the timeout expired. */
|
||||||
int TimeoutExpired;
|
int TimeoutExpired;
|
||||||
|
|
||||||
|
/* The old SIGCHLD handler. */
|
||||||
|
struct sigaction OldSigChldAction;
|
||||||
|
|
||||||
/* The number of pipes left open during execution. */
|
/* The number of pipes left open during execution. */
|
||||||
int PipesLeft;
|
int PipesLeft;
|
||||||
|
|
||||||
/* File descriptor set for call to select. */
|
/* File descriptor set for call to select. */
|
||||||
fd_set PipeSet;
|
fd_set PipeSet;
|
||||||
|
|
||||||
/* The number of children still executing. */
|
|
||||||
int CommandsLeft;
|
|
||||||
|
|
||||||
/* The current status of the child process. */
|
/* The current status of the child process. */
|
||||||
int State;
|
int State;
|
||||||
|
|
||||||
@ -567,7 +558,8 @@ const char* kwsysProcess_GetExceptionString(kwsysProcess* cp)
|
|||||||
void kwsysProcess_Execute(kwsysProcess* cp)
|
void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
kwsysProcessCreateInformation si = {-1, -1, -1, {-1, -1}};
|
struct sigaction newSigChldAction;
|
||||||
|
kwsysProcessCreateInformation si = {-1, -1, -1, -1, {-1, -1}};
|
||||||
|
|
||||||
/* Do not execute a second copy simultaneously. */
|
/* Do not execute a second copy simultaneously. */
|
||||||
if(!cp || cp->State == kwsysProcess_State_Executing)
|
if(!cp || cp->State == kwsysProcess_State_Executing)
|
||||||
@ -605,41 +597,45 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If not running a detached child, add this object to the global
|
/* We want no special handling of SIGCHLD. Repeat call until it is
|
||||||
set of process objects that wish to be notified when a child
|
not interrupted. */
|
||||||
exits. */
|
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
||||||
if(!cp->OptionDetach)
|
newSigChldAction.sa_handler = SIG_DFL;
|
||||||
|
while((sigaction(SIGCHLD, &newSigChldAction, &cp->OldSigChldAction) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
|
||||||
|
/* Setup the stderr and termination pipes to be shared by all processes. */
|
||||||
|
for(i=KWSYSPE_PIPE_STDERR; i < KWSYSPE_PIPE_COUNT; ++i)
|
||||||
{
|
{
|
||||||
if(!kwsysProcessesAdd(cp))
|
/* Create the pipe. */
|
||||||
|
int p[2];
|
||||||
|
if(pipe(p) < 0)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup the stderr pipe to be shared by all processes. */
|
/* Store the pipe. */
|
||||||
{
|
cp->PipeReadEnds[i] = p[0];
|
||||||
/* Create the pipe. */
|
if(i == KWSYSPE_PIPE_STDERR)
|
||||||
int p[2];
|
{
|
||||||
if(pipe(p) < 0)
|
si.StdErr = p[1];
|
||||||
{
|
}
|
||||||
kwsysProcessCleanup(cp, 1);
|
else
|
||||||
return;
|
{
|
||||||
}
|
si.TermPipe = p[1];
|
||||||
|
}
|
||||||
|
|
||||||
/* Store the pipe. */
|
/* Set close-on-exec flag on the pipe's ends. */
|
||||||
cp->PipeReadEnds[KWSYSPE_PIPE_STDERR] = p[0];
|
if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
|
||||||
si.StdErr = p[1];
|
(fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
|
||||||
|
{
|
||||||
/* Set close-on-exec flag on the pipe's ends. */
|
kwsysProcessCleanup(cp, 1);
|
||||||
if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
|
kwsysProcessCleanupDescriptor(&si.StdErr);
|
||||||
(fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
|
kwsysProcessCleanupDescriptor(&si.TermPipe);
|
||||||
{
|
return;
|
||||||
kwsysProcessCleanup(cp, 1);
|
}
|
||||||
kwsysProcessCleanupDescriptor(&si.StdErr);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Replace the stderr pipe with a file if requested. In this case
|
/* Replace the stderr pipe with a file if requested. In this case
|
||||||
the select call will report that stderr is closed immediately. */
|
the select call will report that stderr is closed immediately. */
|
||||||
@ -649,6 +645,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
|||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, 1);
|
||||||
kwsysProcessCleanupDescriptor(&si.StdErr);
|
kwsysProcessCleanupDescriptor(&si.StdErr);
|
||||||
|
kwsysProcessCleanupDescriptor(&si.TermPipe);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,6 +688,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
|||||||
{
|
{
|
||||||
kwsysProcessCleanupDescriptor(&si.StdErr);
|
kwsysProcessCleanupDescriptor(&si.StdErr);
|
||||||
}
|
}
|
||||||
|
kwsysProcessCleanupDescriptor(&si.TermPipe);
|
||||||
kwsysProcessCleanupDescriptor(&si.ErrorPipe[0]);
|
kwsysProcessCleanupDescriptor(&si.ErrorPipe[0]);
|
||||||
kwsysProcessCleanupDescriptor(&si.ErrorPipe[1]);
|
kwsysProcessCleanupDescriptor(&si.ErrorPipe[1]);
|
||||||
return;
|
return;
|
||||||
@ -705,6 +703,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
|||||||
{
|
{
|
||||||
kwsysProcessCleanupDescriptor(&si.StdErr);
|
kwsysProcessCleanupDescriptor(&si.StdErr);
|
||||||
}
|
}
|
||||||
|
kwsysProcessCleanupDescriptor(&si.TermPipe);
|
||||||
|
|
||||||
/* Restore the working directory. */
|
/* Restore the working directory. */
|
||||||
if(cp->RealWorkingDirectory)
|
if(cp->RealWorkingDirectory)
|
||||||
@ -824,10 +823,9 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
|||||||
if(n > 0)
|
if(n > 0)
|
||||||
{
|
{
|
||||||
/* We have data on this pipe. */
|
/* We have data on this pipe. */
|
||||||
if(i == KWSYSPE_PIPE_SIGNAL)
|
if(i == KWSYSPE_PIPE_TERM)
|
||||||
{
|
{
|
||||||
/* A child process has terminated. */
|
/* This is data on the special termination pipe. Ignore it. */
|
||||||
kwsysProcessDestroy(cp);
|
|
||||||
}
|
}
|
||||||
else if(data && length)
|
else if(data && length)
|
||||||
{
|
{
|
||||||
@ -972,6 +970,7 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
|||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
||||||
{
|
{
|
||||||
|
int result = 0;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
int prPipe = 0;
|
int prPipe = 0;
|
||||||
|
|
||||||
@ -990,6 +989,26 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
if(!cp->Detached)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
|
{
|
||||||
|
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. */
|
/* Check if there was an error in one of the waitpid calls. */
|
||||||
if(cp->State == kwsysProcess_State_Error)
|
if(cp->State == kwsysProcess_State_Error)
|
||||||
{
|
{
|
||||||
@ -1065,18 +1084,11 @@ void kwsysProcess_Kill(kwsysProcess* cp)
|
|||||||
cp->Killed = 1;
|
cp->Killed = 1;
|
||||||
for(i=0; i < cp->NumberOfCommands; ++i)
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
{
|
{
|
||||||
int status;
|
|
||||||
if(cp->ForkPIDs[i])
|
if(cp->ForkPIDs[i])
|
||||||
{
|
{
|
||||||
/* Kill the child. */
|
|
||||||
kwsysProcessKill(cp->ForkPIDs[i]);
|
kwsysProcessKill(cp->ForkPIDs[i]);
|
||||||
|
|
||||||
/* Reap the child. Keep trying until the call is not
|
|
||||||
interrupted. */
|
|
||||||
while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && (errno == EINTR));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cp->CommandsLeft = 0;
|
|
||||||
|
|
||||||
/* Close all the pipe read ends. */
|
/* Close all the pipe read ends. */
|
||||||
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
||||||
@ -1095,7 +1107,6 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
|
|||||||
{
|
{
|
||||||
cp->PipeReadEnds[i] = -1;
|
cp->PipeReadEnds[i] = -1;
|
||||||
}
|
}
|
||||||
cp->SignalPipe = -1;
|
|
||||||
cp->SelectError = 0;
|
cp->SelectError = 0;
|
||||||
cp->StartTime.tv_sec = -1;
|
cp->StartTime.tv_sec = -1;
|
||||||
cp->StartTime.tv_usec = -1;
|
cp->StartTime.tv_usec = -1;
|
||||||
@ -1103,7 +1114,6 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
|
|||||||
cp->TimeoutTime.tv_usec = -1;
|
cp->TimeoutTime.tv_usec = -1;
|
||||||
cp->TimeoutExpired = 0;
|
cp->TimeoutExpired = 0;
|
||||||
cp->PipesLeft = 0;
|
cp->PipesLeft = 0;
|
||||||
cp->CommandsLeft = 0;
|
|
||||||
FD_ZERO(&cp->PipeSet);
|
FD_ZERO(&cp->PipeSet);
|
||||||
cp->State = kwsysProcess_State_Starting;
|
cp->State = kwsysProcess_State_Starting;
|
||||||
cp->Killed = 0;
|
cp->Killed = 0;
|
||||||
@ -1184,7 +1194,6 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error)
|
|||||||
{
|
{
|
||||||
/* Kill the child. */
|
/* Kill the child. */
|
||||||
kwsysProcessKill(cp->ForkPIDs[i]);
|
kwsysProcessKill(cp->ForkPIDs[i]);
|
||||||
|
|
||||||
/* Reap the child. Keep trying until the call is not
|
/* Reap the child. Keep trying until the call is not
|
||||||
interrupted. */
|
interrupted. */
|
||||||
while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) &&
|
while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) &&
|
||||||
@ -1200,13 +1209,9 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If not creating a detached child, remove this object from the
|
/* Restore the SIGCHLD handler. */
|
||||||
global set of process objects that wish to be notified when a
|
while((sigaction(SIGCHLD, &cp->OldSigChldAction, 0) < 0) &&
|
||||||
child exits. */
|
(errno == EINTR));
|
||||||
if(!cp->OptionDetach)
|
|
||||||
{
|
|
||||||
kwsysProcessesRemove(cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free memory. */
|
/* Free memory. */
|
||||||
if(cp->ForkPIDs)
|
if(cp->ForkPIDs)
|
||||||
@ -1355,10 +1360,12 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the close-on-exec flag for stdin, stdout, and stderr.
|
/* Clear the close-on-exec flag for stdin, stdout, and stderr.
|
||||||
All other pipe handles will be closed when exec succeeds. */
|
Also clear it for the termination pipe. All other pipe handles
|
||||||
|
will be closed when exec succeeds. */
|
||||||
fcntl(0, F_SETFD, 0);
|
fcntl(0, F_SETFD, 0);
|
||||||
fcntl(1, F_SETFD, 0);
|
fcntl(1, F_SETFD, 0);
|
||||||
fcntl(2, F_SETFD, 0);
|
fcntl(2, F_SETFD, 0);
|
||||||
|
fcntl(si->TermPipe, F_SETFD, 0);
|
||||||
|
|
||||||
/* Restore all default signal handlers. */
|
/* Restore all default signal handlers. */
|
||||||
kwsysProcessRestoreDefaultSignalHandlers();
|
kwsysProcessRestoreDefaultSignalHandlers();
|
||||||
@ -1370,9 +1377,6 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
|||||||
kwsysProcessChildErrorExit(si->ErrorPipe[1]);
|
kwsysProcessChildErrorExit(si->ErrorPipe[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A child has been created. */
|
|
||||||
++cp->CommandsLeft;
|
|
||||||
|
|
||||||
/* We are done with the error reporting pipe write end. */
|
/* We are done with the error reporting pipe write end. */
|
||||||
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
||||||
|
|
||||||
@ -1420,47 +1424,6 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
static void kwsysProcessDestroy(kwsysProcess* cp)
|
|
||||||
{
|
|
||||||
/* A child process has terminated. Reap it if it is one handled by
|
|
||||||
this object. */
|
|
||||||
int i;
|
|
||||||
for(i=0; i < cp->NumberOfCommands; ++i)
|
|
||||||
{
|
|
||||||
if(cp->ForkPIDs[i])
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
while(((result = waitpid(cp->ForkPIDs[i],
|
|
||||||
&cp->CommandExitCodes[i], WNOHANG)) < 0) &&
|
|
||||||
(errno == EINTR));
|
|
||||||
if(result > 0)
|
|
||||||
{
|
|
||||||
/* This child has termianted. */
|
|
||||||
cp->ForkPIDs[i] = 0;
|
|
||||||
if(--cp->CommandsLeft == 0)
|
|
||||||
{
|
|
||||||
/* All children have terminated. Close the signal pipe
|
|
||||||
write end so that no more notifications are sent to this
|
|
||||||
object. */
|
|
||||||
kwsysProcessCleanupDescriptor(&cp->SignalPipe);
|
|
||||||
|
|
||||||
/* TODO: Once the children have terminated, switch
|
|
||||||
WaitForData to use a non-blocking read to get the
|
|
||||||
rest of the data from the pipe. This is needed when
|
|
||||||
grandchildren keep the output pipes open. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
static int kwsysProcessSetupOutputPipeFile(int* p, const char* name)
|
static int kwsysProcessSetupOutputPipeFile(int* p, const char* name)
|
||||||
{
|
{
|
||||||
@ -2051,196 +2014,3 @@ static void kwsysProcessKill(pid_t process_id)
|
|||||||
/* Kill the process. */
|
/* Kill the process. */
|
||||||
kill(process_id, SIGKILL);
|
kill(process_id, SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
/* Global set of executing processes for use by the signal handler.
|
|
||||||
This global instance will be zero-initialized by the compiler. */
|
|
||||||
typedef struct kwsysProcessInstances_s
|
|
||||||
{
|
|
||||||
int Count;
|
|
||||||
int Size;
|
|
||||||
kwsysProcess** Processes;
|
|
||||||
} kwsysProcessInstances;
|
|
||||||
static kwsysProcessInstances kwsysProcesses;
|
|
||||||
|
|
||||||
/* The old SIGCHLD handler. */
|
|
||||||
static struct sigaction kwsysProcessesOldSigChldAction;
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses)
|
|
||||||
{
|
|
||||||
/* Block SIGCHLD while we update the set of pipes to check.
|
|
||||||
TODO: sigprocmask is undefined for threaded apps. See
|
|
||||||
pthread_sigmask. */
|
|
||||||
sigset_t newset;
|
|
||||||
sigset_t oldset;
|
|
||||||
sigemptyset(&newset);
|
|
||||||
sigaddset(&newset, SIGCHLD);
|
|
||||||
sigprocmask(SIG_BLOCK, &newset, &oldset);
|
|
||||||
|
|
||||||
/* Store the new set in that seen by the signal handler. */
|
|
||||||
kwsysProcesses = *newProcesses;
|
|
||||||
|
|
||||||
/* Restore the signal mask to the previous setting. */
|
|
||||||
sigprocmask(SIG_SETMASK, &oldset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
static int kwsysProcessesAdd(kwsysProcess* cp)
|
|
||||||
{
|
|
||||||
/* Create a pipe through which the signal handler can notify the
|
|
||||||
given process object that a child has exited. */
|
|
||||||
{
|
|
||||||
/* Create the pipe. */
|
|
||||||
int oldfl[2];
|
|
||||||
int p[2];
|
|
||||||
if(pipe(p) < 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store the pipes now to be sure they are cleaned up later. */
|
|
||||||
cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL] = p[0];
|
|
||||||
cp->SignalPipe = p[1];
|
|
||||||
|
|
||||||
/* Switch the pipe to non-blocking mode so that reading a byte can
|
|
||||||
be an atomic test-and-set. */
|
|
||||||
if((oldfl[0] = fcntl(p[0], F_GETFL) < 0) ||
|
|
||||||
(oldfl[1] = fcntl(p[1], F_GETFL) < 0) ||
|
|
||||||
(fcntl(p[0], F_SETFL, oldfl[0] | O_NONBLOCK) < 0) ||
|
|
||||||
(fcntl(p[1], F_SETFL, oldfl[1] | O_NONBLOCK) < 0))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The children do not need this pipe. 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attempt to add the given signal pipe to the signal handler set. */
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Make sure there is enough space for the new signal pipe. */
|
|
||||||
kwsysProcessInstances oldProcesses = kwsysProcesses;
|
|
||||||
kwsysProcessInstances newProcesses = oldProcesses;
|
|
||||||
if(oldProcesses.Count == oldProcesses.Size)
|
|
||||||
{
|
|
||||||
/* Start with enough space for a small number of process instances
|
|
||||||
and double the size each time more is needed. */
|
|
||||||
newProcesses.Size = oldProcesses.Size? oldProcesses.Size*2 : 4;
|
|
||||||
|
|
||||||
/* Try allocating the new block of memory. */
|
|
||||||
if((newProcesses.Processes = ((kwsysProcess**)
|
|
||||||
malloc(newProcesses.Size*
|
|
||||||
sizeof(kwsysProcess*)))))
|
|
||||||
{
|
|
||||||
/* Copy the old pipe set to the new memory. */
|
|
||||||
if(oldProcesses.Count > 0)
|
|
||||||
{
|
|
||||||
memcpy(newProcesses.Processes, oldProcesses.Processes,
|
|
||||||
(oldProcesses.Count * sizeof(kwsysProcess*)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Failed to allocate memory for the new signal pipe set. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Append the new signal pipe to the set. */
|
|
||||||
newProcesses.Processes[newProcesses.Count++] = cp;
|
|
||||||
|
|
||||||
/* Store the new set in that seen by the signal handler. */
|
|
||||||
kwsysProcessesUpdate(&newProcesses);
|
|
||||||
|
|
||||||
/* Free the original pipes if new ones were allocated. */
|
|
||||||
if(newProcesses.Processes != oldProcesses.Processes)
|
|
||||||
{
|
|
||||||
free(oldProcesses.Processes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this is the first process, enable the signal handler. */
|
|
||||||
if(newProcesses.Count == 1)
|
|
||||||
{
|
|
||||||
/* Install our handler for SIGCHLD. Repeat call until it is not
|
|
||||||
interrupted. */
|
|
||||||
struct sigaction newSigChldAction;
|
|
||||||
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
|
||||||
newSigChldAction.sa_sigaction = kwsysProcessesSignalHandler;
|
|
||||||
newSigChldAction.sa_flags = SA_NOCLDSTOP | SA_RESTART | SA_SIGINFO;
|
|
||||||
while((sigaction(SIGCHLD, &newSigChldAction,
|
|
||||||
&kwsysProcessesOldSigChldAction) < 0) &&
|
|
||||||
(errno == EINTR));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
static void kwsysProcessesRemove(kwsysProcess* cp)
|
|
||||||
{
|
|
||||||
/* Attempt to remove the given signal pipe from the signal handler set. */
|
|
||||||
{
|
|
||||||
/* Find the given process in the set. */
|
|
||||||
kwsysProcessInstances newProcesses = kwsysProcesses;
|
|
||||||
int i;
|
|
||||||
for(i=0; i < newProcesses.Count; ++i)
|
|
||||||
{
|
|
||||||
if(newProcesses.Processes[i] == cp)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(i < newProcesses.Count)
|
|
||||||
{
|
|
||||||
/* Remove the process from the set. */
|
|
||||||
--newProcesses.Count;
|
|
||||||
for(; i < newProcesses.Count; ++i)
|
|
||||||
{
|
|
||||||
newProcesses.Processes[i] = newProcesses.Processes[i+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store the new set in that seen by the signal handler. */
|
|
||||||
kwsysProcessesUpdate(&newProcesses);
|
|
||||||
|
|
||||||
/* If this was the last process, disable the signal handler. */
|
|
||||||
if(newProcesses.Count == 0)
|
|
||||||
{
|
|
||||||
/* Restore the SIGCHLD handler. Repeat call until it is not
|
|
||||||
interrupted. */
|
|
||||||
while((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) &&
|
|
||||||
(errno == EINTR));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the pipe through which the signal handler may have notified
|
|
||||||
the given process object that a child has exited. */
|
|
||||||
kwsysProcessCleanupDescriptor(&cp->SignalPipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
static void kwsysProcessesSignalHandler(int signum, siginfo_t* info,
|
|
||||||
void* ucontext)
|
|
||||||
{
|
|
||||||
/* Signal all process objects that a child has terminated. */
|
|
||||||
int i;
|
|
||||||
(void)signum;
|
|
||||||
(void)info;
|
|
||||||
(void)ucontext;
|
|
||||||
for(i=0; i < kwsysProcesses.Count; ++i)
|
|
||||||
{
|
|
||||||
/* Set the pipe in a signalled state. */
|
|
||||||
char buf = 1;
|
|
||||||
kwsysProcess* cp = kwsysProcesses.Processes[i];
|
|
||||||
read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
|
|
||||||
write(cp->SignalPipe, &buf, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user