ENH: Changes based on patch from Ryan C. Gordon to enable process execution on BeOS. There seems to be no way to implement it without polling (or threads).
This commit is contained in:
parent
d045ae45f2
commit
de8ffcaef4
@ -75,6 +75,36 @@ typedef ssize_t kwsysProcess_ssize_t;
|
|||||||
typedef int kwsysProcess_ssize_t;
|
typedef int kwsysProcess_ssize_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__BEOS__) && !defined(__ZETA__)
|
||||||
|
/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
|
||||||
|
# include <be/kernel/OS.h>
|
||||||
|
static inline void kwsysProcess_usleep(unsigned int msec)
|
||||||
|
{
|
||||||
|
snooze(msec);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define kwsysProcess_usleep usleep
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BeOS's select() works like WinSock: it's for networking only, and
|
||||||
|
* doesn't work with Unix file handles...socket and file handles are
|
||||||
|
* different namespaces (the same descriptor means different things in
|
||||||
|
* each context!)
|
||||||
|
*
|
||||||
|
* So on Unix-like systems where select() is flakey, we'll set the
|
||||||
|
* pipes' file handles to be non-blocking and just poll them directly
|
||||||
|
* without select().
|
||||||
|
*/
|
||||||
|
#if !defined(__BEOS__)
|
||||||
|
# define KWSYSPE_USE_SELECT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* BeOS does not have siginfo on its signal handlers. */
|
||||||
|
#if !defined(__BEOS__)
|
||||||
|
# define KWSYSPE_USE_SIGINFO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/* The number of pipes for the child's output. The standard stdout
|
/* The number of pipes for the child's output. The standard stdout
|
||||||
and stderr pipes are the first two. One more pipe is used to
|
and stderr pipes are the first two. One more pipe is used to
|
||||||
detect when the child process has terminated. The third pipe is
|
detect when the child process has terminated. The third pipe is
|
||||||
@ -111,6 +141,7 @@ typedef struct kwsysProcessCreateInformation_s
|
|||||||
static int kwsysProcessInitialize(kwsysProcess* cp);
|
static int kwsysProcessInitialize(kwsysProcess* cp);
|
||||||
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
|
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
|
||||||
static void kwsysProcessCleanupDescriptor(int* pfd);
|
static void kwsysProcessCleanupDescriptor(int* pfd);
|
||||||
|
static int kwsysProcessSetNonBlocking(int fd);
|
||||||
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 void kwsysProcessDestroy(kwsysProcess* cp);
|
||||||
@ -135,8 +166,12 @@ static pid_t kwsysProcessFork(kwsysProcess* cp,
|
|||||||
static void kwsysProcessKill(pid_t process_id);
|
static void kwsysProcessKill(pid_t process_id);
|
||||||
static int kwsysProcessesAdd(kwsysProcess* cp);
|
static int kwsysProcessesAdd(kwsysProcess* cp);
|
||||||
static void kwsysProcessesRemove(kwsysProcess* cp);
|
static void kwsysProcessesRemove(kwsysProcess* cp);
|
||||||
|
#if KWSYSPE_USE_SIGINFO
|
||||||
static void kwsysProcessesSignalHandler(int signum, siginfo_t* info,
|
static void kwsysProcessesSignalHandler(int signum, siginfo_t* info,
|
||||||
void* ucontext);
|
void* ucontext);
|
||||||
|
#else
|
||||||
|
static void kwsysProcessesSignalHandler(int signum);
|
||||||
|
#endif
|
||||||
static char** kwsysProcessParseVerbatimCommand(const char* command);
|
static char** kwsysProcessParseVerbatimCommand(const char* command);
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
@ -190,8 +225,10 @@ struct kwsysProcess_s
|
|||||||
/* The number of pipes left open during execution. */
|
/* The number of pipes left open during execution. */
|
||||||
int PipesLeft;
|
int PipesLeft;
|
||||||
|
|
||||||
|
#if KWSYSPE_USE_SELECT
|
||||||
/* File descriptor set for call to select. */
|
/* File descriptor set for call to select. */
|
||||||
fd_set PipeSet;
|
fd_set PipeSet;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* The number of children still executing. */
|
/* The number of children still executing. */
|
||||||
int CommandsLeft;
|
int CommandsLeft;
|
||||||
@ -731,6 +768,15 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
|||||||
kwsysProcessCleanupDescriptor(&si.StdErr);
|
kwsysProcessCleanupDescriptor(&si.StdErr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !KWSYSPE_USE_SELECT
|
||||||
|
if(!kwsysProcessSetNonBlocking(p[0]))
|
||||||
|
{
|
||||||
|
kwsysProcessCleanup(cp, 1);
|
||||||
|
kwsysProcessCleanupDescriptor(&si.StdErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace the stderr pipe with a file if requested. In this case
|
/* Replace the stderr pipe with a file if requested. In this case
|
||||||
@ -775,9 +821,24 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
|||||||
/* Create the pipeline of processes. */
|
/* Create the pipeline of processes. */
|
||||||
{
|
{
|
||||||
int readEnd = -1;
|
int readEnd = -1;
|
||||||
|
int failed = 0;
|
||||||
for(i=0; i < cp->NumberOfCommands; ++i)
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
{
|
{
|
||||||
if(!kwsysProcessCreate(cp, i, &si, &readEnd))
|
if(!kwsysProcessCreate(cp, i, &si, &readEnd))
|
||||||
|
{
|
||||||
|
failed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !KWSYSPE_USE_SELECT
|
||||||
|
/* Set the output pipe of the last process to be non-blocking so
|
||||||
|
we can poll it. */
|
||||||
|
if(i == cp->NumberOfCommands-1 && !kwsysProcessSetNonBlocking(readEnd))
|
||||||
|
{
|
||||||
|
failed = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(failed)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, 1);
|
||||||
|
|
||||||
@ -846,8 +907,11 @@ kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
|
|||||||
{
|
{
|
||||||
if(cp->PipeReadEnds[i] >= 0)
|
if(cp->PipeReadEnds[i] >= 0)
|
||||||
{
|
{
|
||||||
|
#if KWSYSPE_USE_SELECT
|
||||||
/* If the pipe was reported by the last call to select, we must
|
/* If the pipe was reported by the last call to select, we must
|
||||||
read from it. Ignore the data. */
|
read from it. This is needed to satisfy the suggestions from
|
||||||
|
"man select_tut" and is not needed for the polling
|
||||||
|
implementation. Ignore the data. */
|
||||||
if(FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
|
if(FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
|
||||||
{
|
{
|
||||||
/* We are handling this pipe now. Remove it from the set. */
|
/* We are handling this pipe now. Remove it from the set. */
|
||||||
@ -858,6 +922,7 @@ kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
|
|||||||
while((read(cp->PipeReadEnds[i], cp->PipeBuffer,
|
while((read(cp->PipeReadEnds[i], cp->PipeBuffer,
|
||||||
KWSYSPE_PIPE_BUFFER_SIZE) < 0) && (errno == EINTR));
|
KWSYSPE_PIPE_BUFFER_SIZE) < 0) && (errno == EINTR));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* We are done reading from this pipe. */
|
/* We are done reading from this pipe. */
|
||||||
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
|
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
|
||||||
@ -872,20 +937,31 @@ kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
|
|||||||
cp->State = kwsysProcess_State_Disowned;
|
cp->State = kwsysProcess_State_Disowned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
typedef struct kwsysProcessWaitData_s
|
||||||
|
{
|
||||||
|
int Expired;
|
||||||
|
int PipeId;
|
||||||
|
int User;
|
||||||
|
double* UserTimeout;
|
||||||
|
kwsysProcessTime TimeoutTime;
|
||||||
|
} kwsysProcessWaitData;
|
||||||
|
static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length,
|
||||||
|
kwsysProcessWaitData* wd);
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
||||||
double* userTimeout)
|
double* userTimeout)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
int max = -1;
|
|
||||||
kwsysProcessTimeNative* timeout = 0;
|
|
||||||
kwsysProcessTimeNative timeoutLength;
|
|
||||||
kwsysProcessTime timeoutTime;
|
|
||||||
kwsysProcessTime userStartTime = {0, 0};
|
kwsysProcessTime userStartTime = {0, 0};
|
||||||
int user = 0;
|
kwsysProcessWaitData wd =
|
||||||
int expired = 0;
|
{
|
||||||
int pipeId = kwsysProcess_Pipe_None;
|
0,
|
||||||
int numReady = 0;
|
kwsysProcess_Pipe_None,
|
||||||
|
0,
|
||||||
|
userTimeout,
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
/* Make sure we are executing a process. */
|
/* Make sure we are executing a process. */
|
||||||
if(!cp || cp->State != kwsysProcess_State_Executing || cp->Killed ||
|
if(!cp || cp->State != kwsysProcess_State_Executing || cp->Killed ||
|
||||||
@ -902,140 +978,20 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
|||||||
|
|
||||||
/* Calculate the time at which a timeout will expire, and whether it
|
/* Calculate the time at which a timeout will expire, and whether it
|
||||||
is the user or process timeout. */
|
is the user or process timeout. */
|
||||||
user = kwsysProcessGetTimeoutTime(cp, userTimeout, &timeoutTime);
|
wd.User = kwsysProcessGetTimeoutTime(cp, userTimeout,
|
||||||
|
&wd.TimeoutTime);
|
||||||
|
|
||||||
/* Data can only be available when pipes are open. If the process
|
/* Data can only be available when pipes are open. If the process
|
||||||
is not running, cp->PipesLeft will be 0. */
|
is not running, cp->PipesLeft will be 0. */
|
||||||
while(cp->PipesLeft > 0)
|
while(cp->PipesLeft > 0 &&
|
||||||
{
|
!kwsysProcessWaitForPipe(cp, data, length, &wd)) {}
|
||||||
/* Check for any open pipes with data reported ready by the last
|
|
||||||
call to select. According to "man select_tut" we must deal
|
|
||||||
with all descriptors reported by a call to select before
|
|
||||||
passing them to another select call. */
|
|
||||||
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
|
||||||
{
|
|
||||||
if(cp->PipeReadEnds[i] >= 0 &&
|
|
||||||
FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
|
|
||||||
{
|
|
||||||
kwsysProcess_ssize_t n;
|
|
||||||
|
|
||||||
/* We are handling this pipe now. Remove it from the set. */
|
|
||||||
FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet);
|
|
||||||
|
|
||||||
/* The pipe is ready to read without blocking. Keep trying to
|
|
||||||
read until the operation is not interrupted. */
|
|
||||||
while(((n = read(cp->PipeReadEnds[i], cp->PipeBuffer,
|
|
||||||
KWSYSPE_PIPE_BUFFER_SIZE)) < 0) && (errno == EINTR));
|
|
||||||
if(n > 0)
|
|
||||||
{
|
|
||||||
/* We have data on this pipe. */
|
|
||||||
if(i == KWSYSPE_PIPE_SIGNAL)
|
|
||||||
{
|
|
||||||
/* A child process has terminated. */
|
|
||||||
kwsysProcessDestroy(cp);
|
|
||||||
}
|
|
||||||
else if(data && length)
|
|
||||||
{
|
|
||||||
/* Report this data. */
|
|
||||||
*data = cp->PipeBuffer;
|
|
||||||
*length = n;
|
|
||||||
switch(i)
|
|
||||||
{
|
|
||||||
case KWSYSPE_PIPE_STDOUT:
|
|
||||||
pipeId = kwsysProcess_Pipe_STDOUT; break;
|
|
||||||
case KWSYSPE_PIPE_STDERR:
|
|
||||||
pipeId = kwsysProcess_Pipe_STDERR; break;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* We are done reading from this pipe. */
|
|
||||||
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
|
|
||||||
--cp->PipesLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have data, break early. */
|
|
||||||
if(pipeId)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure the set is empty (it should always be empty here
|
|
||||||
anyway). */
|
|
||||||
FD_ZERO(&cp->PipeSet);
|
|
||||||
|
|
||||||
/* Setup a timeout if required. */
|
|
||||||
if(timeoutTime.tv_sec < 0)
|
|
||||||
{
|
|
||||||
timeout = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
timeout = &timeoutLength;
|
|
||||||
}
|
|
||||||
if(kwsysProcessGetTimeoutLeft(&timeoutTime, user?userTimeout:0, &timeoutLength))
|
|
||||||
{
|
|
||||||
/* Timeout has already expired. */
|
|
||||||
expired = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add the pipe reading ends that are still open. */
|
|
||||||
max = -1;
|
|
||||||
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
|
||||||
{
|
|
||||||
if(cp->PipeReadEnds[i] >= 0)
|
|
||||||
{
|
|
||||||
FD_SET(cp->PipeReadEnds[i], &cp->PipeSet);
|
|
||||||
if(cp->PipeReadEnds[i] > max)
|
|
||||||
{
|
|
||||||
max = cp->PipeReadEnds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure we have a non-empty set. */
|
|
||||||
if(max < 0)
|
|
||||||
{
|
|
||||||
/* All pipes have closed. Child has terminated. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run select to block until data are available. Repeat call
|
|
||||||
until it is not interrupted. */
|
|
||||||
while(((numReady = select(max+1, &cp->PipeSet, 0, 0, timeout)) < 0) &&
|
|
||||||
(errno == EINTR));
|
|
||||||
|
|
||||||
/* Check result of select. */
|
|
||||||
if(numReady == 0)
|
|
||||||
{
|
|
||||||
/* Select's timeout expired. */
|
|
||||||
expired = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(numReady < 0)
|
|
||||||
{
|
|
||||||
/* Select returned an error. Leave the error description in the
|
|
||||||
pipe buffer. */
|
|
||||||
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
|
|
||||||
|
|
||||||
/* Kill the children now. */
|
|
||||||
kwsysProcess_Kill(cp);
|
|
||||||
cp->Killed = 0;
|
|
||||||
cp->SelectError = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the user timeout. */
|
/* Update the user timeout. */
|
||||||
if(userTimeout)
|
if(userTimeout)
|
||||||
{
|
{
|
||||||
kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent();
|
kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent();
|
||||||
kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime,
|
kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime,
|
||||||
userStartTime);
|
userStartTime);
|
||||||
double d = kwsysProcessTimeToDouble(difference);
|
double d = kwsysProcessTimeToDouble(difference);
|
||||||
*userTimeout -= d;
|
*userTimeout -= d;
|
||||||
if(*userTimeout < 0)
|
if(*userTimeout < 0)
|
||||||
@ -1045,15 +1001,15 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check what happened. */
|
/* Check what happened. */
|
||||||
if(pipeId)
|
if(wd.PipeId)
|
||||||
{
|
{
|
||||||
/* Data are ready on a pipe. */
|
/* Data are ready on a pipe. */
|
||||||
return pipeId;
|
return wd.PipeId;
|
||||||
}
|
}
|
||||||
else if(expired)
|
else if(wd.Expired)
|
||||||
{
|
{
|
||||||
/* A timeout has expired. */
|
/* A timeout has expired. */
|
||||||
if(user)
|
if(wd.User)
|
||||||
{
|
{
|
||||||
/* The user timeout has expired. It has no time left. */
|
/* The user timeout has expired. It has no time left. */
|
||||||
return kwsysProcess_Pipe_Timeout;
|
return kwsysProcess_Pipe_Timeout;
|
||||||
@ -1074,6 +1030,230 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length,
|
||||||
|
kwsysProcessWaitData* wd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
kwsysProcessTimeNative timeoutLength;
|
||||||
|
|
||||||
|
#if KWSYSPE_USE_SELECT
|
||||||
|
int numReady = 0;
|
||||||
|
int max = -1;
|
||||||
|
kwsysProcessTimeNative* timeout = 0;
|
||||||
|
|
||||||
|
/* Check for any open pipes with data reported ready by the last
|
||||||
|
call to select. According to "man select_tut" we must deal
|
||||||
|
with all descriptors reported by a call to select before
|
||||||
|
passing them to another select call. */
|
||||||
|
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
if(cp->PipeReadEnds[i] >= 0 &&
|
||||||
|
FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
|
||||||
|
{
|
||||||
|
kwsysProcess_ssize_t n;
|
||||||
|
|
||||||
|
/* We are handling this pipe now. Remove it from the set. */
|
||||||
|
FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet);
|
||||||
|
|
||||||
|
/* The pipe is ready to read without blocking. Keep trying to
|
||||||
|
read until the operation is not interrupted. */
|
||||||
|
while(((n = read(cp->PipeReadEnds[i], cp->PipeBuffer,
|
||||||
|
KWSYSPE_PIPE_BUFFER_SIZE)) < 0) && (errno == EINTR));
|
||||||
|
if(n > 0)
|
||||||
|
{
|
||||||
|
/* We have data on this pipe. */
|
||||||
|
if(i == KWSYSPE_PIPE_SIGNAL)
|
||||||
|
{
|
||||||
|
/* A child process has terminated. */
|
||||||
|
kwsysProcessDestroy(cp);
|
||||||
|
}
|
||||||
|
else if(data && length)
|
||||||
|
{
|
||||||
|
/* Report this data. */
|
||||||
|
*data = cp->PipeBuffer;
|
||||||
|
*length = n;
|
||||||
|
switch(i)
|
||||||
|
{
|
||||||
|
case KWSYSPE_PIPE_STDOUT:
|
||||||
|
wd->PipeId = kwsysProcess_Pipe_STDOUT; break;
|
||||||
|
case KWSYSPE_PIPE_STDERR:
|
||||||
|
wd->PipeId = kwsysProcess_Pipe_STDERR; break;
|
||||||
|
};
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We are done reading from this pipe. */
|
||||||
|
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
|
||||||
|
--cp->PipesLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have data, break early. */
|
||||||
|
if(wd->PipeId)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the set is empty (it should always be empty here
|
||||||
|
anyway). */
|
||||||
|
FD_ZERO(&cp->PipeSet);
|
||||||
|
|
||||||
|
/* Setup a timeout if required. */
|
||||||
|
if(wd->TimeoutTime.tv_sec < 0)
|
||||||
|
{
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timeout = &timeoutLength;
|
||||||
|
}
|
||||||
|
if(kwsysProcessGetTimeoutLeft(&wd->TimeoutTime,
|
||||||
|
wd->User?wd->UserTimeout:0,
|
||||||
|
&timeoutLength))
|
||||||
|
{
|
||||||
|
/* Timeout has already expired. */
|
||||||
|
wd->Expired = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the pipe reading ends that are still open. */
|
||||||
|
max = -1;
|
||||||
|
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
if(cp->PipeReadEnds[i] >= 0)
|
||||||
|
{
|
||||||
|
FD_SET(cp->PipeReadEnds[i], &cp->PipeSet);
|
||||||
|
if(cp->PipeReadEnds[i] > max)
|
||||||
|
{
|
||||||
|
max = cp->PipeReadEnds[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we have a non-empty set. */
|
||||||
|
if(max < 0)
|
||||||
|
{
|
||||||
|
/* All pipes have closed. Child has terminated. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run select to block until data are available. Repeat call
|
||||||
|
until it is not interrupted. */
|
||||||
|
while(((numReady = select(max+1, &cp->PipeSet, 0, 0, timeout)) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
|
||||||
|
/* Check result of select. */
|
||||||
|
if(numReady == 0)
|
||||||
|
{
|
||||||
|
/* Select's timeout expired. */
|
||||||
|
wd->Expired = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if(numReady < 0)
|
||||||
|
{
|
||||||
|
/* Select returned an error. Leave the error description in the
|
||||||
|
pipe buffer. */
|
||||||
|
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
|
||||||
|
|
||||||
|
/* Kill the children now. */
|
||||||
|
kwsysProcess_Kill(cp);
|
||||||
|
cp->Killed = 0;
|
||||||
|
cp->SelectError = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
/* Poll pipes for data since we do not have select. */
|
||||||
|
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
||||||
|
{
|
||||||
|
if(cp->PipeReadEnds[i] >= 0)
|
||||||
|
{
|
||||||
|
const int fd = cp->PipeReadEnds[i];
|
||||||
|
int n = read(fd, cp->PipeBuffer, KWSYSPE_PIPE_BUFFER_SIZE);
|
||||||
|
if(n > 0)
|
||||||
|
{
|
||||||
|
/* We have data on this pipe. */
|
||||||
|
if(i == KWSYSPE_PIPE_SIGNAL)
|
||||||
|
{
|
||||||
|
/* A child process has terminated. */
|
||||||
|
kwsysProcessDestroy(cp);
|
||||||
|
}
|
||||||
|
else if(data && length)
|
||||||
|
{
|
||||||
|
/* Report this data. */
|
||||||
|
*data = cp->PipeBuffer;
|
||||||
|
*length = n;
|
||||||
|
switch(i)
|
||||||
|
{
|
||||||
|
case KWSYSPE_PIPE_STDOUT:
|
||||||
|
wd->PipeId = kwsysProcess_Pipe_STDOUT; break;
|
||||||
|
case KWSYSPE_PIPE_STDERR:
|
||||||
|
wd->PipeId = kwsysProcess_Pipe_STDERR; break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (n == 0) /* EOF */
|
||||||
|
{
|
||||||
|
/* We are done reading from this pipe. */
|
||||||
|
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
|
||||||
|
--cp->PipesLeft;
|
||||||
|
}
|
||||||
|
else if (n < 0) /* error */
|
||||||
|
{
|
||||||
|
if((errno != EINTR) && (errno != EAGAIN))
|
||||||
|
{
|
||||||
|
strncpy(cp->ErrorMessage,strerror(errno),
|
||||||
|
KWSYSPE_PIPE_BUFFER_SIZE);
|
||||||
|
/* Kill the children now. */
|
||||||
|
kwsysProcess_Kill(cp);
|
||||||
|
cp->Killed = 0;
|
||||||
|
cp->SelectError = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have data, break early. */
|
||||||
|
if(wd->PipeId)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kwsysProcessGetTimeoutLeft(&wd->TimeoutTime, wd->User?wd->UserTimeout:0,
|
||||||
|
&timeoutLength))
|
||||||
|
{
|
||||||
|
/* Timeout has already expired. */
|
||||||
|
wd->Expired = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((timeoutLength.tv_sec == 0) && (timeoutLength.tv_usec == 0))
|
||||||
|
{
|
||||||
|
/* Timeout has already expired. */
|
||||||
|
wd->Expired = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sleep a little, try again. */
|
||||||
|
{
|
||||||
|
unsigned int msec = ((timeoutLength.tv_sec * 1000) +
|
||||||
|
(timeoutLength.tv_usec / 1000));
|
||||||
|
if (msec > 100000)
|
||||||
|
{
|
||||||
|
msec = 100000; /* do not sleep more than 100 milliseconds at a time */
|
||||||
|
}
|
||||||
|
kwsysProcess_usleep(msec);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
||||||
{
|
{
|
||||||
@ -1215,7 +1395,9 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
|
|||||||
cp->TimeoutExpired = 0;
|
cp->TimeoutExpired = 0;
|
||||||
cp->PipesLeft = 0;
|
cp->PipesLeft = 0;
|
||||||
cp->CommandsLeft = 0;
|
cp->CommandsLeft = 0;
|
||||||
|
#if KWSYSPE_USE_SELECT
|
||||||
FD_ZERO(&cp->PipeSet);
|
FD_ZERO(&cp->PipeSet);
|
||||||
|
#endif
|
||||||
cp->State = kwsysProcess_State_Starting;
|
cp->State = kwsysProcess_State_Starting;
|
||||||
cp->Killed = 0;
|
cp->Killed = 0;
|
||||||
cp->ExitException = kwsysProcess_Exception_None;
|
cp->ExitException = kwsysProcess_Exception_None;
|
||||||
@ -1351,6 +1533,17 @@ static void kwsysProcessCleanupDescriptor(int* pfd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
static int kwsysProcessSetNonBlocking(int fd)
|
||||||
|
{
|
||||||
|
int flags = fcntl(fd, F_GETFL);
|
||||||
|
if(flags >= 0)
|
||||||
|
{
|
||||||
|
flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
return flags >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
kwsysProcessCreateInformation* si, int* readEnd)
|
kwsysProcessCreateInformation* si, int* readEnd)
|
||||||
@ -2260,10 +2453,8 @@ static int kwsysProcessesAdd(kwsysProcess* cp)
|
|||||||
|
|
||||||
/* Switch the pipe to non-blocking mode so that reading a byte can
|
/* Switch the pipe to non-blocking mode so that reading a byte can
|
||||||
be an atomic test-and-set. */
|
be an atomic test-and-set. */
|
||||||
if((oldfl[0] = fcntl(p[0], F_GETFL) < 0) ||
|
if(!kwsysProcessSetNonBlocking(p[0]) ||
|
||||||
(oldfl[1] = fcntl(p[1], F_GETFL) < 0) ||
|
!kwsysProcessSetNonBlocking(p[1]))
|
||||||
(fcntl(p[0], F_SETFL, oldfl[0] | O_NONBLOCK) < 0) ||
|
|
||||||
(fcntl(p[1], F_SETFL, oldfl[1] | O_NONBLOCK) < 0))
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2327,10 +2518,15 @@ static int kwsysProcessesAdd(kwsysProcess* cp)
|
|||||||
interrupted. */
|
interrupted. */
|
||||||
struct sigaction newSigChldAction;
|
struct sigaction newSigChldAction;
|
||||||
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
||||||
|
#if KWSYSPE_USE_SIGINFO
|
||||||
newSigChldAction.sa_sigaction = kwsysProcessesSignalHandler;
|
newSigChldAction.sa_sigaction = kwsysProcessesSignalHandler;
|
||||||
newSigChldAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
newSigChldAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
||||||
#ifdef SA_RESTART
|
# ifdef SA_RESTART
|
||||||
newSigChldAction.sa_flags |= SA_RESTART;
|
newSigChldAction.sa_flags |= SA_RESTART;
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
|
||||||
|
newSigChldAction.sa_flags = SA_NOCLDSTOP;
|
||||||
#endif
|
#endif
|
||||||
while((sigaction(SIGCHLD, &newSigChldAction,
|
while((sigaction(SIGCHLD, &newSigChldAction,
|
||||||
&kwsysProcessesOldSigChldAction) < 0) &&
|
&kwsysProcessesOldSigChldAction) < 0) &&
|
||||||
@ -2391,14 +2587,21 @@ static void kwsysProcessesRemove(kwsysProcess* cp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
static void kwsysProcessesSignalHandler(int signum, siginfo_t* info,
|
static void kwsysProcessesSignalHandler(int signum
|
||||||
void* ucontext)
|
#if KWSYSPE_USE_SIGINFO
|
||||||
|
, siginfo_t* info, void* ucontext
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
/* Signal all process objects that a child has terminated. */
|
|
||||||
int i;
|
|
||||||
(void)signum;
|
(void)signum;
|
||||||
|
#if KWSYSPE_USE_SIGINFO
|
||||||
(void)info;
|
(void)info;
|
||||||
(void)ucontext;
|
(void)ucontext;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Signal all process objects that a child has terminated. */
|
||||||
|
{
|
||||||
|
int i;
|
||||||
for(i=0; i < kwsysProcesses.Count; ++i)
|
for(i=0; i < kwsysProcesses.Count; ++i)
|
||||||
{
|
{
|
||||||
/* Set the pipe in a signalled state. */
|
/* Set the pipe in a signalled state. */
|
||||||
@ -2407,6 +2610,21 @@ static void kwsysProcessesSignalHandler(int signum, siginfo_t* info,
|
|||||||
read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
|
read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
|
||||||
write(cp->SignalPipe, &buf, 1);
|
write(cp->SignalPipe, &buf, 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !KWSYSPE_USE_SIGINFO
|
||||||
|
/* Re-Install our handler for SIGCHLD. Repeat call until it is not
|
||||||
|
interrupted. */
|
||||||
|
{
|
||||||
|
struct sigaction newSigChldAction;
|
||||||
|
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
||||||
|
newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
|
||||||
|
newSigChldAction.sa_flags = SA_NOCLDSTOP;
|
||||||
|
while((sigaction(SIGCHLD, &newSigChldAction,
|
||||||
|
&kwsysProcessesOldSigChldAction) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
@ -2661,3 +2879,4 @@ static char** kwsysProcessParseVerbatimCommand(const char* command)
|
|||||||
/* Return the final command buffer. */
|
/* Return the final command buffer. */
|
||||||
return newCommand;
|
return newCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,17 @@
|
|||||||
# pragma warn -8060 /* possibly incorrect assignment */
|
# pragma warn -8060 /* possibly incorrect assignment */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__BEOS__) && !defined(__ZETA__)
|
||||||
|
/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
|
||||||
|
# include <be/kernel/OS.h>
|
||||||
|
static inline void testProcess_usleep(unsigned int msec)
|
||||||
|
{
|
||||||
|
snooze(msec);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define testProcess_usleep usleep
|
||||||
|
#endif
|
||||||
|
|
||||||
int runChild(const char* cmd[], int state, int exception, int value,
|
int runChild(const char* cmd[], int state, int exception, int value,
|
||||||
int share, int output, int delay, double timeout, int poll,
|
int share, int output, int delay, double timeout, int poll,
|
||||||
int repeat, int disown);
|
int repeat, int disown);
|
||||||
@ -76,6 +87,9 @@ int test4(int argc, const char* argv[])
|
|||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
/* Avoid error diagnostic popups since we are crashing on purpose. */
|
/* Avoid error diagnostic popups since we are crashing on purpose. */
|
||||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||||
|
#elif defined(__BEOS__)
|
||||||
|
/* Avoid error diagnostic popups since we are crashing on purpose. */
|
||||||
|
disable_debugger(1);
|
||||||
#endif
|
#endif
|
||||||
(void)argc; (void)argv;
|
(void)argc; (void)argv;
|
||||||
fprintf(stdout, "Output before crash on stdout from crash test.\n");
|
fprintf(stdout, "Output before crash on stdout from crash test.\n");
|
||||||
@ -264,7 +278,7 @@ int runChild2(kwsysProcess* kp,
|
|||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
Sleep(100);
|
Sleep(100);
|
||||||
#else
|
#else
|
||||||
usleep(100000);
|
testProcess_usleep(100000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if(delay)
|
if(delay)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user