ENH: Added kwsysProcess_Disown an kwsysProcess_Option_Detach to allow detached processes to be created. Currently implemented only on UNIX.

This commit is contained in:
Brad King 2004-07-07 17:27:50 -04:00
parent 52eff235d8
commit ae28d93a72
3 changed files with 175 additions and 22 deletions

View File

@ -32,6 +32,7 @@
#define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory) #define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory)
#define kwsysProcess_SetPipeFile kwsys_ns(Process_SetPipeFile) #define kwsysProcess_SetPipeFile kwsys_ns(Process_SetPipeFile)
#define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared) #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_Option_HideWindow kwsys_ns(Process_Option_HideWindow)
#define kwsysProcess_GetOption kwsys_ns(Process_GetOption) #define kwsysProcess_GetOption kwsys_ns(Process_GetOption)
#define kwsysProcess_SetOption kwsys_ns(Process_SetOption) #define kwsysProcess_SetOption kwsys_ns(Process_SetOption)
@ -43,6 +44,7 @@
#define kwsysProcess_State_Exited kwsys_ns(Process_State_Exited) #define kwsysProcess_State_Exited kwsys_ns(Process_State_Exited)
#define kwsysProcess_State_Expired kwsys_ns(Process_State_Expired) #define kwsysProcess_State_Expired kwsys_ns(Process_State_Expired)
#define kwsysProcess_State_Killed kwsys_ns(Process_State_Killed) #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_GetState kwsys_ns(Process_GetState)
#define kwsysProcess_State_e kwsys_ns(Process_State_e) #define kwsysProcess_State_e kwsys_ns(Process_State_e)
#define kwsysProcess_Exception_None kwsys_ns(Process_Exception_None) #define kwsysProcess_Exception_None kwsys_ns(Process_Exception_None)
@ -58,6 +60,7 @@
#define kwsysProcess_GetErrorString kwsys_ns(Process_GetErrorString) #define kwsysProcess_GetErrorString kwsys_ns(Process_GetErrorString)
#define kwsysProcess_GetExceptionString kwsys_ns(Process_GetExceptionString) #define kwsysProcess_GetExceptionString kwsys_ns(Process_GetExceptionString)
#define kwsysProcess_Execute kwsys_ns(Process_Execute) #define kwsysProcess_Execute kwsys_ns(Process_Execute)
#define kwsysProcess_Disown kwsys_ns(Process_Disown)
#define kwsysProcess_WaitForData kwsys_ns(Process_WaitForData) #define kwsysProcess_WaitForData kwsys_ns(Process_WaitForData)
#define kwsysProcess_Pipes_e kwsys_ns(Process_Pipes_e) #define kwsysProcess_Pipes_e kwsys_ns(Process_Pipes_e)
#define kwsysProcess_Pipe_None kwsys_ns(Process_Pipe_None) #define kwsysProcess_Pipe_None kwsys_ns(Process_Pipe_None)
@ -140,7 +143,11 @@ kwsysEXPORT void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe,
int shared); 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. * kwsysProcess_Option_HideWindow = Whether to hide window on Windows.
* 0 = No (default) * 0 = No (default)
@ -151,7 +158,8 @@ kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId,
int value); int value);
enum kwsysProcess_Option_e 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_Exited = Child process exited normally.
* kwsysProcess_State_Expired = Child process's timeout expired. * kwsysProcess_State_Expired = Child process's timeout expired.
* kwsysProcess_State_Killed = Child process terminated by Kill method. * 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); kwsysEXPORT int kwsysProcess_GetState(kwsysProcess* cp);
enum kwsysProcess_State_e enum kwsysProcess_State_e
@ -174,7 +183,8 @@ enum kwsysProcess_State_e
kwsysProcess_State_Executing, kwsysProcess_State_Executing,
kwsysProcess_State_Exited, kwsysProcess_State_Exited,
kwsysProcess_State_Expired, 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); 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 * Block until data are available on a pipe, a timeout expires, or the
* child process terminates. Arguments are as follows: * child process terminates. Arguments are as follows:
@ -318,6 +337,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_SetWorkingDirectory # undef kwsysProcess_SetWorkingDirectory
# undef kwsysProcess_SetPipeFile # undef kwsysProcess_SetPipeFile
# undef kwsysProcess_SetPipeShared # undef kwsysProcess_SetPipeShared
# undef kwsysProcess_Option_Detach
# undef kwsysProcess_Option_HideWindow # undef kwsysProcess_Option_HideWindow
# undef kwsysProcess_GetOption # undef kwsysProcess_GetOption
# undef kwsysProcess_SetOption # undef kwsysProcess_SetOption
@ -329,6 +349,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_State_Exited # undef kwsysProcess_State_Exited
# undef kwsysProcess_State_Expired # undef kwsysProcess_State_Expired
# undef kwsysProcess_State_Killed # undef kwsysProcess_State_Killed
# undef kwsysProcess_State_Disowned
# undef kwsysProcess_GetState # undef kwsysProcess_GetState
# undef kwsysProcess_State_e # undef kwsysProcess_State_e
# undef kwsysProcess_Exception_None # undef kwsysProcess_Exception_None
@ -344,6 +365,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_GetErrorString # undef kwsysProcess_GetErrorString
# undef kwsysProcess_GetExceptionString # undef kwsysProcess_GetExceptionString
# undef kwsysProcess_Execute # undef kwsysProcess_Execute
# undef kwsysProcess_Disown
# undef kwsysProcess_WaitForData # undef kwsysProcess_WaitForData
# undef kwsysProcess_Pipes_e # undef kwsysProcess_Pipes_e
# undef kwsysProcess_Pipe_None # undef kwsysProcess_Pipe_None

View File

@ -99,6 +99,8 @@ static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProc
static void kwsysProcessSetExitException(kwsysProcess* cp, int sig); static void kwsysProcessSetExitException(kwsysProcess* cp, int sig);
static void kwsysProcessChildErrorExit(int errorPipe); static void kwsysProcessChildErrorExit(int errorPipe);
static void kwsysProcessRestoreDefaultSignalHandlers(void); static void kwsysProcessRestoreDefaultSignalHandlers(void);
static pid_t kwsysProcessFork(kwsysProcess* cp,
kwsysProcessCreateInformation* si);
static void kwsysProcessKill(pid_t process_id); static void kwsysProcessKill(pid_t process_id);
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@ -127,6 +129,12 @@ struct kwsysProcess_s
/* The working directory for the process. */ /* The working directory for the process. */
char* WorkingDirectory; 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. */ /* Time at which the child started. Negative for no timeout. */
kwsysProcessTime StartTime; kwsysProcessTime StartTime;
@ -216,9 +224,16 @@ void kwsysProcess_Delete(kwsysProcess* cp)
/* If the process is executing, wait for it to finish. */ /* If the process is executing, wait for it to finish. */
if(cp->State == kwsysProcess_State_Executing) if(cp->State == kwsysProcess_State_Executing)
{
if(cp->Detached)
{
kwsysProcess_Disown(cp);
}
else
{ {
kwsysProcess_WaitForExit(cp, 0); kwsysProcess_WaitForExit(cp, 0);
} }
}
/* Free memory. */ /* Free memory. */
kwsysProcess_SetCommand(cp, 0); kwsysProcess_SetCommand(cp, 0);
@ -445,17 +460,31 @@ void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
{ {
(void)cp; if(!cp)
(void)optionId; {
return 0; return 0;
}
switch(optionId)
{
case kwsysProcess_Option_Detach: return cp->OptionDetach;
default: return 0;
}
} }
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value)
{ {
(void)cp; if(!cp)
(void)optionId; {
(void)value; 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. */ /* The process has now started. */
cp->State = kwsysProcess_State_Executing; 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,6 +969,7 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
/* Wait for each child to terminate. The process should have /* Wait for each child to terminate. The process should have
already exited because KWSYSPE_PIPE_TERM has been closed by this already exited because KWSYSPE_PIPE_TERM has been closed by this
point. Repeat the call until it is not interrupted. */ point. Repeat the call until it is not interrupted. */
if(!cp->Detached)
{ {
int i; int i;
for(i=0; i < cp->NumberOfCommands; ++i) for(i=0; i < cp->NumberOfCommands; ++i)
@ -1225,7 +1294,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index,
} }
/* Fork off a child process. */ /* Fork off a child process. */
cp->ForkPIDs[index] = fork(); cp->ForkPIDs[index] = kwsysProcessFork(cp, si);
if(cp->ForkPIDs[index] < 0) if(cp->ForkPIDs[index] < 0)
{ {
return 0; return 0;
@ -1719,6 +1788,61 @@ static void kwsysProcessRestoreDefaultSignalHandlers(void)
#endif #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) static void kwsysProcessKill(pid_t process_id)
{ {

View File

@ -1136,6 +1136,13 @@ void kwsysProcess_Execute(kwsysProcess* cp)
cp->State = kwsysProcess_State_Executing; 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, int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,