Commit 45fbde69 authored by Brad King's avatar Brad King

ENH: Added kwsysProcess_Disown an kwsysProcess_Option_Detach to allow detached...

ENH: Added kwsysProcess_Disown an kwsysProcess_Option_Detach to allow detached processes to be created.  Currently implemented only on UNIX.
parent c2d7d285
......@@ -32,6 +32,7 @@
#define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory)
#define kwsysProcess_SetPipeFile kwsys_ns(Process_SetPipeFile)
#define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared)
#define kwsysProcess_Option_Detach kwsys_ns(Process_Option_Detach)
#define kwsysProcess_Option_HideWindow kwsys_ns(Process_Option_HideWindow)
#define kwsysProcess_GetOption kwsys_ns(Process_GetOption)
#define kwsysProcess_SetOption kwsys_ns(Process_SetOption)
......@@ -43,6 +44,7 @@
#define kwsysProcess_State_Exited kwsys_ns(Process_State_Exited)
#define kwsysProcess_State_Expired kwsys_ns(Process_State_Expired)
#define kwsysProcess_State_Killed kwsys_ns(Process_State_Killed)
#define kwsysProcess_State_Disowned kwsys_ns(Process_State_Disowned)
#define kwsysProcess_GetState kwsys_ns(Process_GetState)
#define kwsysProcess_State_e kwsys_ns(Process_State_e)
#define kwsysProcess_Exception_None kwsys_ns(Process_Exception_None)
......@@ -58,6 +60,7 @@
#define kwsysProcess_GetErrorString kwsys_ns(Process_GetErrorString)
#define kwsysProcess_GetExceptionString kwsys_ns(Process_GetExceptionString)
#define kwsysProcess_Execute kwsys_ns(Process_Execute)
#define kwsysProcess_Disown kwsys_ns(Process_Disown)
#define kwsysProcess_WaitForData kwsys_ns(Process_WaitForData)
#define kwsysProcess_Pipes_e kwsys_ns(Process_Pipes_e)
#define kwsysProcess_Pipe_None kwsys_ns(Process_Pipe_None)
......@@ -140,7 +143,11 @@ kwsysEXPORT void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe,
int shared);
/**
* Get/Set a platform-specific option. Possible options are:
* Get/Set a possibly platform-specific option. Possible options are:
*
* kwsysProcess_Option_Detach = Whether to detach the process.
* 0 = No (default)
* 1 = Yes
*
* kwsysProcess_Option_HideWindow = Whether to hide window on Windows.
* 0 = No (default)
......@@ -151,7 +158,8 @@ kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId,
int value);
enum kwsysProcess_Option_e
{
kwsysProcess_Option_HideWindow
kwsysProcess_Option_HideWindow,
kwsysProcess_Option_Detach
};
/**
......@@ -164,6 +172,7 @@ enum kwsysProcess_Option_e
* kwsysProcess_State_Exited = Child process exited normally.
* kwsysProcess_State_Expired = Child process's timeout expired.
* kwsysProcess_State_Killed = Child process terminated by Kill method.
* kwsysProcess_State_Disowned = Child is no longer managed by this object.
*/
kwsysEXPORT int kwsysProcess_GetState(kwsysProcess* cp);
enum kwsysProcess_State_e
......@@ -174,7 +183,8 @@ enum kwsysProcess_State_e
kwsysProcess_State_Executing,
kwsysProcess_State_Exited,
kwsysProcess_State_Expired,
kwsysProcess_State_Killed
kwsysProcess_State_Killed,
kwsysProcess_State_Disowned
};
/**
......@@ -236,6 +246,15 @@ kwsysEXPORT const char* kwsysProcess_GetExceptionString(kwsysProcess* cp);
*/
kwsysEXPORT void kwsysProcess_Execute(kwsysProcess* cp);
/**
* Stop management of a detached child process. This closes any pipes
* being read. If the child was not created with the
* kwsysProcess_Option_Detach option, this method does nothing. This
* is because disowning a non-detached process will cause the child
* exit signal to be left unhandled until this process exits.
*/
kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp);
/**
* Block until data are available on a pipe, a timeout expires, or the
* child process terminates. Arguments are as follows:
......@@ -318,6 +337,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_SetWorkingDirectory
# undef kwsysProcess_SetPipeFile
# undef kwsysProcess_SetPipeShared
# undef kwsysProcess_Option_Detach
# undef kwsysProcess_Option_HideWindow
# undef kwsysProcess_GetOption
# undef kwsysProcess_SetOption
......@@ -329,6 +349,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_State_Exited
# undef kwsysProcess_State_Expired
# undef kwsysProcess_State_Killed
# undef kwsysProcess_State_Disowned
# undef kwsysProcess_GetState
# undef kwsysProcess_State_e
# undef kwsysProcess_Exception_None
......@@ -344,6 +365,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_GetErrorString
# undef kwsysProcess_GetExceptionString
# undef kwsysProcess_Execute
# undef kwsysProcess_Disown
# undef kwsysProcess_WaitForData
# undef kwsysProcess_Pipes_e
# undef kwsysProcess_Pipe_None
......
......@@ -99,6 +99,8 @@ static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProc
static void kwsysProcessSetExitException(kwsysProcess* cp, int sig);
static void kwsysProcessChildErrorExit(int errorPipe);
static void kwsysProcessRestoreDefaultSignalHandlers(void);
static pid_t kwsysProcessFork(kwsysProcess* cp,
kwsysProcessCreateInformation* si);
static void kwsysProcessKill(pid_t process_id);
/*--------------------------------------------------------------------------*/
......@@ -127,6 +129,12 @@ struct kwsysProcess_s
/* The working directory for the process. */
char* WorkingDirectory;
/* Whether to create the child as a detached process. */
int OptionDetach;
/* Whether the child was created as a detached process. */
int Detached;
/* Time at which the child started. Negative for no timeout. */
kwsysProcessTime StartTime;
......@@ -217,7 +225,14 @@ void kwsysProcess_Delete(kwsysProcess* cp)
/* If the process is executing, wait for it to finish. */
if(cp->State == kwsysProcess_State_Executing)
{
kwsysProcess_WaitForExit(cp, 0);
if(cp->Detached)
{
kwsysProcess_Disown(cp);
}
else
{
kwsysProcess_WaitForExit(cp, 0);
}
}
/* Free memory. */
......@@ -445,17 +460,31 @@ void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared)
/*--------------------------------------------------------------------------*/
int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
{
(void)cp;
(void)optionId;
return 0;
if(!cp)
{
return 0;
}
switch(optionId)
{
case kwsysProcess_Option_Detach: return cp->OptionDetach;
default: return 0;
}
}
/*--------------------------------------------------------------------------*/
void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value)
{
(void)cp;
(void)optionId;
(void)value;
if(!cp)
{
return;
}
switch(optionId)
{
case kwsysProcess_Option_Detach: cp->OptionDetach = value; break;
default: break;
}
}
/*--------------------------------------------------------------------------*/
......@@ -673,6 +702,45 @@ void kwsysProcess_Execute(kwsysProcess* cp)
/* The process has now started. */
cp->State = kwsysProcess_State_Executing;
cp->Detached = cp->OptionDetach;
}
/*--------------------------------------------------------------------------*/
kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp)
{
int i;
/* Make sure a detached child process is running. */
if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing)
{
return;
}
/* Close any pipes that are still open. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
if(cp->PipeReadEnds[i] >= 0)
{
/* If the pipe was reported by the last call to select, we must
read from it. Ignore the data. */
if(FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
{
/* 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((read(cp->PipeReadEnds[i], cp->PipeBuffer,
KWSYSPE_PIPE_BUFFER_SIZE) < 0) && (errno == EINTR));
}
/* We are done reading from this pipe. */
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
--cp->PipesLeft;
}
}
cp->State = kwsysProcess_State_Disowned;
}
/*--------------------------------------------------------------------------*/
......@@ -901,21 +969,22 @@ 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. */
{
int i;
for(i=0; i < cp->NumberOfCommands; ++i)
if(!cp->Detached)
{
while(((result = waitpid(cp->ForkPIDs[i],
&cp->CommandExitCodes[i], 0)) < 0) &&
(errno == EINTR));
if(result <= 0 && cp->State != kwsysProcess_State_Error)
int i;
for(i=0; i < cp->NumberOfCommands; ++i)
{
/* Unexpected error. Report the first time this happens. */
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
cp->State = kwsysProcess_State_Error;
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)
......@@ -1225,7 +1294,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index,
}
/* Fork off a child process. */
cp->ForkPIDs[index] = fork();
cp->ForkPIDs[index] = kwsysProcessFork(cp, si);
if(cp->ForkPIDs[index] < 0)
{
return 0;
......@@ -1719,6 +1788,61 @@ static void kwsysProcessRestoreDefaultSignalHandlers(void)
#endif
}
/*--------------------------------------------------------------------------*/
static pid_t kwsysProcessFork(kwsysProcess* cp,
kwsysProcessCreateInformation* si)
{
/* Create a detached process if requested. */
if(cp->OptionDetach)
{
/* Create an intermediate process. */
pid_t middle_pid = fork();
if(middle_pid < 0)
{
/* Fork failed. Return as if we were not detaching. */
return middle_pid;
}
else if(middle_pid == 0)
{
/* This is the intermediate process. Create the real child. */
pid_t child_pid = fork();
if(child_pid == 0)
{
/* This is the real child process. There is nothing to do here. */
return 0;
}
else
{
/* Use the error pipe to report the pid to the real parent. */
while((write(si->ErrorPipe[1], &child_pid, sizeof(child_pid)) < 0) &&
(errno == EINTR));
/* Exit without cleanup. The parent holds all resources. */
_exit(0);
}
}
else
{
/* This is the original parent process. The intermediate
process will use the error pipe to report the pid of the
detached child. */
pid_t child_pid;
int status;
while((read(si->ErrorPipe[0], &child_pid, sizeof(child_pid)) < 0) &&
(errno == EINTR));
/* Wait for the intermediate process to exit and clean it up. */
while((waitpid(middle_pid, &status, 0) < 0) && (errno == EINTR));
return child_pid;
}
}
else
{
/* Not creating a detached process. Use normal fork. */
return fork();
}
}
/*--------------------------------------------------------------------------*/
static void kwsysProcessKill(pid_t process_id)
{
......
......@@ -1136,6 +1136,13 @@ void kwsysProcess_Execute(kwsysProcess* cp)
cp->State = kwsysProcess_State_Executing;
}
/*--------------------------------------------------------------------------*/
void kwsysProcess_Disown(kwsysProcess* cp)
{
/* TODO: Implement windows version. */
(void)cp;
}
/*--------------------------------------------------------------------------*/
int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment