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_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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue