KWSys 2015-07-30 (f63febb7)
Extract upstream KWSys using the following shell commands. $ git archive --prefix=upstream-kwsys/ f63febb7 | tar x $ git shortlog --no-merges --abbrev=8 --format='%h %s' c9336bcf..f63febb7 Brad King (1): 83b4a6b8 Process: Fix conversion warning in testProcess.c James Johnston (7): 4cd8846c Process: Remove trailing whitespace in ProcessUNIX.c b1c44c58 Process: Refactor sleeping code in testProcess.c. faff2ab0 Process: Wait for children to terminate on Ctrl+C. ef517b19 Process: Added initial support for process groups. 906c2cae Process: Added test cases for testing Ctrl+C and process groups. 52874e6a Process: Fix leaked file descriptor in ProcessUNIX f63febb7 Process: Fix error message for startup failure on Windows
This commit is contained in:
parent
dc822da815
commit
1feafc643b
|
@ -1237,7 +1237,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
|
||||||
IF(NOT CYGWIN)
|
IF(NOT CYGWIN)
|
||||||
SET(KWSYS_TEST_PROCESS_7 7)
|
SET(KWSYS_TEST_PROCESS_7 7)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
FOREACH(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7})
|
FOREACH(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7} 9 10)
|
||||||
ADD_TEST(kwsys.testProcess-${n} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n})
|
ADD_TEST(kwsys.testProcess-${n} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n})
|
||||||
SET_PROPERTY(TEST kwsys.testProcess-${n} PROPERTY LABELS ${KWSYS_LABELS_TEST})
|
SET_PROPERTY(TEST kwsys.testProcess-${n} PROPERTY LABELS ${KWSYS_LABELS_TEST})
|
||||||
SET_TESTS_PROPERTIES(kwsys.testProcess-${n} PROPERTIES TIMEOUT 120)
|
SET_TESTS_PROPERTIES(kwsys.testProcess-${n} PROPERTIES TIMEOUT 120)
|
||||||
|
@ -1270,6 +1270,10 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
|
||||||
MESSAGE(STATUS "GET_TEST_PROPERTY returned: ${wfv}")
|
MESSAGE(STATUS "GET_TEST_PROPERTY returned: ${wfv}")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
|
# Set up ctest custom configuration file.
|
||||||
|
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/CTestCustom.cmake.in
|
||||||
|
${PROJECT_BINARY_DIR}/CTestCustom.cmake @ONLY)
|
||||||
|
|
||||||
# Suppress known consistent failures on buggy systems.
|
# Suppress known consistent failures on buggy systems.
|
||||||
IF(KWSYS_TEST_BOGUS_FAILURES)
|
IF(KWSYS_TEST_BOGUS_FAILURES)
|
||||||
SET_TESTS_PROPERTIES(${KWSYS_TEST_BOGUS_FAILURES} PROPERTIES WILL_FAIL ON)
|
SET_TESTS_PROPERTIES(${KWSYS_TEST_BOGUS_FAILURES} PROPERTIES WILL_FAIL ON)
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# kwsys.testProcess-10 involves sending SIGINT to a child process, which then
|
||||||
|
# exits abnormally via a call to _exit(). (On Windows, a call to ExitProcess).
|
||||||
|
# Naturally, this results in plenty of memory being "leaked" by this child
|
||||||
|
# process - the memory check results are not meaningful in this case.
|
||||||
|
#
|
||||||
|
# kwsys.testProcess-9 also tests sending SIGINT to a child process. However,
|
||||||
|
# normal operation of that test involves the child process timing out, and the
|
||||||
|
# host process kills (SIGKILL) it as a result. Since it was SIGKILL'ed, the
|
||||||
|
# resulting memory leaks are not logged by valgrind anyway. Therefore, we
|
||||||
|
# don't have to exclude it.
|
||||||
|
|
||||||
|
set(CTEST_CUSTOM_MEMCHECK_IGNORE
|
||||||
|
${CTEST_CUSTOM_MEMCHECK_IGNORE}
|
||||||
|
kwsys.testProcess-10
|
||||||
|
)
|
131
Process.h.in
131
Process.h.in
|
@ -23,58 +23,60 @@
|
||||||
# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT
|
# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT
|
||||||
#endif
|
#endif
|
||||||
#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS
|
#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS
|
||||||
# define kwsysProcess kwsys_ns(Process)
|
# define kwsysProcess kwsys_ns(Process)
|
||||||
# define kwsysProcess_s kwsys_ns(Process_s)
|
# define kwsysProcess_s kwsys_ns(Process_s)
|
||||||
# define kwsysProcess_New kwsys_ns(Process_New)
|
# define kwsysProcess_New kwsys_ns(Process_New)
|
||||||
# define kwsysProcess_Delete kwsys_ns(Process_Delete)
|
# define kwsysProcess_Delete kwsys_ns(Process_Delete)
|
||||||
# define kwsysProcess_SetCommand kwsys_ns(Process_SetCommand)
|
# define kwsysProcess_SetCommand kwsys_ns(Process_SetCommand)
|
||||||
# define kwsysProcess_AddCommand kwsys_ns(Process_AddCommand)
|
# define kwsysProcess_AddCommand kwsys_ns(Process_AddCommand)
|
||||||
# define kwsysProcess_SetTimeout kwsys_ns(Process_SetTimeout)
|
# define kwsysProcess_SetTimeout kwsys_ns(Process_SetTimeout)
|
||||||
# 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_SetPipeNative kwsys_ns(Process_SetPipeNative)
|
# define kwsysProcess_SetPipeNative kwsys_ns(Process_SetPipeNative)
|
||||||
# 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_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_Option_MergeOutput kwsys_ns(Process_Option_MergeOutput)
|
# define kwsysProcess_Option_MergeOutput kwsys_ns(Process_Option_MergeOutput)
|
||||||
# define kwsysProcess_Option_Verbatim kwsys_ns(Process_Option_Verbatim)
|
# define kwsysProcess_Option_Verbatim kwsys_ns(Process_Option_Verbatim)
|
||||||
# define kwsysProcess_GetOption kwsys_ns(Process_GetOption)
|
# define kwsysProcess_Option_CreateProcessGroup kwsys_ns(Process_Option_CreateProcessGroup)
|
||||||
# define kwsysProcess_SetOption kwsys_ns(Process_SetOption)
|
# define kwsysProcess_GetOption kwsys_ns(Process_GetOption)
|
||||||
# define kwsysProcess_Option_e kwsys_ns(Process_Option_e)
|
# define kwsysProcess_SetOption kwsys_ns(Process_SetOption)
|
||||||
# define kwsysProcess_State_Starting kwsys_ns(Process_State_Starting)
|
# define kwsysProcess_Option_e kwsys_ns(Process_Option_e)
|
||||||
# define kwsysProcess_State_Error kwsys_ns(Process_State_Error)
|
# define kwsysProcess_State_Starting kwsys_ns(Process_State_Starting)
|
||||||
# define kwsysProcess_State_Exception kwsys_ns(Process_State_Exception)
|
# define kwsysProcess_State_Error kwsys_ns(Process_State_Error)
|
||||||
# define kwsysProcess_State_Executing kwsys_ns(Process_State_Executing)
|
# define kwsysProcess_State_Exception kwsys_ns(Process_State_Exception)
|
||||||
# define kwsysProcess_State_Exited kwsys_ns(Process_State_Exited)
|
# define kwsysProcess_State_Executing kwsys_ns(Process_State_Executing)
|
||||||
# define kwsysProcess_State_Expired kwsys_ns(Process_State_Expired)
|
# define kwsysProcess_State_Exited kwsys_ns(Process_State_Exited)
|
||||||
# define kwsysProcess_State_Killed kwsys_ns(Process_State_Killed)
|
# define kwsysProcess_State_Expired kwsys_ns(Process_State_Expired)
|
||||||
# define kwsysProcess_State_Disowned kwsys_ns(Process_State_Disowned)
|
# define kwsysProcess_State_Killed kwsys_ns(Process_State_Killed)
|
||||||
# define kwsysProcess_GetState kwsys_ns(Process_GetState)
|
# define kwsysProcess_State_Disowned kwsys_ns(Process_State_Disowned)
|
||||||
# define kwsysProcess_State_e kwsys_ns(Process_State_e)
|
# define kwsysProcess_GetState kwsys_ns(Process_GetState)
|
||||||
# define kwsysProcess_Exception_None kwsys_ns(Process_Exception_None)
|
# define kwsysProcess_State_e kwsys_ns(Process_State_e)
|
||||||
# define kwsysProcess_Exception_Fault kwsys_ns(Process_Exception_Fault)
|
# define kwsysProcess_Exception_None kwsys_ns(Process_Exception_None)
|
||||||
# define kwsysProcess_Exception_Illegal kwsys_ns(Process_Exception_Illegal)
|
# define kwsysProcess_Exception_Fault kwsys_ns(Process_Exception_Fault)
|
||||||
# define kwsysProcess_Exception_Interrupt kwsys_ns(Process_Exception_Interrupt)
|
# define kwsysProcess_Exception_Illegal kwsys_ns(Process_Exception_Illegal)
|
||||||
# define kwsysProcess_Exception_Numerical kwsys_ns(Process_Exception_Numerical)
|
# define kwsysProcess_Exception_Interrupt kwsys_ns(Process_Exception_Interrupt)
|
||||||
# define kwsysProcess_Exception_Other kwsys_ns(Process_Exception_Other)
|
# define kwsysProcess_Exception_Numerical kwsys_ns(Process_Exception_Numerical)
|
||||||
# define kwsysProcess_GetExitException kwsys_ns(Process_GetExitException)
|
# define kwsysProcess_Exception_Other kwsys_ns(Process_Exception_Other)
|
||||||
# define kwsysProcess_Exception_e kwsys_ns(Process_Exception_e)
|
# define kwsysProcess_GetExitException kwsys_ns(Process_GetExitException)
|
||||||
# define kwsysProcess_GetExitCode kwsys_ns(Process_GetExitCode)
|
# define kwsysProcess_Exception_e kwsys_ns(Process_Exception_e)
|
||||||
# define kwsysProcess_GetExitValue kwsys_ns(Process_GetExitValue)
|
# define kwsysProcess_GetExitCode kwsys_ns(Process_GetExitCode)
|
||||||
# define kwsysProcess_GetErrorString kwsys_ns(Process_GetErrorString)
|
# define kwsysProcess_GetExitValue kwsys_ns(Process_GetExitValue)
|
||||||
# define kwsysProcess_GetExceptionString kwsys_ns(Process_GetExceptionString)
|
# define kwsysProcess_GetErrorString kwsys_ns(Process_GetErrorString)
|
||||||
# define kwsysProcess_Execute kwsys_ns(Process_Execute)
|
# define kwsysProcess_GetExceptionString kwsys_ns(Process_GetExceptionString)
|
||||||
# define kwsysProcess_Disown kwsys_ns(Process_Disown)
|
# define kwsysProcess_Execute kwsys_ns(Process_Execute)
|
||||||
# define kwsysProcess_WaitForData kwsys_ns(Process_WaitForData)
|
# define kwsysProcess_Disown kwsys_ns(Process_Disown)
|
||||||
# define kwsysProcess_Pipes_e kwsys_ns(Process_Pipes_e)
|
# define kwsysProcess_WaitForData kwsys_ns(Process_WaitForData)
|
||||||
# define kwsysProcess_Pipe_None kwsys_ns(Process_Pipe_None)
|
# define kwsysProcess_Pipes_e kwsys_ns(Process_Pipes_e)
|
||||||
# define kwsysProcess_Pipe_STDIN kwsys_ns(Process_Pipe_STDIN)
|
# define kwsysProcess_Pipe_None kwsys_ns(Process_Pipe_None)
|
||||||
# define kwsysProcess_Pipe_STDOUT kwsys_ns(Process_Pipe_STDOUT)
|
# define kwsysProcess_Pipe_STDIN kwsys_ns(Process_Pipe_STDIN)
|
||||||
# define kwsysProcess_Pipe_STDERR kwsys_ns(Process_Pipe_STDERR)
|
# define kwsysProcess_Pipe_STDOUT kwsys_ns(Process_Pipe_STDOUT)
|
||||||
# define kwsysProcess_Pipe_Timeout kwsys_ns(Process_Pipe_Timeout)
|
# define kwsysProcess_Pipe_STDERR kwsys_ns(Process_Pipe_STDERR)
|
||||||
# define kwsysProcess_Pipe_Handle kwsys_ns(Process_Pipe_Handle)
|
# define kwsysProcess_Pipe_Timeout kwsys_ns(Process_Pipe_Timeout)
|
||||||
# define kwsysProcess_WaitForExit kwsys_ns(Process_WaitForExit)
|
# define kwsysProcess_Pipe_Handle kwsys_ns(Process_Pipe_Handle)
|
||||||
# define kwsysProcess_Kill kwsys_ns(Process_Kill)
|
# define kwsysProcess_WaitForExit kwsys_ns(Process_WaitForExit)
|
||||||
|
# define kwsysProcess_Interrupt kwsys_ns(Process_Interrupt)
|
||||||
|
# define kwsysProcess_Kill kwsys_ns(Process_Kill)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
|
@ -199,6 +201,15 @@ kwsysEXPORT void kwsysProcess_SetPipeNative(kwsysProcess* cp, int pipe,
|
||||||
* and ignore the rest of the arguments.
|
* and ignore the rest of the arguments.
|
||||||
* 0 = No (default)
|
* 0 = No (default)
|
||||||
* 1 = Yes
|
* 1 = Yes
|
||||||
|
*
|
||||||
|
* kwsysProcess_Option_CreateProcessGroup = Whether to place the process in a
|
||||||
|
* new process group. This is
|
||||||
|
* useful if you want to send Ctrl+C
|
||||||
|
* to the process. On UNIX, also
|
||||||
|
* places the process in a new
|
||||||
|
* session.
|
||||||
|
* 0 = No (default)
|
||||||
|
* 1 = Yes
|
||||||
*/
|
*/
|
||||||
kwsysEXPORT int kwsysProcess_GetOption(kwsysProcess* cp, int optionId);
|
kwsysEXPORT int kwsysProcess_GetOption(kwsysProcess* cp, int optionId);
|
||||||
kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId,
|
kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId,
|
||||||
|
@ -208,7 +219,8 @@ enum kwsysProcess_Option_e
|
||||||
kwsysProcess_Option_HideWindow,
|
kwsysProcess_Option_HideWindow,
|
||||||
kwsysProcess_Option_Detach,
|
kwsysProcess_Option_Detach,
|
||||||
kwsysProcess_Option_MergeOutput,
|
kwsysProcess_Option_MergeOutput,
|
||||||
kwsysProcess_Option_Verbatim
|
kwsysProcess_Option_Verbatim,
|
||||||
|
kwsysProcess_Option_CreateProcessGroup
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,6 +374,17 @@ enum kwsysProcess_Pipes_e
|
||||||
*/
|
*/
|
||||||
kwsysEXPORT int kwsysProcess_WaitForExit(kwsysProcess* cp, double* timeout);
|
kwsysEXPORT int kwsysProcess_WaitForExit(kwsysProcess* cp, double* timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupt the process group for the child process that is currently
|
||||||
|
* running by sending it the appropriate operating-system specific signal.
|
||||||
|
* The caller should call WaitForExit after this returns to wait for the
|
||||||
|
* child to terminate.
|
||||||
|
*
|
||||||
|
* WARNING: If you didn't specify kwsysProcess_Option_CreateProcessGroup,
|
||||||
|
* you will interrupt your own process group.
|
||||||
|
*/
|
||||||
|
kwsysEXPORT void kwsysProcess_Interrupt(kwsysProcess* cp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forcefully terminate the child process that is currently running.
|
* Forcefully terminate the child process that is currently running.
|
||||||
* The caller should call WaitForExit after this returns to wait for
|
* The caller should call WaitForExit after this returns to wait for
|
||||||
|
@ -394,6 +417,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
|
||||||
# undef kwsysProcess_Option_HideWindow
|
# undef kwsysProcess_Option_HideWindow
|
||||||
# undef kwsysProcess_Option_MergeOutput
|
# undef kwsysProcess_Option_MergeOutput
|
||||||
# undef kwsysProcess_Option_Verbatim
|
# undef kwsysProcess_Option_Verbatim
|
||||||
|
# undef kwsysProcess_Option_CreateProcessGroup
|
||||||
# undef kwsysProcess_GetOption
|
# undef kwsysProcess_GetOption
|
||||||
# undef kwsysProcess_SetOption
|
# undef kwsysProcess_SetOption
|
||||||
# undef kwsysProcess_Option_e
|
# undef kwsysProcess_Option_e
|
||||||
|
@ -430,6 +454,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
|
||||||
# undef kwsysProcess_Pipe_Timeout
|
# undef kwsysProcess_Pipe_Timeout
|
||||||
# undef kwsysProcess_Pipe_Handle
|
# undef kwsysProcess_Pipe_Handle
|
||||||
# undef kwsysProcess_WaitForExit
|
# undef kwsysProcess_WaitForExit
|
||||||
|
# undef kwsysProcess_Interrupt
|
||||||
# undef kwsysProcess_Kill
|
# undef kwsysProcess_Kill
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
357
ProcessUNIX.c
357
ProcessUNIX.c
|
@ -88,7 +88,7 @@ typedef ssize_t kwsysProcess_ssize_t;
|
||||||
typedef int kwsysProcess_ssize_t;
|
typedef int kwsysProcess_ssize_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__BEOS__) && !defined(__ZETA__)
|
#if defined(__BEOS__) && !defined(__ZETA__)
|
||||||
/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
|
/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
|
||||||
# include <be/kernel/OS.h>
|
# include <be/kernel/OS.h>
|
||||||
static inline void kwsysProcess_usleep(unsigned int msec)
|
static inline void kwsysProcess_usleep(unsigned int msec)
|
||||||
|
@ -151,6 +151,7 @@ typedef struct kwsysProcessCreateInformation_s
|
||||||
} kwsysProcessCreateInformation;
|
} kwsysProcessCreateInformation;
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
static void kwsysProcessVolatileFree(volatile void* p);
|
||||||
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);
|
||||||
|
@ -197,7 +198,7 @@ struct kwsysProcess_s
|
||||||
{
|
{
|
||||||
/* The command lines to execute. */
|
/* The command lines to execute. */
|
||||||
char*** Commands;
|
char*** Commands;
|
||||||
int NumberOfCommands;
|
volatile int NumberOfCommands;
|
||||||
|
|
||||||
/* Descriptors for the read ends of the child's output pipes and
|
/* Descriptors for the read ends of the child's output pipes and
|
||||||
the signal pipe. */
|
the signal pipe. */
|
||||||
|
@ -213,8 +214,10 @@ struct kwsysProcess_s
|
||||||
/* Buffer for pipe data. */
|
/* Buffer for pipe data. */
|
||||||
char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
|
char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
|
||||||
|
|
||||||
/* Process IDs returned by the calls to fork. */
|
/* Process IDs returned by the calls to fork. Everything is volatile
|
||||||
pid_t* ForkPIDs;
|
because the signal handler accesses them. You must be very careful
|
||||||
|
when reaping PIDs or modifying this array to avoid race conditions. */
|
||||||
|
volatile pid_t* volatile ForkPIDs;
|
||||||
|
|
||||||
/* Flag for whether the children were terminated by a faild select. */
|
/* Flag for whether the children were terminated by a faild select. */
|
||||||
int SelectError;
|
int SelectError;
|
||||||
|
@ -237,6 +240,9 @@ struct kwsysProcess_s
|
||||||
/* Whether to merge stdout/stderr of the child. */
|
/* Whether to merge stdout/stderr of the child. */
|
||||||
int MergeOutput;
|
int MergeOutput;
|
||||||
|
|
||||||
|
/* Whether to create the process in a new process group. */
|
||||||
|
volatile sig_atomic_t CreateProcessGroup;
|
||||||
|
|
||||||
/* Time at which the child started. Negative for no timeout. */
|
/* Time at which the child started. Negative for no timeout. */
|
||||||
kwsysProcessTime StartTime;
|
kwsysProcessTime StartTime;
|
||||||
|
|
||||||
|
@ -257,8 +263,9 @@ struct kwsysProcess_s
|
||||||
/* The number of children still executing. */
|
/* The number of children still executing. */
|
||||||
int CommandsLeft;
|
int CommandsLeft;
|
||||||
|
|
||||||
/* The current status of the child process. */
|
/* The current status of the child process. Must be atomic because
|
||||||
int State;
|
the signal handler checks this to avoid a race. */
|
||||||
|
volatile sig_atomic_t State;
|
||||||
|
|
||||||
/* The exceptional behavior that terminated the child process, if
|
/* The exceptional behavior that terminated the child process, if
|
||||||
* any. */
|
* any. */
|
||||||
|
@ -271,7 +278,7 @@ struct kwsysProcess_s
|
||||||
int ExitValue;
|
int ExitValue;
|
||||||
|
|
||||||
/* Whether the process was killed. */
|
/* Whether the process was killed. */
|
||||||
int Killed;
|
volatile sig_atomic_t Killed;
|
||||||
|
|
||||||
/* Buffer for error message in case of failure. */
|
/* Buffer for error message in case of failure. */
|
||||||
char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
|
char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
|
||||||
|
@ -649,6 +656,8 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
|
||||||
case kwsysProcess_Option_Detach: return cp->OptionDetach;
|
case kwsysProcess_Option_Detach: return cp->OptionDetach;
|
||||||
case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
|
case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
|
||||||
case kwsysProcess_Option_Verbatim: return cp->Verbatim;
|
case kwsysProcess_Option_Verbatim: return cp->Verbatim;
|
||||||
|
case kwsysProcess_Option_CreateProcessGroup:
|
||||||
|
return cp->CreateProcessGroup;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,6 +675,8 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value)
|
||||||
case kwsysProcess_Option_Detach: cp->OptionDetach = value; break;
|
case kwsysProcess_Option_Detach: cp->OptionDetach = value; break;
|
||||||
case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
|
case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
|
||||||
case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
|
case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
|
||||||
|
case kwsysProcess_Option_CreateProcessGroup:
|
||||||
|
cp->CreateProcessGroup = value; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1489,6 +1500,45 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
void kwsysProcess_Interrupt(kwsysProcess* cp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* Make sure we are executing a process. */
|
||||||
|
if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired ||
|
||||||
|
cp->Killed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interrupt the children. */
|
||||||
|
if (cp->CreateProcessGroup)
|
||||||
|
{
|
||||||
|
if(cp->ForkPIDs)
|
||||||
|
{
|
||||||
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
|
{
|
||||||
|
/* Make sure the PID is still valid. */
|
||||||
|
if(cp->ForkPIDs[i])
|
||||||
|
{
|
||||||
|
/* The user created a process group for this process. The group ID
|
||||||
|
is the process ID for the original process in the group. */
|
||||||
|
kill(-cp->ForkPIDs[i], SIGINT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No process group was created. Kill our own process group.
|
||||||
|
NOTE: While one could argue that we could call kill(cp->ForkPIDs[i],
|
||||||
|
SIGINT) as a way to still interrupt the process even though it's not in
|
||||||
|
a special group, this is not an option on Windows. Therefore, we kill
|
||||||
|
the current process group for consistency with Windows. */
|
||||||
|
kill(0, SIGINT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
void kwsysProcess_Kill(kwsysProcess* cp)
|
void kwsysProcess_Kill(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
|
@ -1538,11 +1588,29 @@ void kwsysProcess_Kill(kwsysProcess* cp)
|
||||||
cp->CommandsLeft = 0;
|
cp->CommandsLeft = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* Call the free() function with a pointer to volatile without causing
|
||||||
|
compiler warnings. */
|
||||||
|
static void kwsysProcessVolatileFree(volatile void* p)
|
||||||
|
{
|
||||||
|
/* clang has made it impossible to free memory that points to volatile
|
||||||
|
without first using special pragmas to disable a warning... */
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
# pragma clang diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
|
free((void*)p); /* The cast will silence most compilers, but not clang. */
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
/* Initialize a process control structure for kwsysProcess_Execute. */
|
/* Initialize a process control structure for kwsysProcess_Execute. */
|
||||||
static int kwsysProcessInitialize(kwsysProcess* cp)
|
static int kwsysProcessInitialize(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
volatile pid_t* oldForkPIDs;
|
||||||
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
|
||||||
{
|
{
|
||||||
cp->PipeReadEnds[i] = -1;
|
cp->PipeReadEnds[i] = -1;
|
||||||
|
@ -1571,16 +1639,21 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
|
||||||
cp->ErrorMessage[0] = 0;
|
cp->ErrorMessage[0] = 0;
|
||||||
strcpy(cp->ExitExceptionString, "No exception");
|
strcpy(cp->ExitExceptionString, "No exception");
|
||||||
|
|
||||||
if(cp->ForkPIDs)
|
oldForkPIDs = cp->ForkPIDs;
|
||||||
|
cp->ForkPIDs = (volatile pid_t*)malloc(
|
||||||
|
sizeof(volatile pid_t)*(size_t)(cp->NumberOfCommands));
|
||||||
|
if(oldForkPIDs)
|
||||||
{
|
{
|
||||||
free(cp->ForkPIDs);
|
kwsysProcessVolatileFree(oldForkPIDs);
|
||||||
}
|
}
|
||||||
cp->ForkPIDs = (pid_t*)malloc(sizeof(pid_t)*(size_t)(cp->NumberOfCommands));
|
|
||||||
if(!cp->ForkPIDs)
|
if(!cp->ForkPIDs)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memset(cp->ForkPIDs, 0, sizeof(pid_t)*(size_t)(cp->NumberOfCommands));
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
|
{
|
||||||
|
cp->ForkPIDs[i] = 0; /* can't use memset due to volatile */
|
||||||
|
}
|
||||||
|
|
||||||
if(cp->CommandExitCodes)
|
if(cp->CommandExitCodes)
|
||||||
{
|
{
|
||||||
|
@ -1671,7 +1744,7 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error)
|
||||||
/* Free memory. */
|
/* Free memory. */
|
||||||
if(cp->ForkPIDs)
|
if(cp->ForkPIDs)
|
||||||
{
|
{
|
||||||
free(cp->ForkPIDs);
|
kwsysProcessVolatileFree(cp->ForkPIDs);
|
||||||
cp->ForkPIDs = 0;
|
cp->ForkPIDs = 0;
|
||||||
}
|
}
|
||||||
if(cp->RealWorkingDirectory)
|
if(cp->RealWorkingDirectory)
|
||||||
|
@ -1758,15 +1831,49 @@ int decc$set_child_standard_streams(int fd1, int fd2, int fd3);
|
||||||
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
kwsysProcessCreateInformation* si)
|
kwsysProcessCreateInformation* si)
|
||||||
{
|
{
|
||||||
|
sigset_t mask, old_mask;
|
||||||
|
int pgidPipe[2];
|
||||||
|
char tmp;
|
||||||
|
ssize_t readRes;
|
||||||
|
|
||||||
/* Create the error reporting pipe. */
|
/* Create the error reporting pipe. */
|
||||||
if(pipe(si->ErrorPipe) < 0)
|
if(pipe(si->ErrorPipe) < 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set close-on-exec flag on the error pipe's write end. */
|
/* Create a pipe for detecting that the child process has created a process
|
||||||
if(fcntl(si->ErrorPipe[1], F_SETFD, FD_CLOEXEC) < 0)
|
group and session. */
|
||||||
|
if(pipe(pgidPipe) < 0)
|
||||||
{
|
{
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set close-on-exec flag on the pipe's write end. */
|
||||||
|
if(fcntl(si->ErrorPipe[1], F_SETFD, FD_CLOEXEC) < 0 ||
|
||||||
|
fcntl(pgidPipe[1], F_SETFD, FD_CLOEXEC) < 0)
|
||||||
|
{
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block SIGINT / SIGTERM while we start. The purpose is so that our signal
|
||||||
|
handler doesn't get called from the child process after the fork and
|
||||||
|
before the exec, and subsequently start kill()'ing PIDs from ForkPIDs. */
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGINT);
|
||||||
|
sigaddset(&mask, SIGTERM);
|
||||||
|
if(sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0)
|
||||||
|
{
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1774,13 +1881,19 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
#if defined(__VMS)
|
#if defined(__VMS)
|
||||||
/* VMS needs vfork and execvp to be in the same function because
|
/* VMS needs vfork and execvp to be in the same function because
|
||||||
they use setjmp/longjmp to run the child startup code in the
|
they use setjmp/longjmp to run the child startup code in the
|
||||||
parent! TODO: OptionDetach. */
|
parent! TODO: OptionDetach. Also
|
||||||
|
TODO: CreateProcessGroup. */
|
||||||
cp->ForkPIDs[prIndex] = vfork();
|
cp->ForkPIDs[prIndex] = vfork();
|
||||||
#else
|
#else
|
||||||
cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si);
|
cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si);
|
||||||
#endif
|
#endif
|
||||||
if(cp->ForkPIDs[prIndex] < 0)
|
if(cp->ForkPIDs[prIndex] < 0)
|
||||||
{
|
{
|
||||||
|
sigprocmask(SIG_SETMASK, &old_mask, 0);
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1790,8 +1903,10 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
/* Specify standard pipes for child process. */
|
/* Specify standard pipes for child process. */
|
||||||
decc$set_child_standard_streams(si->StdIn, si->StdOut, si->StdErr);
|
decc$set_child_standard_streams(si->StdIn, si->StdOut, si->StdErr);
|
||||||
#else
|
#else
|
||||||
/* Close the read end of the error reporting pipe. */
|
/* Close the read end of the error reporting / process group
|
||||||
|
setup pipe. */
|
||||||
close(si->ErrorPipe[0]);
|
close(si->ErrorPipe[0]);
|
||||||
|
close(pgidPipe[0]);
|
||||||
|
|
||||||
/* Setup the stdin, stdout, and stderr pipes. */
|
/* Setup the stdin, stdout, and stderr pipes. */
|
||||||
if(si->StdIn > 0)
|
if(si->StdIn > 0)
|
||||||
|
@ -1819,11 +1934,25 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
|
|
||||||
/* Restore all default signal handlers. */
|
/* Restore all default signal handlers. */
|
||||||
kwsysProcessRestoreDefaultSignalHandlers();
|
kwsysProcessRestoreDefaultSignalHandlers();
|
||||||
|
|
||||||
|
/* Now that we have restored default signal handling and created the
|
||||||
|
process group, restore mask. */
|
||||||
|
sigprocmask(SIG_SETMASK, &old_mask, 0);
|
||||||
|
|
||||||
|
/* Create new process group. We use setsid instead of setpgid to avoid
|
||||||
|
the child getting hung up on signals like SIGTTOU. (In the real world,
|
||||||
|
this has been observed where "git svn" ends up calling the "resize"
|
||||||
|
program which opens /dev/tty. */
|
||||||
|
if(cp->CreateProcessGroup && setsid() < 0)
|
||||||
|
{
|
||||||
|
kwsysProcessChildErrorExit(si->ErrorPipe[1]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Execute the real process. If successful, this does not return. */
|
/* Execute the real process. If successful, this does not return. */
|
||||||
execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]);
|
execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]);
|
||||||
/* TODO: What does VMS do if the child fails to start? */
|
/* TODO: What does VMS do if the child fails to start? */
|
||||||
|
/* TODO: On VMS, how do we put the process in a new group? */
|
||||||
|
|
||||||
/* Failure. Report error to parent and terminate. */
|
/* Failure. Report error to parent and terminate. */
|
||||||
kwsysProcessChildErrorExit(si->ErrorPipe[1]);
|
kwsysProcessChildErrorExit(si->ErrorPipe[1]);
|
||||||
|
@ -1834,12 +1963,34 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
|
||||||
decc$set_child_standard_streams(0, 1, 2);
|
decc$set_child_standard_streams(0, 1, 2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* We are done with the error reporting pipe and process group setup pipe
|
||||||
|
write end. */
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[1]);
|
||||||
|
|
||||||
|
/* Make sure the child is in the process group before we proceed. This
|
||||||
|
avoids race conditions with calls to the kill function that we make for
|
||||||
|
signalling process groups. */
|
||||||
|
while((readRes = read(pgidPipe[0], &tmp, 1)) > 0);
|
||||||
|
if(readRes < 0)
|
||||||
|
{
|
||||||
|
sigprocmask(SIG_SETMASK, &old_mask, 0);
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
kwsysProcessCleanupDescriptor(&pgidPipe[0]);
|
||||||
|
|
||||||
|
/* Unmask signals. */
|
||||||
|
if(sigprocmask(SIG_SETMASK, &old_mask, 0) < 0)
|
||||||
|
{
|
||||||
|
kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* A child has been created. */
|
/* A child has been created. */
|
||||||
++cp->CommandsLeft;
|
++cp->CommandsLeft;
|
||||||
|
|
||||||
/* We are done with the error reporting pipe write end. */
|
|
||||||
kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
|
|
||||||
|
|
||||||
/* Block until the child's exec call succeeds and closes the error
|
/* Block until the child's exec call succeeds and closes the error
|
||||||
pipe or writes data to the pipe to report an error. */
|
pipe or writes data to the pipe to report an error. */
|
||||||
{
|
{
|
||||||
|
@ -1877,6 +2028,17 @@ static void kwsysProcessDestroy(kwsysProcess* cp)
|
||||||
/* A child process has terminated. Reap it if it is one handled by
|
/* A child process has terminated. Reap it if it is one handled by
|
||||||
this object. */
|
this object. */
|
||||||
int i;
|
int i;
|
||||||
|
/* Temporarily disable signals that access ForkPIDs. We don't want them to
|
||||||
|
read a reaped PID, and writes to ForkPIDs are not atomic. */
|
||||||
|
sigset_t mask, old_mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGINT);
|
||||||
|
sigaddset(&mask, SIGTERM);
|
||||||
|
if(sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for(i=0; i < cp->NumberOfCommands; ++i)
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
{
|
{
|
||||||
if(cp->ForkPIDs[i])
|
if(cp->ForkPIDs[i])
|
||||||
|
@ -1910,6 +2072,9 @@ static void kwsysProcessDestroy(kwsysProcess* cp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Re-enable signals. */
|
||||||
|
sigprocmask(SIG_SETMASK, &old_mask, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
@ -1938,7 +2103,7 @@ static int kwsysProcessSetupOutputPipeFile(int* p, const char* name)
|
||||||
|
|
||||||
/* Assign the replacement descriptor. */
|
/* Assign the replacement descriptor. */
|
||||||
*p = fout;
|
*p = fout;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
@ -2582,19 +2747,23 @@ typedef struct kwsysProcessInstances_s
|
||||||
} kwsysProcessInstances;
|
} kwsysProcessInstances;
|
||||||
static kwsysProcessInstances kwsysProcesses;
|
static kwsysProcessInstances kwsysProcesses;
|
||||||
|
|
||||||
/* The old SIGCHLD handler. */
|
/* The old SIGCHLD / SIGINT / SIGTERM handlers. */
|
||||||
static struct sigaction kwsysProcessesOldSigChldAction;
|
static struct sigaction kwsysProcessesOldSigChldAction;
|
||||||
|
static struct sigaction kwsysProcessesOldSigIntAction;
|
||||||
|
static struct sigaction kwsysProcessesOldSigTermAction;
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses)
|
static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses)
|
||||||
{
|
{
|
||||||
/* Block SIGCHLD while we update the set of pipes to check.
|
/* Block signals while we update the set of pipes to check.
|
||||||
TODO: sigprocmask is undefined for threaded apps. See
|
TODO: sigprocmask is undefined for threaded apps. See
|
||||||
pthread_sigmask. */
|
pthread_sigmask. */
|
||||||
sigset_t newset;
|
sigset_t newset;
|
||||||
sigset_t oldset;
|
sigset_t oldset;
|
||||||
sigemptyset(&newset);
|
sigemptyset(&newset);
|
||||||
sigaddset(&newset, SIGCHLD);
|
sigaddset(&newset, SIGCHLD);
|
||||||
|
sigaddset(&newset, SIGINT);
|
||||||
|
sigaddset(&newset, SIGTERM);
|
||||||
sigprocmask(SIG_BLOCK, &newset, &oldset);
|
sigprocmask(SIG_BLOCK, &newset, &oldset);
|
||||||
|
|
||||||
/* Store the new set in that seen by the signal handler. */
|
/* Store the new set in that seen by the signal handler. */
|
||||||
|
@ -2686,21 +2855,36 @@ static int kwsysProcessesAdd(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
/* Install our handler for SIGCHLD. Repeat call until it is not
|
/* Install our handler for SIGCHLD. Repeat call until it is not
|
||||||
interrupted. */
|
interrupted. */
|
||||||
struct sigaction newSigChldAction;
|
struct sigaction newSigAction;
|
||||||
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
memset(&newSigAction, 0, sizeof(struct sigaction));
|
||||||
#if KWSYSPE_USE_SIGINFO
|
#if KWSYSPE_USE_SIGINFO
|
||||||
newSigChldAction.sa_sigaction = kwsysProcessesSignalHandler;
|
newSigAction.sa_sigaction = kwsysProcessesSignalHandler;
|
||||||
newSigChldAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
newSigAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
||||||
# ifdef SA_RESTART
|
# ifdef SA_RESTART
|
||||||
newSigChldAction.sa_flags |= SA_RESTART;
|
newSigAction.sa_flags |= SA_RESTART;
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
|
newSigAction.sa_handler = kwsysProcessesSignalHandler;
|
||||||
newSigChldAction.sa_flags = SA_NOCLDSTOP;
|
newSigAction.sa_flags = SA_NOCLDSTOP;
|
||||||
#endif
|
#endif
|
||||||
while((sigaction(SIGCHLD, &newSigChldAction,
|
sigemptyset(&newSigAction.sa_mask);
|
||||||
|
while((sigaction(SIGCHLD, &newSigAction,
|
||||||
&kwsysProcessesOldSigChldAction) < 0) &&
|
&kwsysProcessesOldSigChldAction) < 0) &&
|
||||||
(errno == EINTR));
|
(errno == EINTR));
|
||||||
|
|
||||||
|
/* Install our handler for SIGINT / SIGTERM. Repeat call until
|
||||||
|
it is not interrupted. */
|
||||||
|
sigemptyset(&newSigAction.sa_mask);
|
||||||
|
sigaddset(&newSigAction.sa_mask, SIGTERM);
|
||||||
|
while((sigaction(SIGINT, &newSigAction,
|
||||||
|
&kwsysProcessesOldSigIntAction) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
|
||||||
|
sigemptyset(&newSigAction.sa_mask);
|
||||||
|
sigaddset(&newSigAction.sa_mask, SIGINT);
|
||||||
|
while((sigaction(SIGTERM, &newSigAction,
|
||||||
|
&kwsysProcessesOldSigIntAction) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2734,10 +2918,14 @@ static void kwsysProcessesRemove(kwsysProcess* cp)
|
||||||
/* If this was the last process, disable the signal handler. */
|
/* If this was the last process, disable the signal handler. */
|
||||||
if(newProcesses.Count == 0)
|
if(newProcesses.Count == 0)
|
||||||
{
|
{
|
||||||
/* Restore the SIGCHLD handler. Repeat call until it is not
|
/* Restore the signal handlers. Repeat call until it is not
|
||||||
interrupted. */
|
interrupted. */
|
||||||
while((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) &&
|
while((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) &&
|
||||||
(errno == EINTR));
|
(errno == EINTR));
|
||||||
|
while((sigaction(SIGINT, &kwsysProcessesOldSigIntAction, 0) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
while((sigaction(SIGTERM, &kwsysProcessesOldSigTermAction, 0) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
|
||||||
/* Free the table of process pointers since it is now empty.
|
/* Free the table of process pointers since it is now empty.
|
||||||
This is safe because the signal handler has been removed. */
|
This is safe because the signal handler has been removed. */
|
||||||
|
@ -2763,39 +2951,108 @@ static void kwsysProcessesSignalHandler(int signum
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
(void)signum;
|
int i, j, procStatus, old_errno = errno;
|
||||||
#if KWSYSPE_USE_SIGINFO
|
#if KWSYSPE_USE_SIGINFO
|
||||||
(void)info;
|
(void)info;
|
||||||
(void)ucontext;
|
(void)ucontext;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Signal all process objects that a child has terminated. */
|
/* Signal all process objects that a child has terminated. */
|
||||||
{
|
switch(signum)
|
||||||
int i;
|
|
||||||
for(i=0; i < kwsysProcesses.Count; ++i)
|
|
||||||
{
|
{
|
||||||
/* Set the pipe in a signalled state. */
|
case SIGCHLD:
|
||||||
char buf = 1;
|
for(i=0; i < kwsysProcesses.Count; ++i)
|
||||||
kwsysProcess* cp = kwsysProcesses.Processes[i];
|
{
|
||||||
kwsysProcess_ssize_t status=
|
/* Set the pipe in a signalled state. */
|
||||||
read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
|
char buf = 1;
|
||||||
(void)status;
|
kwsysProcess* cp = kwsysProcesses.Processes[i];
|
||||||
status=write(cp->SignalPipe, &buf, 1);
|
kwsysProcess_ssize_t pipeStatus=
|
||||||
(void)status;
|
read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
|
||||||
|
(void)pipeStatus;
|
||||||
|
pipeStatus=write(cp->SignalPipe, &buf, 1);
|
||||||
|
(void)pipeStatus;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
case SIGTERM:
|
||||||
|
/* Signal child processes that are running in new process groups. */
|
||||||
|
for(i=0; i < kwsysProcesses.Count; ++i)
|
||||||
|
{
|
||||||
|
kwsysProcess* cp = kwsysProcesses.Processes[i];
|
||||||
|
/* Check Killed to avoid data race condition when killing.
|
||||||
|
Check State to avoid data race condition in kwsysProcessCleanup
|
||||||
|
when there is an error (it leaves a reaped PID). */
|
||||||
|
if(cp->CreateProcessGroup && !cp->Killed &&
|
||||||
|
cp->State != kwsysProcess_State_Error && cp->ForkPIDs)
|
||||||
|
{
|
||||||
|
for(j=0; j < cp->NumberOfCommands; ++j)
|
||||||
|
{
|
||||||
|
/* Make sure the PID is still valid. */
|
||||||
|
if(cp->ForkPIDs[j])
|
||||||
|
{
|
||||||
|
/* The user created a process group for this process. The group ID
|
||||||
|
is the process ID for the original process in the group. */
|
||||||
|
kill(-cp->ForkPIDs[j], SIGINT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for all processes to terminate. */
|
||||||
|
while(wait(&procStatus) >= 0 || errno != ECHILD)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminate the process, which is now in an inconsistent state
|
||||||
|
because we reaped all the PIDs that it may have been reaping
|
||||||
|
or may have reaped in the future. Reraise the signal so that
|
||||||
|
the proper exit code is returned. */
|
||||||
|
{
|
||||||
|
/* Install default signal handler. */
|
||||||
|
struct sigaction defSigAction;
|
||||||
|
sigset_t unblockSet;
|
||||||
|
memset(&defSigAction, 0, sizeof(defSigAction));
|
||||||
|
defSigAction.sa_handler = SIG_DFL;
|
||||||
|
sigemptyset(&defSigAction.sa_mask);
|
||||||
|
while((sigaction(signum, &defSigAction, 0) < 0) &&
|
||||||
|
(errno == EINTR));
|
||||||
|
/* Unmask the signal. */
|
||||||
|
sigemptyset(&unblockSet);
|
||||||
|
sigaddset(&unblockSet, signum);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &unblockSet, 0);
|
||||||
|
/* Raise the signal again. */
|
||||||
|
raise(signum);
|
||||||
|
/* We shouldn't get here... but if we do... */
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
/* break omitted to silence unreachable code clang compiler warning. */
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#if !KWSYSPE_USE_SIGINFO
|
#if !KWSYSPE_USE_SIGINFO
|
||||||
/* Re-Install our handler for SIGCHLD. Repeat call until it is not
|
/* Re-Install our handler. Repeat call until it is not interrupted. */
|
||||||
interrupted. */
|
|
||||||
{
|
{
|
||||||
struct sigaction newSigChldAction;
|
struct sigaction newSigAction;
|
||||||
memset(&newSigChldAction, 0, sizeof(struct sigaction));
|
struct sigaction &oldSigAction;
|
||||||
|
memset(&newSigAction, 0, sizeof(struct sigaction));
|
||||||
newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
|
newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
|
||||||
newSigChldAction.sa_flags = SA_NOCLDSTOP;
|
newSigChldAction.sa_flags = SA_NOCLDSTOP;
|
||||||
while((sigaction(SIGCHLD, &newSigChldAction,
|
sigemptyset(&newSigAction.sa_mask);
|
||||||
&kwsysProcessesOldSigChldAction) < 0) &&
|
switch(signum)
|
||||||
|
{
|
||||||
|
case SIGCHLD: oldSigAction = &kwsysProcessesOldSigChldAction; break;
|
||||||
|
case SIGINT:
|
||||||
|
sigaddset(&newSigAction.sa_mask, SIGTERM);
|
||||||
|
oldSigAction = &kwsysProcessesOldSigIntAction; break;
|
||||||
|
case SIGTERM:
|
||||||
|
sigaddset(&newSigAction.sa_mask, SIGINT);
|
||||||
|
oldSigAction = &kwsysProcessesOldSigTermAction; break;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
while((sigaction(signum, &newSigAction,
|
||||||
|
oldSigAction) < 0) &&
|
||||||
(errno == EINTR));
|
(errno == EINTR));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
errno = old_errno;
|
||||||
}
|
}
|
||||||
|
|
462
ProcessWin32.c
462
ProcessWin32.c
|
@ -109,14 +109,15 @@ static DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd);
|
||||||
static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp,
|
static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp,
|
||||||
kwsysProcessPipeData* td);
|
kwsysProcessPipeData* td);
|
||||||
static int kwsysProcessInitialize(kwsysProcess* cp);
|
static int kwsysProcessInitialize(kwsysProcess* cp);
|
||||||
static int kwsysProcessCreate(kwsysProcess* cp, int index,
|
static DWORD kwsysProcessCreate(kwsysProcess* cp, int index,
|
||||||
kwsysProcessCreateInformation* si);
|
kwsysProcessCreateInformation* si);
|
||||||
static void kwsysProcessDestroy(kwsysProcess* cp, int event);
|
static void kwsysProcessDestroy(kwsysProcess* cp, int event);
|
||||||
static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name);
|
static DWORD kwsysProcessSetupOutputPipeFile(PHANDLE handle,
|
||||||
|
const char* name);
|
||||||
static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle);
|
static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle);
|
||||||
static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle);
|
static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle);
|
||||||
static void kwsysProcessCleanupHandle(PHANDLE h);
|
static void kwsysProcessCleanupHandle(PHANDLE h);
|
||||||
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
|
static void kwsysProcessCleanup(kwsysProcess* cp, DWORD error);
|
||||||
static void kwsysProcessCleanErrorMessage(kwsysProcess* cp);
|
static void kwsysProcessCleanErrorMessage(kwsysProcess* cp);
|
||||||
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
|
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
|
||||||
kwsysProcessTime* timeoutTime);
|
kwsysProcessTime* timeoutTime);
|
||||||
|
@ -133,6 +134,13 @@ static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProc
|
||||||
static void kwsysProcessSetExitException(kwsysProcess* cp, int code);
|
static void kwsysProcessSetExitException(kwsysProcess* cp, int code);
|
||||||
static void kwsysProcessKillTree(int pid);
|
static void kwsysProcessKillTree(int pid);
|
||||||
static void kwsysProcessDisablePipeThreads(kwsysProcess* cp);
|
static void kwsysProcessDisablePipeThreads(kwsysProcess* cp);
|
||||||
|
static int kwsysProcessesInitialize(void);
|
||||||
|
static int kwsysTryEnterCreateProcessSection(void);
|
||||||
|
static void kwsysLeaveCreateProcessSection(void);
|
||||||
|
static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessId,
|
||||||
|
int newProcessGroup);
|
||||||
|
static void kwsysProcessesRemove(HANDLE hProcess);
|
||||||
|
static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType);
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
/* A structure containing synchronization data for each thread. */
|
/* A structure containing synchronization data for each thread. */
|
||||||
|
@ -222,6 +230,9 @@ struct kwsysProcess_s
|
||||||
/* Whether to merge stdout/stderr of the child. */
|
/* Whether to merge stdout/stderr of the child. */
|
||||||
int MergeOutput;
|
int MergeOutput;
|
||||||
|
|
||||||
|
/* Whether to create the process in a new process group. */
|
||||||
|
int CreateProcessGroup;
|
||||||
|
|
||||||
/* Mutex to protect the shared index used by threads to report data. */
|
/* Mutex to protect the shared index used by threads to report data. */
|
||||||
HANDLE SharedIndexMutex;
|
HANDLE SharedIndexMutex;
|
||||||
|
|
||||||
|
@ -321,6 +332,16 @@ kwsysProcess* kwsysProcess_New(void)
|
||||||
/* Windows version number data. */
|
/* Windows version number data. */
|
||||||
OSVERSIONINFO osv;
|
OSVERSIONINFO osv;
|
||||||
|
|
||||||
|
/* Initialize list of processes before we get any farther. It's especially
|
||||||
|
important that the console Ctrl handler be added BEFORE starting the
|
||||||
|
first process. This prevents the risk of an orphaned process being
|
||||||
|
started by the main thread while the default Ctrl handler is in
|
||||||
|
progress. */
|
||||||
|
if(!kwsysProcessesInitialize())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate a process control structure. */
|
/* Allocate a process control structure. */
|
||||||
cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
|
cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
|
||||||
if(!cp)
|
if(!cp)
|
||||||
|
@ -836,6 +857,8 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
|
||||||
case kwsysProcess_Option_HideWindow: return cp->HideWindow;
|
case kwsysProcess_Option_HideWindow: return cp->HideWindow;
|
||||||
case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
|
case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
|
||||||
case kwsysProcess_Option_Verbatim: return cp->Verbatim;
|
case kwsysProcess_Option_Verbatim: return cp->Verbatim;
|
||||||
|
case kwsysProcess_Option_CreateProcessGroup:
|
||||||
|
return cp->CreateProcessGroup;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -854,6 +877,8 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value)
|
||||||
case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break;
|
case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break;
|
||||||
case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
|
case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
|
||||||
case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
|
case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
|
||||||
|
case kwsysProcess_Option_CreateProcessGroup:
|
||||||
|
cp->CreateProcessGroup = value; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -945,7 +970,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
if(!GetCurrentDirectoryW(cp->RealWorkingDirectoryLength,
|
if(!GetCurrentDirectoryW(cp->RealWorkingDirectoryLength,
|
||||||
cp->RealWorkingDirectory))
|
cp->RealWorkingDirectory))
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetCurrentDirectoryW(cp->WorkingDirectory);
|
SetCurrentDirectoryW(cp->WorkingDirectory);
|
||||||
|
@ -957,14 +982,16 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
/* Create a handle to read a file for stdin. */
|
/* Create a handle to read a file for stdin. */
|
||||||
wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN);
|
wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN);
|
||||||
|
DWORD error;
|
||||||
cp->PipeChildStd[0] =
|
cp->PipeChildStd[0] =
|
||||||
CreateFileW(wstdin, GENERIC_READ|GENERIC_WRITE,
|
CreateFileW(wstdin, GENERIC_READ|GENERIC_WRITE,
|
||||||
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
0, OPEN_EXISTING, 0, 0);
|
0, OPEN_EXISTING, 0, 0);
|
||||||
|
error = GetLastError(); /* Check now in case free changes this. */
|
||||||
free(wstdin);
|
free(wstdin);
|
||||||
if(cp->PipeChildStd[0] == INVALID_HANDLE_VALUE)
|
if(cp->PipeChildStd[0] == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -990,17 +1017,18 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read,
|
if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read,
|
||||||
&cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0))
|
&cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0))
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cp->PipeFileSTDOUT)
|
if(cp->PipeFileSTDOUT)
|
||||||
{
|
{
|
||||||
/* Use a file for stdout. */
|
/* Use a file for stdout. */
|
||||||
if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1],
|
DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1],
|
||||||
cp->PipeFileSTDOUT))
|
cp->PipeFileSTDOUT);
|
||||||
|
if(error)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1023,7 +1051,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
GetCurrentProcess(), &cp->PipeChildStd[1],
|
GetCurrentProcess(), &cp->PipeChildStd[1],
|
||||||
0, FALSE, DUPLICATE_SAME_ACCESS))
|
0, FALSE, DUPLICATE_SAME_ACCESS))
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1034,17 +1062,18 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read,
|
if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read,
|
||||||
&cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0))
|
&cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0))
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cp->PipeFileSTDERR)
|
if(cp->PipeFileSTDERR)
|
||||||
{
|
{
|
||||||
/* Use a file for stderr. */
|
/* Use a file for stderr. */
|
||||||
if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2],
|
DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2],
|
||||||
cp->PipeFileSTDERR))
|
cp->PipeFileSTDERR);
|
||||||
|
if(error)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1067,7 +1096,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
GetCurrentProcess(), &cp->PipeChildStd[2],
|
GetCurrentProcess(), &cp->PipeChildStd[2],
|
||||||
0, FALSE, DUPLICATE_SAME_ACCESS))
|
0, FALSE, DUPLICATE_SAME_ACCESS))
|
||||||
{
|
{
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1106,11 +1135,12 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
HANDLE p[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
|
HANDLE p[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
|
||||||
if (!CreatePipe(&p[0], &p[1], 0, 0))
|
if (!CreatePipe(&p[0], &p[1], 0, 0))
|
||||||
{
|
{
|
||||||
|
DWORD error = GetLastError();
|
||||||
if (nextStdInput != cp->PipeChildStd[0])
|
if (nextStdInput != cp->PipeChildStd[0])
|
||||||
{
|
{
|
||||||
kwsysProcessCleanupHandle(&nextStdInput);
|
kwsysProcessCleanupHandle(&nextStdInput);
|
||||||
}
|
}
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nextStdInput = p[0];
|
nextStdInput = p[0];
|
||||||
|
@ -1119,7 +1149,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
si.hStdError = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2];
|
si.hStdError = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2];
|
||||||
|
|
||||||
{
|
{
|
||||||
int res = kwsysProcessCreate(cp, i, &si);
|
DWORD error = kwsysProcessCreate(cp, i, &si);
|
||||||
|
|
||||||
/* Close our copies of pipes used between children. */
|
/* Close our copies of pipes used between children. */
|
||||||
if (si.hStdInput != cp->PipeChildStd[0])
|
if (si.hStdInput != cp->PipeChildStd[0])
|
||||||
|
@ -1134,7 +1164,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanupHandle(&si.hStdError);
|
kwsysProcessCleanupHandle(&si.hStdError);
|
||||||
}
|
}
|
||||||
if (res)
|
if (!error)
|
||||||
{
|
{
|
||||||
cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess;
|
cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess;
|
||||||
}
|
}
|
||||||
|
@ -1144,7 +1174,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
kwsysProcessCleanupHandle(&nextStdInput);
|
kwsysProcessCleanupHandle(&nextStdInput);
|
||||||
}
|
}
|
||||||
kwsysProcessCleanup(cp, 1);
|
kwsysProcessCleanup(cp, error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1459,6 +1489,52 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
void kwsysProcess_Interrupt(kwsysProcess* cp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
/* Make sure we are executing a process. */
|
||||||
|
if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired ||
|
||||||
|
cp->Killed)
|
||||||
|
{
|
||||||
|
KWSYSPE_DEBUG((stderr, "interrupt: child not executing\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip actually interrupting the child if it has already terminated. */
|
||||||
|
if(cp->Terminated)
|
||||||
|
{
|
||||||
|
KWSYSPE_DEBUG((stderr, "interrupt: child already terminated\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interrupt the children. */
|
||||||
|
if (cp->CreateProcessGroup)
|
||||||
|
{
|
||||||
|
if(cp->ProcessInformation)
|
||||||
|
{
|
||||||
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
|
{
|
||||||
|
/* Make sure the process handle isn't closed (e.g. from disowning). */
|
||||||
|
if(cp->ProcessInformation[i].hProcess)
|
||||||
|
{
|
||||||
|
/* The user created a process group for this process. The group ID
|
||||||
|
is the process ID for the original process in the group. Note
|
||||||
|
that we have to use Ctrl+Break: Ctrl+C is not allowed for process
|
||||||
|
groups. */
|
||||||
|
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
|
||||||
|
cp->ProcessInformation[i].dwProcessId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No process group was created. Kill our own process group... */
|
||||||
|
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
void kwsysProcess_Kill(kwsysProcess* cp)
|
void kwsysProcess_Kill(kwsysProcess* cp)
|
||||||
{
|
{
|
||||||
|
@ -1487,7 +1563,8 @@ void kwsysProcess_Kill(kwsysProcess* cp)
|
||||||
for(i=0; i < cp->NumberOfCommands; ++i)
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
{
|
{
|
||||||
kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId);
|
kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId);
|
||||||
// close the handle if we kill it
|
/* Remove from global list of processes and close handles. */
|
||||||
|
kwsysProcessesRemove(cp->ProcessInformation[i].hProcess);
|
||||||
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
|
||||||
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
|
||||||
}
|
}
|
||||||
|
@ -1686,7 +1763,7 @@ int kwsysProcessInitialize(kwsysProcess* cp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
static int kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
|
static DWORD kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
|
||||||
{
|
{
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
|
|
||||||
|
@ -1697,13 +1774,19 @@ static int kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
|
||||||
if (flags & HANDLE_FLAG_INHERIT)
|
if (flags & HANDLE_FLAG_INHERIT)
|
||||||
{
|
{
|
||||||
*out = in;
|
*out = in;
|
||||||
return 1;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create an inherited copy of this handle. */
|
/* Create an inherited copy of this handle. */
|
||||||
return DuplicateHandle(GetCurrentProcess(), in,
|
if (DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), out,
|
||||||
GetCurrentProcess(), out,
|
0, TRUE, DUPLICATE_SAME_ACCESS))
|
||||||
0, TRUE, DUPLICATE_SAME_ACCESS);
|
{
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1719,29 +1802,46 @@ static int kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
|
||||||
(GENERIC_WRITE | FILE_READ_ATTRIBUTES)),
|
(GENERIC_WRITE | FILE_READ_ATTRIBUTES)),
|
||||||
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
&sa, OPEN_EXISTING, 0, 0);
|
&sa, OPEN_EXISTING, 0, 0);
|
||||||
return *out != INVALID_HANDLE_VALUE;
|
return (*out != INVALID_HANDLE_VALUE) ? ERROR_SUCCESS : GetLastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
int kwsysProcessCreate(kwsysProcess* cp, int index,
|
DWORD kwsysProcessCreate(kwsysProcess* cp, int index,
|
||||||
kwsysProcessCreateInformation* si)
|
kwsysProcessCreateInformation* si)
|
||||||
{
|
{
|
||||||
int res =
|
DWORD creationFlags;
|
||||||
|
DWORD error = ERROR_SUCCESS;
|
||||||
|
|
||||||
/* Create inherited copies the handles. */
|
/* Check if we are currently exiting. */
|
||||||
kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput,
|
if (!kwsysTryEnterCreateProcessSection())
|
||||||
si->hStdInput, 1) &&
|
{
|
||||||
kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput,
|
/* The Ctrl handler is currently working on exiting our process. Rather
|
||||||
si->hStdOutput, 0) &&
|
than return an error code, which could cause incorrect conclusions to be
|
||||||
kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError,
|
reached by the caller, we simply hang. (For example, a CMake try_run
|
||||||
si->hStdError, 0) &&
|
configure step might cause the project to configure wrong.) */
|
||||||
|
Sleep(INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
/* Create the child in a suspended state so we can wait until all
|
/* Create the child in a suspended state so we can wait until all
|
||||||
children have been created before running any one. */
|
children have been created before running any one. */
|
||||||
CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, CREATE_SUSPENDED, 0,
|
creationFlags = CREATE_SUSPENDED;
|
||||||
0, &si->StartupInfo, &cp->ProcessInformation[index]);
|
if (cp->CreateProcessGroup)
|
||||||
|
{
|
||||||
|
creationFlags |= CREATE_NEW_PROCESS_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create inherited copies of the handles. */
|
||||||
|
(error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput,
|
||||||
|
si->hStdInput, 1)) ||
|
||||||
|
(error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput,
|
||||||
|
si->hStdOutput, 0)) ||
|
||||||
|
(error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError,
|
||||||
|
si->hStdError, 0)) ||
|
||||||
|
/* Create the process. */
|
||||||
|
(!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0,
|
||||||
|
0, &si->StartupInfo, &cp->ProcessInformation[index]) &&
|
||||||
|
(error = GetLastError()));
|
||||||
|
|
||||||
/* Close the inherited copies of the handles. */
|
/* Close the inherited copies of the handles. */
|
||||||
if (si->StartupInfo.hStdInput != si->hStdInput)
|
if (si->StartupInfo.hStdInput != si->hStdInput)
|
||||||
|
@ -1757,7 +1857,23 @@ int kwsysProcessCreate(kwsysProcess* cp, int index,
|
||||||
kwsysProcessCleanupHandle(&si->StartupInfo.hStdError);
|
kwsysProcessCleanupHandle(&si->StartupInfo.hStdError);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
/* Add the process to the global list of processes. */
|
||||||
|
if (!error &&
|
||||||
|
!kwsysProcessesAdd(cp->ProcessInformation[index].hProcess,
|
||||||
|
cp->ProcessInformation[index].dwProcessId, cp->CreateProcessGroup))
|
||||||
|
{
|
||||||
|
/* This failed for some reason. Kill the suspended process. */
|
||||||
|
TerminateProcess(cp->ProcessInformation[index].hProcess, 1);
|
||||||
|
/* And clean up... */
|
||||||
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
|
||||||
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hThread);
|
||||||
|
strcpy(cp->ErrorMessage, "kwsysProcessesAdd function failed");
|
||||||
|
error = ERROR_NOT_ENOUGH_MEMORY; /* Most likely reason. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the console Ctrl handler is waiting for us, this will release it... */
|
||||||
|
kwsysLeaveCreateProcessSection();
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
@ -1779,6 +1895,9 @@ void kwsysProcessDestroy(kwsysProcess* cp, int event)
|
||||||
GetExitCodeProcess(cp->ProcessInformation[index].hProcess,
|
GetExitCodeProcess(cp->ProcessInformation[index].hProcess,
|
||||||
&cp->CommandExitCodes[index]);
|
&cp->CommandExitCodes[index]);
|
||||||
|
|
||||||
|
/* Remove from global list of processes. */
|
||||||
|
kwsysProcessesRemove(cp->ProcessInformation[index].hProcess);
|
||||||
|
|
||||||
/* Close the process handle for the terminated process. */
|
/* Close the process handle for the terminated process. */
|
||||||
kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
|
||||||
|
|
||||||
|
@ -1813,13 +1932,14 @@ void kwsysProcessDestroy(kwsysProcess* cp, int event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name)
|
DWORD kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name)
|
||||||
{
|
{
|
||||||
HANDLE fout;
|
HANDLE fout;
|
||||||
wchar_t* wname;
|
wchar_t* wname;
|
||||||
|
DWORD error;
|
||||||
if(!name)
|
if(!name)
|
||||||
{
|
{
|
||||||
return 1;
|
return ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close the existing handle. */
|
/* Close the existing handle. */
|
||||||
|
@ -1829,15 +1949,16 @@ int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name)
|
||||||
wname = kwsysEncoding_DupToWide(name);
|
wname = kwsysEncoding_DupToWide(name);
|
||||||
fout = CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, 0,
|
fout = CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, 0,
|
||||||
CREATE_ALWAYS, 0, 0);
|
CREATE_ALWAYS, 0, 0);
|
||||||
|
error = GetLastError();
|
||||||
free(wname);
|
free(wname);
|
||||||
if(fout == INVALID_HANDLE_VALUE)
|
if(fout == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
return 0;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assign the replacement handle. */
|
/* Assign the replacement handle. */
|
||||||
*phandle = fout;
|
*phandle = fout;
|
||||||
return 1;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
@ -1876,7 +1997,7 @@ void kwsysProcessCleanupHandle(PHANDLE h)
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* Close all handles created by kwsysProcess_Execute. */
|
/* Close all handles created by kwsysProcess_Execute. */
|
||||||
void kwsysProcessCleanup(kwsysProcess* cp, int error)
|
void kwsysProcessCleanup(kwsysProcess* cp, DWORD error)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
/* If this is an error case, report the error. */
|
/* If this is an error case, report the error. */
|
||||||
|
@ -1886,21 +2007,27 @@ void kwsysProcessCleanup(kwsysProcess* cp, int error)
|
||||||
if(cp->ErrorMessage[0] == 0)
|
if(cp->ErrorMessage[0] == 0)
|
||||||
{
|
{
|
||||||
/* Format the error message. */
|
/* Format the error message. */
|
||||||
DWORD original = GetLastError();
|
|
||||||
wchar_t err_msg[KWSYSPE_PIPE_BUFFER_SIZE];
|
wchar_t err_msg[KWSYSPE_PIPE_BUFFER_SIZE];
|
||||||
DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
|
DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS, 0, original,
|
FORMAT_MESSAGE_IGNORE_INSERTS, 0, error,
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
err_msg, KWSYSPE_PIPE_BUFFER_SIZE, 0);
|
err_msg, KWSYSPE_PIPE_BUFFER_SIZE, 0);
|
||||||
WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage,
|
|
||||||
KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL);
|
|
||||||
if(length < 1)
|
if(length < 1)
|
||||||
{
|
{
|
||||||
/* FormatMessage failed. Use a default message. */
|
/* FormatMessage failed. Use a default message. */
|
||||||
_snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
|
_snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
|
||||||
"Process execution failed with error 0x%X. "
|
"Process execution failed with error 0x%X. "
|
||||||
"FormatMessage failed with error 0x%X",
|
"FormatMessage failed with error 0x%X",
|
||||||
original, GetLastError());
|
error, GetLastError());
|
||||||
|
}
|
||||||
|
if(!WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage,
|
||||||
|
KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL))
|
||||||
|
{
|
||||||
|
/* WideCharToMultiByte failed. Use a default message. */
|
||||||
|
_snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
|
||||||
|
"Process execution failed with error 0x%X. "
|
||||||
|
"WideCharToMultiByte failed with error 0x%X",
|
||||||
|
error, GetLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1923,6 +2050,8 @@ void kwsysProcessCleanup(kwsysProcess* cp, int error)
|
||||||
}
|
}
|
||||||
for(i=0; i < cp->NumberOfCommands; ++i)
|
for(i=0; i < cp->NumberOfCommands; ++i)
|
||||||
{
|
{
|
||||||
|
/* Remove from global list of processes and close handles. */
|
||||||
|
kwsysProcessesRemove(cp->ProcessInformation[i].hProcess);
|
||||||
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
|
||||||
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
|
kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
|
||||||
}
|
}
|
||||||
|
@ -2659,3 +2788,230 @@ static void kwsysProcessDisablePipeThreads(kwsysProcess* cp)
|
||||||
ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
|
ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* Global set of executing processes for use by the Ctrl handler.
|
||||||
|
This global instance will be zero-initialized by the compiler.
|
||||||
|
|
||||||
|
Note that the console Ctrl handler runs on a background thread and so
|
||||||
|
everything it does must be thread safe. Here, we track the hProcess
|
||||||
|
HANDLEs directly instead of kwsysProcess instances, so that we don't have
|
||||||
|
to make kwsysProcess thread safe. */
|
||||||
|
typedef struct kwsysProcessInstance_s
|
||||||
|
{
|
||||||
|
HANDLE hProcess;
|
||||||
|
DWORD dwProcessId;
|
||||||
|
int NewProcessGroup; /* Whether the process was created in a new group. */
|
||||||
|
} kwsysProcessInstance;
|
||||||
|
|
||||||
|
typedef struct kwsysProcessInstances_s
|
||||||
|
{
|
||||||
|
/* Whether we have initialized key fields below, like critical sections. */
|
||||||
|
int Initialized;
|
||||||
|
|
||||||
|
/* Ctrl handler runs on a different thread, so we must sync access. */
|
||||||
|
CRITICAL_SECTION Lock;
|
||||||
|
|
||||||
|
int Exiting;
|
||||||
|
size_t Count;
|
||||||
|
size_t Size;
|
||||||
|
kwsysProcessInstance* Processes;
|
||||||
|
} kwsysProcessInstances;
|
||||||
|
static kwsysProcessInstances kwsysProcesses;
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* Initialize critial section and set up console Ctrl handler. You MUST call
|
||||||
|
this before using any other kwsysProcesses* functions below. */
|
||||||
|
static int kwsysProcessesInitialize(void)
|
||||||
|
{
|
||||||
|
/* Initialize everything if not done already. */
|
||||||
|
if(!kwsysProcesses.Initialized)
|
||||||
|
{
|
||||||
|
InitializeCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
|
||||||
|
/* Set up console ctrl handler. */
|
||||||
|
if(!SetConsoleCtrlHandler(kwsysCtrlHandler, TRUE))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kwsysProcesses.Initialized = 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* The Ctrl handler waits on the global list of processes. To prevent an
|
||||||
|
orphaned process, do not create a new process if the Ctrl handler is
|
||||||
|
already running. Do so by using this function to check if it is ok to
|
||||||
|
create a process. */
|
||||||
|
static int kwsysTryEnterCreateProcessSection(void)
|
||||||
|
{
|
||||||
|
/* Enter main critical section; this means creating a process and the Ctrl
|
||||||
|
handler are mutually exclusive. */
|
||||||
|
EnterCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
/* Indicate to the caller if they can create a process. */
|
||||||
|
if(kwsysProcesses.Exiting)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* Matching function on successful kwsysTryEnterCreateProcessSection return.
|
||||||
|
Make sure you called kwsysProcessesAdd if applicable before calling this.*/
|
||||||
|
static void kwsysLeaveCreateProcessSection(void)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* Add new process to global process list. The Ctrl handler will wait for
|
||||||
|
the process to exit before it returns. Do not close the process handle
|
||||||
|
until after calling kwsysProcessesRemove. The newProcessGroup parameter
|
||||||
|
must be set if the process was created with CREATE_NEW_PROCESS_GROUP. */
|
||||||
|
static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessid,
|
||||||
|
int newProcessGroup)
|
||||||
|
{
|
||||||
|
if(!kwsysProcessesInitialize() || !hProcess ||
|
||||||
|
hProcess == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter the critical section. */
|
||||||
|
EnterCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
|
||||||
|
/* Make sure there is enough space for the new process handle. */
|
||||||
|
if(kwsysProcesses.Count == kwsysProcesses.Size)
|
||||||
|
{
|
||||||
|
size_t newSize;
|
||||||
|
kwsysProcessInstance *newArray;
|
||||||
|
/* Start with enough space for a small number of process handles
|
||||||
|
and double the size each time more is needed. */
|
||||||
|
newSize = kwsysProcesses.Size? kwsysProcesses.Size*2 : 4;
|
||||||
|
|
||||||
|
/* Try allocating the new block of memory. */
|
||||||
|
if(newArray = (kwsysProcessInstance*)malloc(
|
||||||
|
newSize*sizeof(kwsysProcessInstance)))
|
||||||
|
{
|
||||||
|
/* Copy the old process handles to the new memory. */
|
||||||
|
if(kwsysProcesses.Count > 0)
|
||||||
|
{
|
||||||
|
memcpy(newArray, kwsysProcesses.Processes,
|
||||||
|
kwsysProcesses.Count * sizeof(kwsysProcessInstance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Failed to allocate memory for the new process handle set. */
|
||||||
|
LeaveCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free original array. */
|
||||||
|
free(kwsysProcesses.Processes);
|
||||||
|
|
||||||
|
/* Update original structure with new allocation. */
|
||||||
|
kwsysProcesses.Size = newSize;
|
||||||
|
kwsysProcesses.Processes = newArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append the new process information to the set. */
|
||||||
|
kwsysProcesses.Processes[kwsysProcesses.Count].hProcess = hProcess;
|
||||||
|
kwsysProcesses.Processes[kwsysProcesses.Count].dwProcessId = dwProcessid;
|
||||||
|
kwsysProcesses.Processes[kwsysProcesses.Count++].NewProcessGroup =
|
||||||
|
newProcessGroup;
|
||||||
|
|
||||||
|
/* Leave critical section and return success. */
|
||||||
|
LeaveCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
/* Removes process to global process list. */
|
||||||
|
static void kwsysProcessesRemove(HANDLE hProcess)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!hProcess || hProcess == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
|
||||||
|
/* Find the given process in the set. */
|
||||||
|
for(i=0; i < kwsysProcesses.Count; ++i)
|
||||||
|
{
|
||||||
|
if(kwsysProcesses.Processes[i].hProcess == hProcess)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i < kwsysProcesses.Count)
|
||||||
|
{
|
||||||
|
/* Found it! Remove the process from the set. */
|
||||||
|
--kwsysProcesses.Count;
|
||||||
|
for(; i < kwsysProcesses.Count; ++i)
|
||||||
|
{
|
||||||
|
kwsysProcesses.Processes[i] = kwsysProcesses.Processes[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this was the last process, free the array. */
|
||||||
|
if(kwsysProcesses.Count == 0)
|
||||||
|
{
|
||||||
|
kwsysProcesses.Size = 0;
|
||||||
|
free(kwsysProcesses.Processes);
|
||||||
|
kwsysProcesses.Processes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
(void)dwCtrlType;
|
||||||
|
/* Enter critical section. */
|
||||||
|
EnterCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
|
||||||
|
/* Set flag indicating that we are exiting. */
|
||||||
|
kwsysProcesses.Exiting = 1;
|
||||||
|
|
||||||
|
/* If some of our processes were created in a new process group, we must
|
||||||
|
manually interrupt them. They won't otherwise receive a Ctrl+C/Break. */
|
||||||
|
for(i=0; i < kwsysProcesses.Count; ++i)
|
||||||
|
{
|
||||||
|
if(kwsysProcesses.Processes[i].NewProcessGroup)
|
||||||
|
{
|
||||||
|
DWORD groupId = kwsysProcesses.Processes[i].dwProcessId;
|
||||||
|
if(groupId)
|
||||||
|
{
|
||||||
|
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, groupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for each child process to exit. This is the key step that prevents
|
||||||
|
us from leaving several orphaned children processes running in the
|
||||||
|
background when the user presses Ctrl+C. */
|
||||||
|
for(i=0; i < kwsysProcesses.Count; ++i)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(kwsysProcesses.Processes[i].hProcess, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Leave critical section. */
|
||||||
|
LeaveCriticalSection(&kwsysProcesses.Lock);
|
||||||
|
|
||||||
|
/* Continue on to default Ctrl handler (which calls ExitProcess). */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
269
testProcess.c
269
testProcess.c
|
@ -29,26 +29,48 @@
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#else
|
#else
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
|
# include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__BORLANDC__)
|
#if defined(__BORLANDC__)
|
||||||
# pragma warn -8060 /* possibly incorrect assignment */
|
# pragma warn -8060 /* possibly incorrect assignment */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Platform-specific sleep functions. */
|
||||||
|
|
||||||
#if defined(__BEOS__) && !defined(__ZETA__)
|
#if defined(__BEOS__) && !defined(__ZETA__)
|
||||||
/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
|
/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
|
||||||
# include <be/kernel/OS.h>
|
# include <be/kernel/OS.h>
|
||||||
static inline void testProcess_usleep(unsigned int msec)
|
static inline void testProcess_usleep(unsigned int usec)
|
||||||
{
|
{
|
||||||
snooze(msec);
|
snooze(usec);
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
/* Windows can only sleep in millisecond intervals. */
|
||||||
|
static void testProcess_usleep(unsigned int usec)
|
||||||
|
{
|
||||||
|
Sleep(usec / 1000);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
# define testProcess_usleep usleep
|
# define testProcess_usleep usleep
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
static void testProcess_sleep(unsigned int sec)
|
||||||
|
{
|
||||||
|
Sleep(sec*1000);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void testProcess_sleep(unsigned int sec)
|
||||||
|
{
|
||||||
|
sleep(sec);
|
||||||
|
}
|
||||||
|
#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, int createNewGroup,
|
||||||
|
unsigned int interruptDelay);
|
||||||
|
|
||||||
static int test1(int argc, const char* argv[])
|
static int test1(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
|
@ -73,11 +95,7 @@ static int test3(int argc, const char* argv[])
|
||||||
fprintf(stderr, "Output before sleep on stderr from timeout test.\n");
|
fprintf(stderr, "Output before sleep on stderr from timeout test.\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
#if defined(_WIN32)
|
testProcess_sleep(15);
|
||||||
Sleep(15000);
|
|
||||||
#else
|
|
||||||
sleep(15);
|
|
||||||
#endif
|
|
||||||
fprintf(stdout, "Output after sleep on stdout from timeout test.\n");
|
fprintf(stdout, "Output after sleep on stdout from timeout test.\n");
|
||||||
fprintf(stderr, "Output after sleep on stderr from timeout test.\n");
|
fprintf(stderr, "Output after sleep on stderr from timeout test.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -102,7 +120,7 @@ static int test4(int argc, const char* argv[])
|
||||||
#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");
|
||||||
fprintf(stderr, "Output before crash on stderr from crash test.\n");
|
fprintf(stderr, "Output before crash on stderr from crash test.\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
assert(invalidAddress); /* Quiet Clang scan-build. */
|
assert(invalidAddress); /* Quiet Clang scan-build. */
|
||||||
|
@ -127,7 +145,7 @@ static int test5(int argc, const char* argv[])
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
r = runChild(cmd, kwsysProcess_State_Exception,
|
r = runChild(cmd, kwsysProcess_State_Exception,
|
||||||
kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0);
|
kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0, 0, 0);
|
||||||
fprintf(stdout, "Output on stdout after recursive test.\n");
|
fprintf(stdout, "Output on stdout after recursive test.\n");
|
||||||
fprintf(stderr, "Output on stderr after recursive test.\n");
|
fprintf(stderr, "Output on stderr after recursive test.\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -168,11 +186,7 @@ static int test7(int argc, const char* argv[])
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
/* Sleep for 1 second. */
|
/* Sleep for 1 second. */
|
||||||
#if defined(_WIN32)
|
testProcess_sleep(1);
|
||||||
Sleep(1000);
|
|
||||||
#else
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
|
||||||
fprintf(stdout, "Output on stdout after sleep.\n");
|
fprintf(stdout, "Output on stdout after sleep.\n");
|
||||||
fprintf(stderr, "Output on stderr after sleep.\n");
|
fprintf(stderr, "Output on stderr after sleep.\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -196,7 +210,7 @@ static int test8(int argc, const char* argv[])
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None,
|
r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None,
|
||||||
1, 1, 1, 0, 10, 0, 1, 1);
|
1, 1, 1, 0, 10, 0, 1, 1, 0, 0);
|
||||||
fprintf(stdout, "Output on stdout after grandchild test.\n");
|
fprintf(stdout, "Output on stdout after grandchild test.\n");
|
||||||
fprintf(stderr, "Output on stderr after grandchild test.\n");
|
fprintf(stderr, "Output on stderr after grandchild test.\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -217,18 +231,137 @@ static int test8_grandchild(int argc, const char* argv[])
|
||||||
implemented. */
|
implemented. */
|
||||||
fclose(stdout);
|
fclose(stdout);
|
||||||
fclose(stderr);
|
fclose(stderr);
|
||||||
|
testProcess_sleep(15);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test9(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
/* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
|
||||||
|
process. Here, we start a child process that sleeps for a long time
|
||||||
|
while ignoring signals. The test is successful if this process waits
|
||||||
|
for the child to return before exiting from the Ctrl+C handler.
|
||||||
|
|
||||||
|
WARNING: This test will falsely pass if the share parameter of runChild
|
||||||
|
was set to 0 when invoking the test9 process. */
|
||||||
|
int r;
|
||||||
|
const char* cmd[4];
|
||||||
|
(void)argc;
|
||||||
|
cmd[0] = argv[0];
|
||||||
|
cmd[1] = "run";
|
||||||
|
cmd[2] = "109";
|
||||||
|
cmd[3] = 0;
|
||||||
|
fprintf(stdout, "Output on stdout before grandchild test.\n");
|
||||||
|
fprintf(stderr, "Output on stderr before grandchild test.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
r = runChild(cmd, kwsysProcess_State_Exited,
|
||||||
|
kwsysProcess_Exception_None,
|
||||||
|
0, 1, 1, 0, 30, 0, 1, 0, 0, 0);
|
||||||
|
/* This sleep will avoid a race condition between this function exiting
|
||||||
|
normally and our Ctrl+C handler exiting abnormally after the process
|
||||||
|
exits. */
|
||||||
|
testProcess_sleep(1);
|
||||||
|
fprintf(stdout, "Output on stdout after grandchild test.\n");
|
||||||
|
fprintf(stderr, "Output on stderr after grandchild test.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
Sleep(15000);
|
static BOOL WINAPI test9_grandchild_handler(DWORD dwCtrlType)
|
||||||
#else
|
{
|
||||||
sleep(15);
|
/* Ignore all Ctrl+C/Break signals. We must use an actual handler function
|
||||||
|
instead of using SetConsoleCtrlHandler(NULL, TRUE) so that we can also
|
||||||
|
ignore Ctrl+Break in addition to Ctrl+C. */
|
||||||
|
(void)dwCtrlType;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int test9_grandchild(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
/* The grandchild just sleeps for a few seconds while ignoring signals. */
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if(!SetConsoleCtrlHandler(test9_grandchild_handler, TRUE))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct sigaction sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = SIG_IGN;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
if(sigaction(SIGINT, &sa, 0) < 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
|
||||||
|
fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
/* Sleep for 9 seconds. */
|
||||||
|
testProcess_sleep(9);
|
||||||
|
fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
|
||||||
|
fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test10(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
/* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
|
||||||
|
process. Here, we start a child process that sleeps for a long time and
|
||||||
|
processes signals normally. However, this grandchild is created in a new
|
||||||
|
process group - ensuring that Ctrl+C we receive is sent to our process
|
||||||
|
groups. We make sure it exits anyway. */
|
||||||
|
int r;
|
||||||
|
const char* cmd[4];
|
||||||
|
(void)argc;
|
||||||
|
cmd[0] = argv[0];
|
||||||
|
cmd[1] = "run";
|
||||||
|
cmd[2] = "110";
|
||||||
|
cmd[3] = 0;
|
||||||
|
fprintf(stdout, "Output on stdout before grandchild test.\n");
|
||||||
|
fprintf(stderr, "Output on stderr before grandchild test.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
r = runChild(cmd, kwsysProcess_State_Exception,
|
||||||
|
kwsysProcess_Exception_Interrupt,
|
||||||
|
0, 1, 1, 0, 30, 0, 1, 0, 1, 0);
|
||||||
|
fprintf(stdout, "Output on stdout after grandchild test.\n");
|
||||||
|
fprintf(stderr, "Output on stderr after grandchild test.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test10_grandchild(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
/* The grandchild just sleeps for a few seconds and handles signals. */
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
|
||||||
|
fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
/* Sleep for 6 seconds. */
|
||||||
|
testProcess_sleep(6);
|
||||||
|
fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
|
||||||
|
fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int runChild2(kwsysProcess* kp,
|
static int runChild2(kwsysProcess* kp,
|
||||||
const char* cmd[], int state, int exception, int value,
|
const char* cmd[], int state, int exception, int value,
|
||||||
int share, int output, int delay, double timeout,
|
int share, int output, int delay, double timeout,
|
||||||
int poll, int disown)
|
int poll, int disown, int createNewGroup,
|
||||||
|
unsigned int interruptDelay)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
char* data = 0;
|
char* data = 0;
|
||||||
|
@ -249,6 +382,10 @@ static int runChild2(kwsysProcess* kp,
|
||||||
{
|
{
|
||||||
kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1);
|
kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1);
|
||||||
}
|
}
|
||||||
|
if(createNewGroup)
|
||||||
|
{
|
||||||
|
kwsysProcess_SetOption(kp, kwsysProcess_Option_CreateProcessGroup, 1);
|
||||||
|
}
|
||||||
kwsysProcess_Execute(kp);
|
kwsysProcess_Execute(kp);
|
||||||
|
|
||||||
if(poll)
|
if(poll)
|
||||||
|
@ -256,6 +393,12 @@ static int runChild2(kwsysProcess* kp,
|
||||||
pUserTimeout = &userTimeout;
|
pUserTimeout = &userTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(interruptDelay)
|
||||||
|
{
|
||||||
|
testProcess_sleep(interruptDelay);
|
||||||
|
kwsysProcess_Interrupt(kp);
|
||||||
|
}
|
||||||
|
|
||||||
if(!share && !disown)
|
if(!share && !disown)
|
||||||
{
|
{
|
||||||
int p;
|
int p;
|
||||||
|
@ -286,17 +429,13 @@ static int runChild2(kwsysProcess* kp,
|
||||||
if(poll)
|
if(poll)
|
||||||
{
|
{
|
||||||
/* Delay to avoid busy loop during polling. */
|
/* Delay to avoid busy loop during polling. */
|
||||||
#if defined(_WIN32)
|
|
||||||
Sleep(100);
|
|
||||||
#else
|
|
||||||
testProcess_usleep(100000);
|
testProcess_usleep(100000);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if(delay)
|
if(delay)
|
||||||
{
|
{
|
||||||
/* Purposely sleeping only on Win32 to let pipe fill up. */
|
/* Purposely sleeping only on Win32 to let pipe fill up. */
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
Sleep(100);
|
testProcess_usleep(100000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,7 +476,7 @@ static int runChild2(kwsysProcess* kp,
|
||||||
printf("Error in administrating child process: [%s]\n",
|
printf("Error in administrating child process: [%s]\n",
|
||||||
kwsysProcess_GetErrorString(kp)); break;
|
kwsysProcess_GetErrorString(kp)); break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
{
|
{
|
||||||
if(exception != kwsysProcess_GetExitException(kp))
|
if(exception != kwsysProcess_GetExitException(kp))
|
||||||
|
@ -353,7 +492,7 @@ static int runChild2(kwsysProcess* kp,
|
||||||
value, kwsysProcess_GetExitValue(kp));
|
value, kwsysProcess_GetExitValue(kp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(kwsysProcess_GetState(kp) != state)
|
if(kwsysProcess_GetState(kp) != state)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Mismatch in state. "
|
fprintf(stderr, "Mismatch in state. "
|
||||||
|
@ -374,9 +513,37 @@ static int runChild2(kwsysProcess* kp,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a child process and blocks until it returns. Arguments as follows:
|
||||||
|
*
|
||||||
|
* cmd = Command line to run.
|
||||||
|
* state = Expected return value of kwsysProcess_GetState after exit.
|
||||||
|
* exception = Expected return value of kwsysProcess_GetExitException.
|
||||||
|
* value = Expected return value of kwsysProcess_GetExitValue.
|
||||||
|
* share = Whether to share stdout/stderr child pipes with our pipes
|
||||||
|
* by way of kwsysProcess_SetPipeShared. If false, new pipes
|
||||||
|
* are created.
|
||||||
|
* output = If !share && !disown, whether to write the child's stdout
|
||||||
|
* and stderr output to our stdout.
|
||||||
|
* delay = If !share && !disown, adds an additional short delay to
|
||||||
|
* the pipe loop to allow the pipes to fill up; Windows only.
|
||||||
|
* timeout = Non-zero to sets a timeout in seconds via
|
||||||
|
* kwsysProcess_SetTimeout.
|
||||||
|
* poll = If !share && !disown, we count the number of 0.1 second
|
||||||
|
* intervals where the child pipes had no new data. We fail
|
||||||
|
* if not in the bounds of MINPOLL/MAXPOLL.
|
||||||
|
* repeat = Number of times to run the process.
|
||||||
|
* disown = If set, the process is disowned.
|
||||||
|
* createNewGroup = If set, the process is created in a new process group.
|
||||||
|
* interruptDelay = If non-zero, number of seconds to delay before
|
||||||
|
* interrupting the process. Note that this delay will occur
|
||||||
|
* BEFORE any reading/polling of pipes occurs and before any
|
||||||
|
* detachment occurs.
|
||||||
|
*/
|
||||||
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 share, int output, int delay, double timeout,
|
||||||
int poll, int repeat, int disown)
|
int poll, int repeat, int disown, int createNewGroup,
|
||||||
|
unsigned int interruptDelay)
|
||||||
{
|
{
|
||||||
int result = 1;
|
int result = 1;
|
||||||
kwsysProcess* kp = kwsysProcess_New();
|
kwsysProcess* kp = kwsysProcess_New();
|
||||||
|
@ -388,7 +555,8 @@ int runChild(const char* cmd[], int state, int exception, int value,
|
||||||
while(repeat-- > 0)
|
while(repeat-- > 0)
|
||||||
{
|
{
|
||||||
result = runChild2(kp, cmd, state, exception, value, share,
|
result = runChild2(kp, cmd, state, exception, value, share,
|
||||||
output, delay, timeout, poll, disown);
|
output, delay, timeout, poll, disown, createNewGroup,
|
||||||
|
interruptDelay);
|
||||||
}
|
}
|
||||||
kwsysProcess_Delete(kp);
|
kwsysProcess_Delete(kp);
|
||||||
return result;
|
return result;
|
||||||
|
@ -435,7 +603,7 @@ int main(int argc, const char* argv[])
|
||||||
n = atoi(argv[2]);
|
n = atoi(argv[2]);
|
||||||
}
|
}
|
||||||
/* Check arguments. */
|
/* Check arguments. */
|
||||||
if(((n >= 1 && n <= 8) || n == 108) && argc == 3)
|
if(((n >= 1 && n <= 10) || n == 108 || n == 109 || n == 110) && argc == 3)
|
||||||
{
|
{
|
||||||
/* This is the child process for a requested test number. */
|
/* This is the child process for a requested test number. */
|
||||||
switch (n)
|
switch (n)
|
||||||
|
@ -448,15 +616,19 @@ int main(int argc, const char* argv[])
|
||||||
case 6: test6(argc, argv); return 0;
|
case 6: test6(argc, argv); return 0;
|
||||||
case 7: return test7(argc, argv);
|
case 7: return test7(argc, argv);
|
||||||
case 8: return test8(argc, argv);
|
case 8: return test8(argc, argv);
|
||||||
|
case 9: return test9(argc, argv);
|
||||||
|
case 10: return test10(argc, argv);
|
||||||
case 108: return test8_grandchild(argc, argv);
|
case 108: return test8_grandchild(argc, argv);
|
||||||
|
case 109: return test9_grandchild(argc, argv);
|
||||||
|
case 110: return test10_grandchild(argc, argv);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "Invalid test number %d.\n", n);
|
fprintf(stderr, "Invalid test number %d.\n", n);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else if(n >= 1 && n <= 8)
|
else if(n >= 1 && n <= 10)
|
||||||
{
|
{
|
||||||
/* This is the parent process for a requested test number. */
|
/* This is the parent process for a requested test number. */
|
||||||
int states[8] =
|
int states[10] =
|
||||||
{
|
{
|
||||||
kwsysProcess_State_Exited,
|
kwsysProcess_State_Exited,
|
||||||
kwsysProcess_State_Exited,
|
kwsysProcess_State_Exited,
|
||||||
|
@ -465,9 +637,11 @@ int main(int argc, const char* argv[])
|
||||||
kwsysProcess_State_Exited,
|
kwsysProcess_State_Exited,
|
||||||
kwsysProcess_State_Expired,
|
kwsysProcess_State_Expired,
|
||||||
kwsysProcess_State_Exited,
|
kwsysProcess_State_Exited,
|
||||||
kwsysProcess_State_Exited
|
kwsysProcess_State_Exited,
|
||||||
|
kwsysProcess_State_Expired, /* Ctrl+C handler test */
|
||||||
|
kwsysProcess_State_Exception /* Process group test */
|
||||||
};
|
};
|
||||||
int exceptions[8] =
|
int exceptions[10] =
|
||||||
{
|
{
|
||||||
kwsysProcess_Exception_None,
|
kwsysProcess_Exception_None,
|
||||||
kwsysProcess_Exception_None,
|
kwsysProcess_Exception_None,
|
||||||
|
@ -476,14 +650,19 @@ int main(int argc, const char* argv[])
|
||||||
kwsysProcess_Exception_None,
|
kwsysProcess_Exception_None,
|
||||||
kwsysProcess_Exception_None,
|
kwsysProcess_Exception_None,
|
||||||
kwsysProcess_Exception_None,
|
kwsysProcess_Exception_None,
|
||||||
kwsysProcess_Exception_None
|
kwsysProcess_Exception_None,
|
||||||
|
kwsysProcess_Exception_None,
|
||||||
|
kwsysProcess_Exception_Interrupt
|
||||||
};
|
};
|
||||||
int values[8] = {0, 123, 1, 1, 0, 0, 0, 0};
|
int values[10] = {0, 123, 1, 1, 0, 0, 0, 0, 1, 1};
|
||||||
int outputs[8] = {1, 1, 1, 1, 1, 0, 1, 1};
|
int shares[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1};
|
||||||
int delays[8] = {0, 0, 0, 0, 0, 1, 0, 0};
|
int outputs[10] = {1, 1, 1, 1, 1, 0, 1, 1, 1, 1};
|
||||||
double timeouts[8] = {10, 10, 10, 30, 30, 10, -1, 10};
|
int delays[10] = {0, 0, 0, 0, 0, 1, 0, 0, 0, 0};
|
||||||
int polls[8] = {0, 0, 0, 0, 0, 0, 1, 0};
|
double timeouts[10] = {10, 10, 10, 30, 30, 10, -1, 10, 6, 4};
|
||||||
int repeat[8] = {2, 1, 1, 1, 1, 1, 1, 1};
|
int polls[10] = {0, 0, 0, 0, 0, 0, 1, 0, 0, 0};
|
||||||
|
int repeat[10] = {2, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||||
|
int createNewGroups[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1};
|
||||||
|
unsigned int interruptDelays[10] = {0, 0, 0, 0, 0, 0, 0, 0, 3, 2};
|
||||||
int r;
|
int r;
|
||||||
const char* cmd[4];
|
const char* cmd[4];
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -515,9 +694,10 @@ int main(int argc, const char* argv[])
|
||||||
fprintf(stderr, "Output on stderr before test %d.\n", n);
|
fprintf(stderr, "Output on stderr before test %d.\n", n);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], 0,
|
r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], shares[n-1],
|
||||||
outputs[n-1], delays[n-1], timeouts[n-1],
|
outputs[n-1], delays[n-1], timeouts[n-1],
|
||||||
polls[n-1], repeat[n-1], 0);
|
polls[n-1], repeat[n-1], 0, createNewGroups[n-1],
|
||||||
|
interruptDelays[n-1]);
|
||||||
fprintf(stdout, "Output on stdout after test %d.\n", n);
|
fprintf(stdout, "Output on stdout after test %d.\n", n);
|
||||||
fprintf(stderr, "Output on stderr after test %d.\n", n);
|
fprintf(stderr, "Output on stderr after test %d.\n", n);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
@ -536,7 +716,8 @@ int main(int argc, const char* argv[])
|
||||||
int exception = kwsysProcess_Exception_None;
|
int exception = kwsysProcess_Exception_None;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
double timeout = 0;
|
double timeout = 0;
|
||||||
int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1, 0);
|
int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout,
|
||||||
|
0, 1, 0, 0, 0);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue