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:
parent
52eff235d8
commit
ae28d93a72
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue