| /*============================================================================ |
| KWSys - Kitware System Library |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "kwsysPrivate.h" |
| #include KWSYS_HEADER(Process.h) |
| #include KWSYS_HEADER(System.h) |
| |
| /* Work-around CMake dependency scanning limitation. This must |
| duplicate the above list of headers. */ |
| #if 0 |
| # include "Process.h.in" |
| # include "System.h.in" |
| #endif |
| |
| /* |
| |
| Implementation for UNIX |
| |
| On UNIX, a child process is forked to exec the program. Three output |
| pipes are read by the parent process using a select call to block |
| until data are ready. Two of the pipes are stdout and stderr for the |
| child. The third is a special pipe populated by a signal handler to |
| indicate that a child has terminated. This is used in conjunction |
| with the timeout on the select call to implement a timeout for program |
| even when it closes stdout and stderr and at the same time avoiding |
| races. |
| |
| */ |
| |
| |
| /* |
| |
| TODO: |
| |
| We cannot create the pipeline of processes in suspended states. How |
| do we cleanup processes already started when one fails to load? Right |
| now we are just killing them, which is probably not the right thing to |
| do. |
| |
| */ |
| |
| #if defined(__CYGWIN__) |
| /* Increase the file descriptor limit for select() before including |
| related system headers. (Default: 64) */ |
| # define FD_SETSIZE 16384 |
| #endif |
| |
| #include <stddef.h> /* ptrdiff_t */ |
| #include <stdio.h> /* snprintf */ |
| #include <stdlib.h> /* malloc, free */ |
| #include <string.h> /* strdup, strerror, memset */ |
| #include <sys/time.h> /* struct timeval */ |
| #include <sys/types.h> /* pid_t, fd_set */ |
| #include <sys/wait.h> /* waitpid */ |
| #include <sys/stat.h> /* open mode */ |
| #include <unistd.h> /* pipe, close, fork, execvp, select, _exit */ |
| #include <fcntl.h> /* fcntl */ |
| #include <errno.h> /* errno */ |
| #include <time.h> /* gettimeofday */ |
| #include <signal.h> /* sigaction */ |
| #include <dirent.h> /* DIR, dirent */ |
| #include <ctype.h> /* isspace */ |
| #include <assert.h> /* assert */ |
| |
| #if defined(__VMS) |
| # define KWSYSPE_VMS_NONBLOCK , O_NONBLOCK |
| #else |
| # define KWSYSPE_VMS_NONBLOCK |
| #endif |
| |
| #if defined(KWSYS_C_HAS_PTRDIFF_T) && KWSYS_C_HAS_PTRDIFF_T |
| typedef ptrdiff_t kwsysProcess_ptrdiff_t; |
| #else |
| typedef int kwsysProcess_ptrdiff_t; |
| #endif |
| |
| #if defined(KWSYS_C_HAS_SSIZE_T) && KWSYS_C_HAS_SSIZE_T |
| typedef ssize_t kwsysProcess_ssize_t; |
| #else |
| typedef int kwsysProcess_ssize_t; |
| #endif |
| |
| #if defined(__BEOS__) && !defined(__ZETA__) |
| /* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */ |
| # include <be/kernel/OS.h> |
| static inline void kwsysProcess_usleep(unsigned int msec) |
| { |
| snooze(msec); |
| } |
| #else |
| # define kwsysProcess_usleep usleep |
| #endif |
| |
| /* |
| * BeOS's select() works like WinSock: it's for networking only, and |
| * doesn't work with Unix file handles...socket and file handles are |
| * different namespaces (the same descriptor means different things in |
| * each context!) |
| * |
| * So on Unix-like systems where select() is flakey, we'll set the |
| * pipes' file handles to be non-blocking and just poll them directly |
| * without select(). |
| */ |
| #if !defined(__BEOS__) && !defined(__VMS) && !defined(__MINT__) |
| # define KWSYSPE_USE_SELECT 1 |
| #endif |
| |
| /* Some platforms do not have siginfo on their signal handlers. */ |
| #if defined(SA_SIGINFO) && !defined(__BEOS__) |
| # define KWSYSPE_USE_SIGINFO 1 |
| #endif |
| |
| /* The number of pipes for the child's output. The standard stdout |
| and stderr pipes are the first two. One more pipe is used to |
| detect when the child process has terminated. The third pipe is |
| not given to the child process, so it cannot close it until it |
| terminates. */ |
| #define KWSYSPE_PIPE_COUNT 3 |
| #define KWSYSPE_PIPE_STDOUT 0 |
| #define KWSYSPE_PIPE_STDERR 1 |
| #define KWSYSPE_PIPE_SIGNAL 2 |
| |
| /* The maximum amount to read from a pipe at a time. */ |
| #define KWSYSPE_PIPE_BUFFER_SIZE 1024 |
| |
| /* Keep track of times using a signed representation. Switch to the |
| native (possibly unsigned) representation only when calling native |
| functions. */ |
| typedef struct timeval kwsysProcessTimeNative; |
| typedef struct kwsysProcessTime_s kwsysProcessTime; |
| struct kwsysProcessTime_s |
| { |
| long tv_sec; |
| long tv_usec; |
| }; |
| |
| typedef struct kwsysProcessCreateInformation_s |
| { |
| int StdIn; |
| int StdOut; |
| int StdErr; |
| int ErrorPipe[2]; |
| } kwsysProcessCreateInformation; |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessVolatileFree(volatile void* p); |
| static int kwsysProcessInitialize(kwsysProcess* cp); |
| static void kwsysProcessCleanup(kwsysProcess* cp, int error); |
| static void kwsysProcessCleanupDescriptor(int* pfd); |
| static void kwsysProcessClosePipes(kwsysProcess* cp); |
| static int kwsysProcessSetNonBlocking(int fd); |
| static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, |
| kwsysProcessCreateInformation* si); |
| static void kwsysProcessDestroy(kwsysProcess* cp); |
| static int kwsysProcessSetupOutputPipeFile(int* p, const char* name); |
| static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]); |
| static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, |
| kwsysProcessTime* timeoutTime); |
| static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, |
| double* userTimeout, |
| kwsysProcessTimeNative* timeoutLength, |
| int zeroIsExpired); |
| static kwsysProcessTime kwsysProcessTimeGetCurrent(void); |
| static double kwsysProcessTimeToDouble(kwsysProcessTime t); |
| static kwsysProcessTime kwsysProcessTimeFromDouble(double d); |
| static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2); |
| static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2); |
| static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2); |
| static void kwsysProcessSetExitException(kwsysProcess* cp, int sig); |
| static void kwsysProcessChildErrorExit(int errorPipe); |
| static void kwsysProcessRestoreDefaultSignalHandlers(void); |
| static pid_t kwsysProcessFork(kwsysProcess* cp, |
| kwsysProcessCreateInformation* si); |
| static void kwsysProcessKill(pid_t process_id); |
| #if defined(__VMS) |
| static int kwsysProcessSetVMSFeature(const char* name, int value); |
| #endif |
| static int kwsysProcessesAdd(kwsysProcess* cp); |
| static void kwsysProcessesRemove(kwsysProcess* cp); |
| #if KWSYSPE_USE_SIGINFO |
| static void kwsysProcessesSignalHandler(int signum, siginfo_t* info, |
| void* ucontext); |
| #else |
| static void kwsysProcessesSignalHandler(int signum); |
| #endif |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Structure containing data used to implement the child's execution. */ |
| struct kwsysProcess_s |
| { |
| /* The command lines to execute. */ |
| char*** Commands; |
| volatile int NumberOfCommands; |
| |
| /* Descriptors for the read ends of the child's output pipes and |
| the signal pipe. */ |
| int PipeReadEnds[KWSYSPE_PIPE_COUNT]; |
| |
| /* Descriptors for the child's ends of the pipes. |
| Used temporarily during process creation. */ |
| int PipeChildStd[3]; |
| |
| /* Write descriptor for child termination signal pipe. */ |
| int SignalPipe; |
| |
| /* Buffer for pipe data. */ |
| char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE]; |
| |
| /* Process IDs returned by the calls to fork. Everything is volatile |
| 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. */ |
| int SelectError; |
| |
| /* The timeout length. */ |
| double Timeout; |
| |
| /* The working directory for the process. */ |
| char* WorkingDirectory; |
| |
| /* Whether to create the child as a detached process. */ |
| int OptionDetach; |
| |
| /* Whether the child was created as a detached process. */ |
| int Detached; |
| |
| /* Whether to treat command lines as verbatim. */ |
| int Verbatim; |
| |
| /* Whether to merge stdout/stderr of the child. */ |
| 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. */ |
| kwsysProcessTime StartTime; |
| |
| /* Time at which the child will timeout. Negative for no timeout. */ |
| kwsysProcessTime TimeoutTime; |
| |
| /* Flag for whether the timeout expired. */ |
| int TimeoutExpired; |
| |
| /* The number of pipes left open during execution. */ |
| int PipesLeft; |
| |
| #if KWSYSPE_USE_SELECT |
| /* File descriptor set for call to select. */ |
| fd_set PipeSet; |
| #endif |
| |
| /* The number of children still executing. */ |
| int CommandsLeft; |
| |
| /* The current status of the child process. Must be atomic because |
| the signal handler checks this to avoid a race. */ |
| volatile sig_atomic_t State; |
| |
| /* The exceptional behavior that terminated the child process, if |
| * any. */ |
| int ExitException; |
| |
| /* The exit code of the child process. */ |
| int ExitCode; |
| |
| /* The exit value of the child process, if any. */ |
| int ExitValue; |
| |
| /* Whether the process was killed. */ |
| volatile sig_atomic_t Killed; |
| |
| /* Buffer for error message in case of failure. */ |
| char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1]; |
| |
| /* Description for the ExitException. */ |
| char ExitExceptionString[KWSYSPE_PIPE_BUFFER_SIZE+1]; |
| |
| /* The exit codes of each child process in the pipeline. */ |
| int* CommandExitCodes; |
| |
| /* Name of files to which stdin and stdout pipes are attached. */ |
| char* PipeFileSTDIN; |
| char* PipeFileSTDOUT; |
| char* PipeFileSTDERR; |
| |
| /* Whether each pipe is shared with the parent process. */ |
| int PipeSharedSTDIN; |
| int PipeSharedSTDOUT; |
| int PipeSharedSTDERR; |
| |
| /* Native pipes provided by the user. */ |
| int PipeNativeSTDIN[2]; |
| int PipeNativeSTDOUT[2]; |
| int PipeNativeSTDERR[2]; |
| |
| /* The real working directory of this process. */ |
| int RealWorkingDirectoryLength; |
| char* RealWorkingDirectory; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysProcess* kwsysProcess_New(void) |
| { |
| /* Allocate a process control structure. */ |
| kwsysProcess* cp = (kwsysProcess*)malloc(sizeof(kwsysProcess)); |
| if(!cp) |
| { |
| return 0; |
| } |
| memset(cp, 0, sizeof(kwsysProcess)); |
| |
| /* Share stdin with the parent process by default. */ |
| cp->PipeSharedSTDIN = 1; |
| |
| /* No native pipes by default. */ |
| cp->PipeNativeSTDIN[0] = -1; |
| cp->PipeNativeSTDIN[1] = -1; |
| cp->PipeNativeSTDOUT[0] = -1; |
| cp->PipeNativeSTDOUT[1] = -1; |
| cp->PipeNativeSTDERR[0] = -1; |
| cp->PipeNativeSTDERR[1] = -1; |
| |
| /* Set initial status. */ |
| cp->State = kwsysProcess_State_Starting; |
| |
| return cp; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_Delete(kwsysProcess* cp) |
| { |
| /* Make sure we have an instance. */ |
| if(!cp) |
| { |
| return; |
| } |
| |
| /* If the process is executing, wait for it to finish. */ |
| if(cp->State == kwsysProcess_State_Executing) |
| { |
| if(cp->Detached) |
| { |
| kwsysProcess_Disown(cp); |
| } |
| else |
| { |
| kwsysProcess_WaitForExit(cp, 0); |
| } |
| } |
| |
| /* Free memory. */ |
| kwsysProcess_SetCommand(cp, 0); |
| kwsysProcess_SetWorkingDirectory(cp, 0); |
| kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0); |
| kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0); |
| kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0); |
| if(cp->CommandExitCodes) |
| { |
| free(cp->CommandExitCodes); |
| } |
| free(cp); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command) |
| { |
| int i; |
| if(!cp) |
| { |
| return 0; |
| } |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| char** c = cp->Commands[i]; |
| while(*c) |
| { |
| free(*c++); |
| } |
| free(cp->Commands[i]); |
| } |
| cp->NumberOfCommands = 0; |
| if(cp->Commands) |
| { |
| free(cp->Commands); |
| cp->Commands = 0; |
| } |
| if(command) |
| { |
| return kwsysProcess_AddCommand(cp, command); |
| } |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) |
| { |
| int newNumberOfCommands; |
| char*** newCommands; |
| |
| /* Make sure we have a command to add. */ |
| if(!cp || !command || !*command) |
| { |
| return 0; |
| } |
| |
| /* Allocate a new array for command pointers. */ |
| newNumberOfCommands = cp->NumberOfCommands + 1; |
| if(!(newCommands = |
| (char***)malloc(sizeof(char**) *(size_t)(newNumberOfCommands)))) |
| { |
| /* Out of memory. */ |
| return 0; |
| } |
| |
| /* Copy any existing commands into the new array. */ |
| { |
| int i; |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| newCommands[i] = cp->Commands[i]; |
| } |
| } |
| |
| /* Add the new command. */ |
| if(cp->Verbatim) |
| { |
| /* In order to run the given command line verbatim we need to |
| parse it. */ |
| newCommands[cp->NumberOfCommands] = |
| kwsysSystem_Parse_CommandForUnix(*command, 0); |
| if(!newCommands[cp->NumberOfCommands] || |
| !newCommands[cp->NumberOfCommands][0]) |
| { |
| /* Out of memory or no command parsed. */ |
| free(newCommands); |
| return 0; |
| } |
| } |
| else |
| { |
| /* Copy each argument string individually. */ |
| char const* const* c = command; |
| kwsysProcess_ptrdiff_t n = 0; |
| kwsysProcess_ptrdiff_t i = 0; |
| while(*c++); |
| n = c - command - 1; |
| newCommands[cp->NumberOfCommands] = |
| (char**)malloc((size_t)(n+1)*sizeof(char*)); |
| if(!newCommands[cp->NumberOfCommands]) |
| { |
| /* Out of memory. */ |
| free(newCommands); |
| return 0; |
| } |
| for(i=0; i < n; ++i) |
| { |
| assert(command[i]); /* Quiet Clang scan-build. */ |
| newCommands[cp->NumberOfCommands][i] = strdup(command[i]); |
| if(!newCommands[cp->NumberOfCommands][i]) |
| { |
| break; |
| } |
| } |
| if(i < n) |
| { |
| /* Out of memory. */ |
| for(;i > 0; --i) |
| { |
| free(newCommands[cp->NumberOfCommands][i-1]); |
| } |
| free(newCommands); |
| return 0; |
| } |
| newCommands[cp->NumberOfCommands][n] = 0; |
| } |
| |
| /* Successfully allocated new command array. Free the old array. */ |
| free(cp->Commands); |
| cp->Commands = newCommands; |
| cp->NumberOfCommands = newNumberOfCommands; |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| cp->Timeout = timeout; |
| if(cp->Timeout < 0) |
| { |
| cp->Timeout = 0; |
| } |
| // Force recomputation of TimeoutTime. |
| cp->TimeoutTime.tv_sec = -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) |
| { |
| if(!cp) |
| { |
| return 0; |
| } |
| if(cp->WorkingDirectory == dir) |
| { |
| return 1; |
| } |
| if(cp->WorkingDirectory && dir && strcmp(cp->WorkingDirectory, dir) == 0) |
| { |
| return 1; |
| } |
| if(cp->WorkingDirectory) |
| { |
| free(cp->WorkingDirectory); |
| cp->WorkingDirectory = 0; |
| } |
| if(dir) |
| { |
| cp->WorkingDirectory = (char*)malloc(strlen(dir) + 1); |
| if(!cp->WorkingDirectory) |
| { |
| return 0; |
| } |
| strcpy(cp->WorkingDirectory, dir); |
| } |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_SetPipeFile(kwsysProcess* cp, int prPipe, const char* file) |
| { |
| char** pfile; |
| if(!cp) |
| { |
| return 0; |
| } |
| switch(prPipe) |
| { |
| case kwsysProcess_Pipe_STDIN: pfile = &cp->PipeFileSTDIN; break; |
| case kwsysProcess_Pipe_STDOUT: pfile = &cp->PipeFileSTDOUT; break; |
| case kwsysProcess_Pipe_STDERR: pfile = &cp->PipeFileSTDERR; break; |
| default: return 0; |
| } |
| if(*pfile) |
| { |
| free(*pfile); |
| *pfile = 0; |
| } |
| if(file) |
| { |
| *pfile = (char*)malloc(strlen(file)+1); |
| if(!*pfile) |
| { |
| return 0; |
| } |
| strcpy(*pfile, file); |
| } |
| |
| /* If we are redirecting the pipe, do not share it or use a native |
| pipe. */ |
| if(*pfile) |
| { |
| kwsysProcess_SetPipeNative(cp, prPipe, 0); |
| kwsysProcess_SetPipeShared(cp, prPipe, 0); |
| } |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetPipeShared(kwsysProcess* cp, int prPipe, int shared) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| |
| switch(prPipe) |
| { |
| case kwsysProcess_Pipe_STDIN: cp->PipeSharedSTDIN = shared?1:0; break; |
| case kwsysProcess_Pipe_STDOUT: cp->PipeSharedSTDOUT = shared?1:0; break; |
| case kwsysProcess_Pipe_STDERR: cp->PipeSharedSTDERR = shared?1:0; break; |
| default: return; |
| } |
| |
| /* If we are sharing the pipe, do not redirect it to a file or use a |
| native pipe. */ |
| if(shared) |
| { |
| kwsysProcess_SetPipeFile(cp, prPipe, 0); |
| kwsysProcess_SetPipeNative(cp, prPipe, 0); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetPipeNative(kwsysProcess* cp, int prPipe, int p[2]) |
| { |
| int* pPipeNative = 0; |
| |
| if(!cp) |
| { |
| return; |
| } |
| |
| switch(prPipe) |
| { |
| case kwsysProcess_Pipe_STDIN: pPipeNative = cp->PipeNativeSTDIN; break; |
| case kwsysProcess_Pipe_STDOUT: pPipeNative = cp->PipeNativeSTDOUT; break; |
| case kwsysProcess_Pipe_STDERR: pPipeNative = cp->PipeNativeSTDERR; break; |
| default: return; |
| } |
| |
| /* Copy the native pipe descriptors provided. */ |
| if(p) |
| { |
| pPipeNative[0] = p[0]; |
| pPipeNative[1] = p[1]; |
| } |
| else |
| { |
| pPipeNative[0] = -1; |
| pPipeNative[1] = -1; |
| } |
| |
| /* If we are using a native pipe, do not share it or redirect it to |
| a file. */ |
| if(p) |
| { |
| kwsysProcess_SetPipeFile(cp, prPipe, 0); |
| kwsysProcess_SetPipeShared(cp, prPipe, 0); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) |
| { |
| if(!cp) |
| { |
| return 0; |
| } |
| |
| switch(optionId) |
| { |
| case kwsysProcess_Option_Detach: return cp->OptionDetach; |
| case kwsysProcess_Option_MergeOutput: return cp->MergeOutput; |
| case kwsysProcess_Option_Verbatim: return cp->Verbatim; |
| case kwsysProcess_Option_CreateProcessGroup: |
| return cp->CreateProcessGroup; |
| default: return 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| |
| switch(optionId) |
| { |
| case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; |
| case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break; |
| case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; |
| case kwsysProcess_Option_CreateProcessGroup: |
| cp->CreateProcessGroup = value; break; |
| default: break; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetState(kwsysProcess* cp) |
| { |
| return cp? cp->State : kwsysProcess_State_Error; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetExitException(kwsysProcess* cp) |
| { |
| return cp? cp->ExitException : kwsysProcess_Exception_Other; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetExitCode(kwsysProcess* cp) |
| { |
| return cp? cp->ExitCode : 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetExitValue(kwsysProcess* cp) |
| { |
| return cp? cp->ExitValue : -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| const char* kwsysProcess_GetErrorString(kwsysProcess* cp) |
| { |
| if(!cp) |
| { |
| return "Process management structure could not be allocated"; |
| } |
| else if(cp->State == kwsysProcess_State_Error) |
| { |
| return cp->ErrorMessage; |
| } |
| return "Success"; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) |
| { |
| if(!cp) |
| { |
| return "GetExceptionString called with NULL process management structure"; |
| } |
| else if(cp->State == kwsysProcess_State_Exception) |
| { |
| return cp->ExitExceptionString; |
| } |
| return "No exception"; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_Execute(kwsysProcess* cp) |
| { |
| int i; |
| |
| /* Do not execute a second copy simultaneously. */ |
| if(!cp || cp->State == kwsysProcess_State_Executing) |
| { |
| return; |
| } |
| |
| /* Make sure we have something to run. */ |
| if(cp->NumberOfCommands < 1) |
| { |
| strcpy(cp->ErrorMessage, "No command"); |
| cp->State = kwsysProcess_State_Error; |
| return; |
| } |
| |
| /* Initialize the control structure for a new process. */ |
| if(!kwsysProcessInitialize(cp)) |
| { |
| strcpy(cp->ErrorMessage, "Out of memory"); |
| cp->State = kwsysProcess_State_Error; |
| return; |
| } |
| |
| #if defined(__VMS) |
| /* Make sure pipes behave like streams on VMS. */ |
| if(!kwsysProcessSetVMSFeature("DECC$STREAM_PIPE", 1)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| #endif |
| |
| /* Save the real working directory of this process and change to |
| the working directory for the child processes. This is needed |
| to make pipe file paths evaluate correctly. */ |
| if(cp->WorkingDirectory) |
| { |
| int r; |
| if(!getcwd(cp->RealWorkingDirectory, |
| (size_t)(cp->RealWorkingDirectoryLength))) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Some platforms specify that the chdir call may be |
| interrupted. Repeat the call until it finishes. */ |
| while(((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR)); |
| if(r < 0) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| |
| /* If not running a detached child, add this object to the global |
| set of process objects that wish to be notified when a child |
| exits. */ |
| if(!cp->OptionDetach) |
| { |
| if(!kwsysProcessesAdd(cp)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| |
| /* Setup the stdin pipe for the first process. */ |
| if(cp->PipeFileSTDIN) |
| { |
| /* Open a file for the child's stdin to read. */ |
| cp->PipeChildStd[0] = open(cp->PipeFileSTDIN, O_RDONLY); |
| if(cp->PipeChildStd[0] < 0) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Set close-on-exec flag on the pipe's end. */ |
| if(fcntl(cp->PipeChildStd[0], F_SETFD, FD_CLOEXEC) < 0) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| else if(cp->PipeSharedSTDIN) |
| { |
| cp->PipeChildStd[0] = 0; |
| } |
| else if(cp->PipeNativeSTDIN[0] >= 0) |
| { |
| cp->PipeChildStd[0] = cp->PipeNativeSTDIN[0]; |
| |
| /* Set close-on-exec flag on the pipe's ends. The read end will |
| be dup2-ed into the stdin descriptor after the fork but before |
| the exec. */ |
| if((fcntl(cp->PipeNativeSTDIN[0], F_SETFD, FD_CLOEXEC) < 0) || |
| (fcntl(cp->PipeNativeSTDIN[1], F_SETFD, FD_CLOEXEC) < 0)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| else |
| { |
| cp->PipeChildStd[0] = -1; |
| } |
| |
| /* Create the output pipe for the last process. |
| We always create this so the pipe can be passed to select even if |
| it will report closed immediately. */ |
| { |
| /* Create the pipe. */ |
| int p[2]; |
| if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Store the pipe. */ |
| cp->PipeReadEnds[KWSYSPE_PIPE_STDOUT] = p[0]; |
| cp->PipeChildStd[1] = p[1]; |
| |
| /* Set close-on-exec flag on the pipe's ends. */ |
| if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || |
| (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Set to non-blocking in case select lies, or for the polling |
| implementation. */ |
| if(!kwsysProcessSetNonBlocking(p[0])) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| |
| if (cp->PipeFileSTDOUT) |
| { |
| /* Use a file for stdout. */ |
| if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1], |
| cp->PipeFileSTDOUT)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| else if (cp->PipeSharedSTDOUT) |
| { |
| /* Use the parent stdout. */ |
| kwsysProcessCleanupDescriptor(&cp->PipeChildStd[1]); |
| cp->PipeChildStd[1] = 1; |
| } |
| else if (cp->PipeNativeSTDOUT[1] >= 0) |
| { |
| /* Use the given descriptor for stdout. */ |
| if(!kwsysProcessSetupOutputPipeNative(&cp->PipeChildStd[1], |
| cp->PipeNativeSTDOUT)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| |
| /* Create stderr pipe to be shared by all processes in the pipeline. |
| We always create this so the pipe can be passed to select even if |
| it will report closed immediately. */ |
| { |
| /* Create the pipe. */ |
| int p[2]; |
| if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Store the pipe. */ |
| cp->PipeReadEnds[KWSYSPE_PIPE_STDERR] = p[0]; |
| cp->PipeChildStd[2] = p[1]; |
| |
| /* Set close-on-exec flag on the pipe's ends. */ |
| if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || |
| (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Set to non-blocking in case select lies, or for the polling |
| implementation. */ |
| if(!kwsysProcessSetNonBlocking(p[0])) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| |
| if (cp->PipeFileSTDERR) |
| { |
| /* Use a file for stderr. */ |
| if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2], |
| cp->PipeFileSTDERR)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| else if (cp->PipeSharedSTDERR) |
| { |
| /* Use the parent stderr. */ |
| kwsysProcessCleanupDescriptor(&cp->PipeChildStd[2]); |
| cp->PipeChildStd[2] = 2; |
| } |
| else if (cp->PipeNativeSTDERR[1] >= 0) |
| { |
| /* Use the given handle for stderr. */ |
| if(!kwsysProcessSetupOutputPipeNative(&cp->PipeChildStd[2], |
| cp->PipeNativeSTDERR)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| |
| /* The timeout period starts now. */ |
| cp->StartTime = kwsysProcessTimeGetCurrent(); |
| cp->TimeoutTime.tv_sec = -1; |
| cp->TimeoutTime.tv_usec = -1; |
| |
| /* Create the pipeline of processes. */ |
| { |
| kwsysProcessCreateInformation si = {-1, -1, -1, {-1, -1}}; |
| int nextStdIn = cp->PipeChildStd[0]; |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| /* Setup the process's pipes. */ |
| si.StdIn = nextStdIn; |
| if (i == cp->NumberOfCommands-1) |
| { |
| nextStdIn = -1; |
| si.StdOut = cp->PipeChildStd[1]; |
| } |
| else |
| { |
| /* Create a pipe to sit between the children. */ |
| int p[2] = {-1,-1}; |
| if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) |
| { |
| if (nextStdIn != cp->PipeChildStd[0]) |
| { |
| kwsysProcessCleanupDescriptor(&nextStdIn); |
| } |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Set close-on-exec flag on the pipe's ends. */ |
| if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || |
| (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) |
| { |
| close(p[0]); |
| close(p[1]); |
| if (nextStdIn != cp->PipeChildStd[0]) |
| { |
| kwsysProcessCleanupDescriptor(&nextStdIn); |
| } |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| nextStdIn = p[0]; |
| si.StdOut = p[1]; |
| } |
| si.StdErr = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2]; |
| |
| { |
| int res = kwsysProcessCreate(cp, i, &si); |
| |
| /* Close our copies of pipes used between children. */ |
| if (si.StdIn != cp->PipeChildStd[0]) |
| { |
| kwsysProcessCleanupDescriptor(&si.StdIn); |
| } |
| if (si.StdOut != cp->PipeChildStd[1]) |
| { |
| kwsysProcessCleanupDescriptor(&si.StdOut); |
| } |
| if (si.StdErr != cp->PipeChildStd[2] && !cp->MergeOutput) |
| { |
| kwsysProcessCleanupDescriptor(&si.StdErr); |
| } |
| |
| if(!res) |
| { |
| kwsysProcessCleanupDescriptor(&si.ErrorPipe[0]); |
| kwsysProcessCleanupDescriptor(&si.ErrorPipe[1]); |
| if (nextStdIn != cp->PipeChildStd[0]) |
| { |
| kwsysProcessCleanupDescriptor(&nextStdIn); |
| } |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| } |
| } |
| } |
| |
| /* The parent process does not need the child's pipe ends. */ |
| for (i=0; i < 3; ++i) |
| { |
| kwsysProcessCleanupDescriptor(&cp->PipeChildStd[i]); |
| } |
| |
| /* Restore the working directory. */ |
| if(cp->RealWorkingDirectory) |
| { |
| /* Some platforms specify that the chdir call may be |
| interrupted. Repeat the call until it finishes. */ |
| while((chdir(cp->RealWorkingDirectory) < 0) && (errno == EINTR)); |
| free(cp->RealWorkingDirectory); |
| cp->RealWorkingDirectory = 0; |
| } |
| |
| /* All the pipes are now open. */ |
| cp->PipesLeft = KWSYSPE_PIPE_COUNT; |
| |
| /* The process has now started. */ |
| cp->State = kwsysProcess_State_Executing; |
| cp->Detached = cp->OptionDetach; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp) |
| { |
| /* Make sure a detached child process is running. */ |
| if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing || |
| cp->TimeoutExpired || cp->Killed) |
| { |
| return; |
| } |
| |
| /* Close all the pipes safely. */ |
| kwsysProcessClosePipes(cp); |
| |
| /* We will not wait for exit, so cleanup now. */ |
| kwsysProcessCleanup(cp, 0); |
| |
| /* The process has been disowned. */ |
| cp->State = kwsysProcess_State_Disowned; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| typedef struct kwsysProcessWaitData_s |
| { |
| int Expired; |
| int PipeId; |
| int User; |
| double* UserTimeout; |
| kwsysProcessTime TimeoutTime; |
| } kwsysProcessWaitData; |
| static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length, |
| kwsysProcessWaitData* wd); |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length, |
| double* userTimeout) |
| { |
| kwsysProcessTime userStartTime = {0, 0}; |
| kwsysProcessWaitData wd = |
| { |
| 0, |
| kwsysProcess_Pipe_None, |
| 0, |
| 0, |
| {0, 0} |
| }; |
| wd.UserTimeout = userTimeout; |
| /* Make sure we are executing a process. */ |
| if(!cp || cp->State != kwsysProcess_State_Executing || cp->Killed || |
| cp->TimeoutExpired) |
| { |
| return kwsysProcess_Pipe_None; |
| } |
| |
| /* Record the time at which user timeout period starts. */ |
| if(userTimeout) |
| { |
| userStartTime = kwsysProcessTimeGetCurrent(); |
| } |
| |
| /* Calculate the time at which a timeout will expire, and whether it |
| is the user or process timeout. */ |
| wd.User = kwsysProcessGetTimeoutTime(cp, userTimeout, |
| &wd.TimeoutTime); |
| |
| /* Data can only be available when pipes are open. If the process |
| is not running, cp->PipesLeft will be 0. */ |
| while(cp->PipesLeft > 0 && |
| !kwsysProcessWaitForPipe(cp, data, length, &wd)) {} |
| |
| /* Update the user timeout. */ |
| if(userTimeout) |
| { |
| kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent(); |
| kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime, |
| userStartTime); |
| double d = kwsysProcessTimeToDouble(difference); |
| *userTimeout -= d; |
| if(*userTimeout < 0) |
| { |
| *userTimeout = 0; |
| } |
| } |
| |
| /* Check what happened. */ |
| if(wd.PipeId) |
| { |
| /* Data are ready on a pipe. */ |
| return wd.PipeId; |
| } |
| else if(wd.Expired) |
| { |
| /* A timeout has expired. */ |
| if(wd.User) |
| { |
| /* The user timeout has expired. It has no time left. */ |
| return kwsysProcess_Pipe_Timeout; |
| } |
| else |
| { |
| /* The process timeout has expired. Kill the children now. */ |
| kwsysProcess_Kill(cp); |
| cp->Killed = 0; |
| cp->TimeoutExpired = 1; |
| return kwsysProcess_Pipe_None; |
| } |
| } |
| else |
| { |
| /* No pipes are left open. */ |
| return kwsysProcess_Pipe_None; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length, |
| kwsysProcessWaitData* wd) |
| { |
| int i; |
| kwsysProcessTimeNative timeoutLength; |
| |
| #if KWSYSPE_USE_SELECT |
| int numReady = 0; |
| int max = -1; |
| kwsysProcessTimeNative* timeout = 0; |
| |
| /* Check for any open pipes with data reported ready by the last |
| call to select. According to "man select_tut" we must deal |
| with all descriptors reported by a call to select before |
| passing them to another select call. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| if(cp->PipeReadEnds[i] >= 0 && |
| FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet)) |
| { |
| kwsysProcess_ssize_t n; |
| |
| /* We are handling this pipe now. Remove it from the set. */ |
| FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet); |
| |
| /* The pipe is ready to read without blocking. Keep trying to |
| read until the operation is not interrupted. */ |
| while(((n = read(cp->PipeReadEnds[i], cp->PipeBuffer, |
| KWSYSPE_PIPE_BUFFER_SIZE)) < 0) && (errno == EINTR)); |
| if(n > 0) |
| { |
| /* We have data on this pipe. */ |
| if(i == KWSYSPE_PIPE_SIGNAL) |
| { |
| /* A child process has terminated. */ |
| kwsysProcessDestroy(cp); |
| } |
| else if(data && length) |
| { |
| /* Report this data. */ |
| *data = cp->PipeBuffer; |
| *length = (int)(n); |
| switch(i) |
| { |
| case KWSYSPE_PIPE_STDOUT: |
| wd->PipeId = kwsysProcess_Pipe_STDOUT; break; |
| case KWSYSPE_PIPE_STDERR: |
| wd->PipeId = kwsysProcess_Pipe_STDERR; break; |
| }; |
| return 1; |
| } |
| } |
| else if(n < 0 && errno == EAGAIN) |
| { |
| /* No data are really ready. The select call lied. See the |
| "man select" page on Linux for cases when this occurs. */ |
| } |
| else |
| { |
| /* We are done reading from this pipe. */ |
| kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); |
| --cp->PipesLeft; |
| } |
| } |
| } |
| |
| /* If we have data, break early. */ |
| if(wd->PipeId) |
| { |
| return 1; |
| } |
| |
| /* Make sure the set is empty (it should always be empty here |
| anyway). */ |
| FD_ZERO(&cp->PipeSet); |
| |
| /* Setup a timeout if required. */ |
| if(wd->TimeoutTime.tv_sec < 0) |
| { |
| timeout = 0; |
| } |
| else |
| { |
| timeout = &timeoutLength; |
| } |
| if(kwsysProcessGetTimeoutLeft(&wd->TimeoutTime, |
| wd->User?wd->UserTimeout:0, |
| &timeoutLength, 0)) |
| { |
| /* Timeout has already expired. */ |
| wd->Expired = 1; |
| return 1; |
| } |
| |
| /* Add the pipe reading ends that are still open. */ |
| max = -1; |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| if(cp->PipeReadEnds[i] >= 0) |
| { |
| FD_SET(cp->PipeReadEnds[i], &cp->PipeSet); |
| if(cp->PipeReadEnds[i] > max) |
| { |
| max = cp->PipeReadEnds[i]; |
| } |
| } |
| } |
| |
| /* Make sure we have a non-empty set. */ |
| if(max < 0) |
| { |
| /* All pipes have closed. Child has terminated. */ |
| return 1; |
| } |
| |
| /* Run select to block until data are available. Repeat call |
| until it is not interrupted. */ |
| while(((numReady = select(max+1, &cp->PipeSet, 0, 0, timeout)) < 0) && |
| (errno == EINTR)); |
| |
| /* Check result of select. */ |
| if(numReady == 0) |
| { |
| /* Select's timeout expired. */ |
| wd->Expired = 1; |
| return 1; |
| } |
| else if(numReady < 0) |
| { |
| /* Select returned an error. Leave the error description in the |
| pipe buffer. */ |
| strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); |
| |
| /* Kill the children now. */ |
| kwsysProcess_Kill(cp); |
| cp->Killed = 0; |
| cp->SelectError = 1; |
| } |
| |
| return 0; |
| #else |
| /* Poll pipes for data since we do not have select. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| if(cp->PipeReadEnds[i] >= 0) |
| { |
| const int fd = cp->PipeReadEnds[i]; |
| int n = read(fd, cp->PipeBuffer, KWSYSPE_PIPE_BUFFER_SIZE); |
| if(n > 0) |
| { |
| /* We have data on this pipe. */ |
| if(i == KWSYSPE_PIPE_SIGNAL) |
| { |
| /* A child process has terminated. */ |
| kwsysProcessDestroy(cp); |
| } |
| else if(data && length) |
| { |
| /* Report this data. */ |
| *data = cp->PipeBuffer; |
| *length = n; |
| switch(i) |
| { |
| case KWSYSPE_PIPE_STDOUT: |
| wd->PipeId = kwsysProcess_Pipe_STDOUT; break; |
| case KWSYSPE_PIPE_STDERR: |
| wd->PipeId = kwsysProcess_Pipe_STDERR; break; |
| }; |
| } |
| return 1; |
| } |
| else if (n == 0) /* EOF */ |
| { |
| /* We are done reading from this pipe. */ |
| #if defined(__VMS) |
| if(!cp->CommandsLeft) |
| #endif |
| { |
| kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); |
| --cp->PipesLeft; |
| } |
| } |
| else if (n < 0) /* error */ |
| { |
| #if defined(__VMS) |
| if(!cp->CommandsLeft) |
| { |
| kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); |
| --cp->PipesLeft; |
| } |
| else |
| #endif |
| if((errno != EINTR) && (errno != EAGAIN)) |
| { |
| strncpy(cp->ErrorMessage,strerror(errno), |
| KWSYSPE_PIPE_BUFFER_SIZE); |
| /* Kill the children now. */ |
| kwsysProcess_Kill(cp); |
| cp->Killed = 0; |
| cp->SelectError = 1; |
| return 1; |
| } |
| } |
| } |
| } |
| |
| /* If we have data, break early. */ |
| if(wd->PipeId) |
| { |
| return 1; |
| } |
| |
| if(kwsysProcessGetTimeoutLeft(&wd->TimeoutTime, wd->User?wd->UserTimeout:0, |
| &timeoutLength, 1)) |
| { |
| /* Timeout has already expired. */ |
| wd->Expired = 1; |
| return 1; |
| } |
| |
| /* Sleep a little, try again. */ |
| { |
| unsigned int msec = ((timeoutLength.tv_sec * 1000) + |
| (timeoutLength.tv_usec / 1000)); |
| if (msec > 100000) |
| { |
| msec = 100000; /* do not sleep more than 100 milliseconds at a time */ |
| } |
| kwsysProcess_usleep(msec); |
| } |
| return 0; |
| #endif |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout) |
| { |
| int status = 0; |
| int prPipe = 0; |
| |
| /* Make sure we are executing a process. */ |
| if(!cp || cp->State != kwsysProcess_State_Executing) |
| { |
| return 1; |
| } |
| |
| /* Wait for all the pipes to close. Ignore all data. */ |
| while((prPipe = kwsysProcess_WaitForData(cp, 0, 0, userTimeout)) > 0) |
| { |
| if(prPipe == kwsysProcess_Pipe_Timeout) |
| { |
| return 0; |
| } |
| } |
| |
| /* Check if there was an error in one of the waitpid calls. */ |
| if(cp->State == kwsysProcess_State_Error) |
| { |
| /* The error message is already in its buffer. Tell |
| kwsysProcessCleanup to not create it. */ |
| kwsysProcessCleanup(cp, 0); |
| return 1; |
| } |
| |
| /* Check whether the child reported an error invoking the process. */ |
| if(cp->SelectError) |
| { |
| /* The error message is already in its buffer. Tell |
| kwsysProcessCleanup to not create it. */ |
| kwsysProcessCleanup(cp, 0); |
| cp->State = kwsysProcess_State_Error; |
| return 1; |
| } |
| |
| /* Use the status of the last process in the pipeline. */ |
| status = cp->CommandExitCodes[cp->NumberOfCommands-1]; |
| |
| /* Determine the outcome. */ |
| if(cp->Killed) |
| { |
| /* We killed the child. */ |
| cp->State = kwsysProcess_State_Killed; |
| } |
| else if(cp->TimeoutExpired) |
| { |
| /* The timeout expired. */ |
| cp->State = kwsysProcess_State_Expired; |
| } |
| else if(WIFEXITED(status)) |
| { |
| /* The child exited normally. */ |
| cp->State = kwsysProcess_State_Exited; |
| cp->ExitException = kwsysProcess_Exception_None; |
| cp->ExitCode = status; |
| cp->ExitValue = (int)WEXITSTATUS(status); |
| } |
| else if(WIFSIGNALED(status)) |
| { |
| /* The child received an unhandled signal. */ |
| cp->State = kwsysProcess_State_Exception; |
| cp->ExitCode = status; |
| kwsysProcessSetExitException(cp, (int)WTERMSIG(status)); |
| } |
| else |
| { |
| /* Error getting the child return code. */ |
| strcpy(cp->ErrorMessage, "Error getting child return code."); |
| cp->State = kwsysProcess_State_Error; |
| } |
| |
| /* Normal cleanup. */ |
| kwsysProcessCleanup(cp, 0); |
| 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) |
| { |
| int i; |
| |
| /* Make sure we are executing a process. */ |
| if(!cp || cp->State != kwsysProcess_State_Executing) |
| { |
| return; |
| } |
| |
| /* First close the child exit report pipe write end to avoid causing a |
| SIGPIPE when the child terminates and our signal handler tries to |
| report it after we have already closed the read end. */ |
| kwsysProcessCleanupDescriptor(&cp->SignalPipe); |
| |
| #if !defined(__APPLE__) |
| /* Close all the pipe read ends. Do this before killing the |
| children because Cygwin has problems killing processes that are |
| blocking to wait for writing to their output pipes. */ |
| kwsysProcessClosePipes(cp); |
| #endif |
| |
| /* Kill the children. */ |
| cp->Killed = 1; |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| int status; |
| if(cp->ForkPIDs[i]) |
| { |
| /* Kill the child. */ |
| kwsysProcessKill(cp->ForkPIDs[i]); |
| |
| /* Reap the child. Keep trying until the call is not |
| interrupted. */ |
| while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && (errno == EINTR)); |
| } |
| } |
| |
| #if defined(__APPLE__) |
| /* Close all the pipe read ends. Do this after killing the |
| children because OS X has problems closing pipe read ends whose |
| pipes are full and still have an open write end. */ |
| kwsysProcessClosePipes(cp); |
| #endif |
| |
| 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__) && !defined(__INTEL_COMPILER) |
| # 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__) && !defined(__INTEL_COMPILER) |
| # pragma clang diagnostic pop |
| #endif |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Initialize a process control structure for kwsysProcess_Execute. */ |
| static int kwsysProcessInitialize(kwsysProcess* cp) |
| { |
| int i; |
| volatile pid_t* oldForkPIDs; |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| cp->PipeReadEnds[i] = -1; |
| } |
| for(i=0; i < 3; ++i) |
| { |
| cp->PipeChildStd[i] = -1; |
| } |
| cp->SignalPipe = -1; |
| cp->SelectError = 0; |
| cp->StartTime.tv_sec = -1; |
| cp->StartTime.tv_usec = -1; |
| cp->TimeoutTime.tv_sec = -1; |
| cp->TimeoutTime.tv_usec = -1; |
| cp->TimeoutExpired = 0; |
| cp->PipesLeft = 0; |
| cp->CommandsLeft = 0; |
| #if KWSYSPE_USE_SELECT |
| FD_ZERO(&cp->PipeSet); |
| #endif |
| cp->State = kwsysProcess_State_Starting; |
| cp->Killed = 0; |
| cp->ExitException = kwsysProcess_Exception_None; |
| cp->ExitCode = 1; |
| cp->ExitValue = 1; |
| cp->ErrorMessage[0] = 0; |
| strcpy(cp->ExitExceptionString, "No exception"); |
| |
| oldForkPIDs = cp->ForkPIDs; |
| cp->ForkPIDs = (volatile pid_t*)malloc( |
| sizeof(volatile pid_t)*(size_t)(cp->NumberOfCommands)); |
| if(oldForkPIDs) |
| { |
| kwsysProcessVolatileFree(oldForkPIDs); |
| } |
| if(!cp->ForkPIDs) |
| { |
| return 0; |
| } |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| cp->ForkPIDs[i] = 0; /* can't use memset due to volatile */ |
| } |
| |
| if(cp->CommandExitCodes) |
| { |
| free(cp->CommandExitCodes); |
| } |
| cp->CommandExitCodes = (int*)malloc(sizeof(int)* |
| (size_t)(cp->NumberOfCommands)); |
| if(!cp->CommandExitCodes) |
| { |
| return 0; |
| } |
| memset(cp->CommandExitCodes, 0, sizeof(int)*(size_t)(cp->NumberOfCommands)); |
| |
| /* Allocate memory to save the real working directory. */ |
| if ( cp->WorkingDirectory ) |
| { |
| #if defined(MAXPATHLEN) |
| cp->RealWorkingDirectoryLength = MAXPATHLEN; |
| #elif defined(PATH_MAX) |
| cp->RealWorkingDirectoryLength = PATH_MAX; |
| #else |
| cp->RealWorkingDirectoryLength = 4096; |
| #endif |
| cp->RealWorkingDirectory = |
| (char*)malloc((size_t)(cp->RealWorkingDirectoryLength)); |
| if(!cp->RealWorkingDirectory) |
| { |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Free all resources used by the given kwsysProcess instance that were |
| allocated by kwsysProcess_Execute. */ |
| static void kwsysProcessCleanup(kwsysProcess* cp, int error) |
| { |
| int i; |
| |
| if(error) |
| { |
| /* We are cleaning up due to an error. Report the error message |
| if one has not been provided already. */ |
| if(cp->ErrorMessage[0] == 0) |
| { |
| strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); |
| } |
| |
| /* Set the error state. */ |
| cp->State = kwsysProcess_State_Error; |
| |
| /* Kill any children already started. */ |
| if(cp->ForkPIDs) |
| { |
| int status; |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| if(cp->ForkPIDs[i]) |
| { |
| /* Kill the child. */ |
| kwsysProcessKill(cp->ForkPIDs[i]); |
| |
| /* Reap the child. Keep trying until the call is not |
| interrupted. */ |
| while((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && |
| (errno == EINTR)); |
| } |
| } |
| } |
| |
| /* Restore the working directory. */ |
| if(cp->RealWorkingDirectory) |
| { |
| while((chdir(cp->RealWorkingDirectory) < 0) && (errno == EINTR)); |
| } |
| } |
| |
| /* If not creating a detached child, remove this object from the |
| global set of process objects that wish to be notified when a |
| child exits. */ |
| if(!cp->OptionDetach) |
| { |
| kwsysProcessesRemove(cp); |
| } |
| |
| /* Free memory. */ |
| if(cp->ForkPIDs) |
| { |
| kwsysProcessVolatileFree(cp->ForkPIDs); |
| cp->ForkPIDs = 0; |
| } |
| if(cp->RealWorkingDirectory) |
| { |
| free(cp->RealWorkingDirectory); |
| cp->RealWorkingDirectory = 0; |
| } |
| |
| /* Close pipe handles. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); |
| } |
| for(i=0; i < 3; ++i) |
| { |
| kwsysProcessCleanupDescriptor(&cp->PipeChildStd[i]); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Close the given file descriptor if it is open. Reset its value to -1. */ |
| static void kwsysProcessCleanupDescriptor(int* pfd) |
| { |
| if(pfd && *pfd > 2) |
| { |
| /* Keep trying to close until it is not interrupted by a |
| * signal. */ |
| while((close(*pfd) < 0) && (errno == EINTR)); |
| *pfd = -1; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessClosePipes(kwsysProcess* cp) |
| { |
| int i; |
| |
| /* Close any pipes that are still open. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| if(cp->PipeReadEnds[i] >= 0) |
| { |
| #if KWSYSPE_USE_SELECT |
| /* If the pipe was reported by the last call to select, we must |
| read from it. This is needed to satisfy the suggestions from |
| "man select_tut" and is not needed for the polling |
| implementation. Ignore the data. */ |
| if(FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet)) |
| { |
| /* We are handling this pipe now. Remove it from the set. */ |
| FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet); |
| |
| /* The pipe is ready to read without blocking. Keep trying to |
| read until the operation is not interrupted. */ |
| while((read(cp->PipeReadEnds[i], cp->PipeBuffer, |
| KWSYSPE_PIPE_BUFFER_SIZE) < 0) && (errno == EINTR)); |
| } |
| #endif |
| |
| /* We are done reading from this pipe. */ |
| kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); |
| --cp->PipesLeft; |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessSetNonBlocking(int fd) |
| { |
| int flags = fcntl(fd, F_GETFL); |
| if(flags >= 0) |
| { |
| flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
| } |
| return flags >= 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| #if defined(__VMS) |
| int decc$set_child_standard_streams(int fd1, int fd2, int fd3); |
| #endif |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, |
| kwsysProcessCreateInformation* si) |
| { |
| sigset_t mask, old_mask; |
| int pgidPipe[2]; |
| char tmp; |
| ssize_t readRes; |
| |
| /* Create the error reporting pipe. */ |
| if(pipe(si->ErrorPipe) < 0) |
| { |
| return 0; |
| } |
| |
| /* Create a pipe for detecting that the child process has created a process |
| 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; |
| } |
| |
| /* Fork off a child process. */ |
| #if defined(__VMS) |
| /* VMS needs vfork and execvp to be in the same function because |
| they use setjmp/longjmp to run the child startup code in the |
| parent! TODO: OptionDetach. Also |
| TODO: CreateProcessGroup. */ |
| cp->ForkPIDs[prIndex] = vfork(); |
| #else |
| cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si); |
| #endif |
| 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; |
| } |
| |
| if(cp->ForkPIDs[prIndex] == 0) |
| { |
| #if defined(__VMS) |
| /* Specify standard pipes for child process. */ |
| decc$set_child_standard_streams(si->StdIn, si->StdOut, si->StdErr); |
| #else |
| /* Close the read end of the error reporting / process group |
| setup pipe. */ |
| close(si->ErrorPipe[0]); |
| close(pgidPipe[0]); |
| |
| /* Setup the stdin, stdout, and stderr pipes. */ |
| if(si->StdIn > 0) |
| { |
| dup2(si->StdIn, 0); |
| } |
| else if(si->StdIn < 0) |
| { |
| close(0); |
| } |
| if(si->StdOut != 1) |
| { |
| dup2(si->StdOut, 1); |
| } |
| if(si->StdErr != 2) |
| { |
| dup2(si->StdErr, 2); |
| } |
| |
| /* Clear the close-on-exec flag for stdin, stdout, and stderr. |
| All other pipe handles will be closed when exec succeeds. */ |
| fcntl(0, F_SETFD, 0); |
| fcntl(1, F_SETFD, 0); |
| fcntl(2, F_SETFD, 0); |
| |
| /* Restore all default signal handlers. */ |
| 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 |
| |
| /* Execute the real process. If successful, this does not return. */ |
| execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]); |
| /* 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. */ |
| kwsysProcessChildErrorExit(si->ErrorPipe[1]); |
| } |
| |
| #if defined(__VMS) |
| /* Restore the standard pipes of this process. */ |
| decc$set_child_standard_streams(0, 1, 2); |
| #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. */ |
| ++cp->CommandsLeft; |
| |
| /* Block until the child's exec call succeeds and closes the error |
| pipe or writes data to the pipe to report an error. */ |
| { |
| kwsysProcess_ssize_t total = 0; |
| kwsysProcess_ssize_t n = 1; |
| /* Read the entire error message up to the length of our buffer. */ |
| while(total < KWSYSPE_PIPE_BUFFER_SIZE && n > 0) |
| { |
| /* Keep trying to read until the operation is not interrupted. */ |
| while(((n = read(si->ErrorPipe[0], cp->ErrorMessage+total, |
| (size_t)(KWSYSPE_PIPE_BUFFER_SIZE-total))) < 0) && |
| (errno == EINTR)); |
| if(n > 0) |
| { |
| total += n; |
| } |
| } |
| |
| /* We are done with the error reporting pipe read end. */ |
| kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); |
| |
| if(total > 0) |
| { |
| /* The child failed to execute the process. */ |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessDestroy(kwsysProcess* cp) |
| { |
| /* A child process has terminated. Reap it if it is one handled by |
| this object. */ |
| 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) |
| { |
| if(cp->ForkPIDs[i]) |
| { |
| int result; |
| while(((result = waitpid(cp->ForkPIDs[i], |
| &cp->CommandExitCodes[i], WNOHANG)) < 0) && |
| (errno == EINTR)); |
| if(result > 0) |
| { |
| /* This child has termianted. */ |
| cp->ForkPIDs[i] = 0; |
| if(--cp->CommandsLeft == 0) |
| { |
| /* All children have terminated. Close the signal pipe |
| write end so that no more notifications are sent to this |
| object. */ |
| kwsysProcessCleanupDescriptor(&cp->SignalPipe); |
| |
| /* TODO: Once the children have terminated, switch |
| WaitForData to use a non-blocking read to get the |
| rest of the data from the pipe. This is needed when |
| grandchildren keep the output pipes open. */ |
| } |
| } |
| else if(result < 0 && cp->State != kwsysProcess_State_Error) |
| { |
| /* Unexpected error. Report the first time this happens. */ |
| strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); |
| cp->State = kwsysProcess_State_Error; |
| } |
| } |
| } |
| |
| /* Re-enable signals. */ |
| sigprocmask(SIG_SETMASK, &old_mask, 0); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessSetupOutputPipeFile(int* p, const char* name) |
| { |
| int fout; |
| if(!name) |
| { |
| return 1; |
| } |
| |
| /* Close the existing descriptor. */ |
| kwsysProcessCleanupDescriptor(p); |
| |
| /* Open a file for the pipe to write. */ |
| if((fout = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) |
| { |
| return 0; |
| } |
| |
| /* Set close-on-exec flag on the pipe's end. */ |
| if(fcntl(fout, F_SETFD, FD_CLOEXEC) < 0) |
| { |
| return 0; |
| } |
| |
| /* Assign the replacement descriptor. */ |
| *p = fout; |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]) |
| { |
| /* Close the existing descriptor. */ |
| kwsysProcessCleanupDescriptor(p); |
| |
| /* Set close-on-exec flag on the pipe's ends. The proper end will |
| be dup2-ed into the standard descriptor number after fork but |
| before exec. */ |
| if((fcntl(des[0], F_SETFD, FD_CLOEXEC) < 0) || |
| (fcntl(des[1], F_SETFD, FD_CLOEXEC) < 0)) |
| { |
| return 0; |
| } |
| |
| /* Assign the replacement descriptor. */ |
| *p = des[1]; |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Get the time at which either the process or user timeout will |
| expire. Returns 1 if the user timeout is first, and 0 otherwise. */ |
| static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, |
| kwsysProcessTime* timeoutTime) |
| { |
| /* The first time this is called, we need to calculate the time at |
| which the child will timeout. */ |
| if(cp->Timeout > 0 && cp->TimeoutTime.tv_sec < 0) |
| { |
| kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout); |
| cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length); |
| } |
| |
| /* Start with process timeout. */ |
| *timeoutTime = cp->TimeoutTime; |
| |
| /* Check if the user timeout is earlier. */ |
| if(userTimeout) |
| { |
| kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); |
| kwsysProcessTime userTimeoutLength = kwsysProcessTimeFromDouble(*userTimeout); |
| kwsysProcessTime userTimeoutTime = kwsysProcessTimeAdd(currentTime, |
| userTimeoutLength); |
| if(timeoutTime->tv_sec < 0 || |
| kwsysProcessTimeLess(userTimeoutTime, *timeoutTime)) |
| { |
| *timeoutTime = userTimeoutTime; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Get the length of time before the given timeout time arrives. |
| Returns 1 if the time has already arrived, and 0 otherwise. */ |
| static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, |
| double* userTimeout, |
| kwsysProcessTimeNative* timeoutLength, |
| int zeroIsExpired) |
| { |
| if(timeoutTime->tv_sec < 0) |
| { |
| /* No timeout time has been requested. */ |
| return 0; |
| } |
| else |
| { |
| /* Calculate the remaining time. */ |
| kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); |
| kwsysProcessTime timeLeft = kwsysProcessTimeSubtract(*timeoutTime, |
| currentTime); |
| if(timeLeft.tv_sec < 0 && userTimeout && *userTimeout <= 0) |
| { |
| /* Caller has explicitly requested a zero timeout. */ |
| timeLeft.tv_sec = 0; |
| timeLeft.tv_usec = 0; |
| } |
| |
| if(timeLeft.tv_sec < 0 || |
| (timeLeft.tv_sec == 0 && timeLeft.tv_usec == 0 && zeroIsExpired)) |
| { |
| /* Timeout has already expired. */ |
| return 1; |
| } |
| else |
| { |
| /* There is some time left. */ |
| timeoutLength->tv_sec = timeLeft.tv_sec; |
| timeoutLength->tv_usec = timeLeft.tv_usec; |
| return 0; |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static kwsysProcessTime kwsysProcessTimeGetCurrent(void) |
| { |
| kwsysProcessTime current; |
| kwsysProcessTimeNative current_native; |
| gettimeofday(¤t_native, 0); |
| current.tv_sec = (long)current_native.tv_sec; |
| current.tv_usec = (long)current_native.tv_usec; |
| return current; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static double kwsysProcessTimeToDouble(kwsysProcessTime t) |
| { |
| return (double)t.tv_sec + (double)(t.tv_usec)*0.000001; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static kwsysProcessTime kwsysProcessTimeFromDouble(double d) |
| { |
| kwsysProcessTime t; |
| t.tv_sec = (long)d; |
| t.tv_usec = (long)((d-(double)(t.tv_sec))*1000000); |
| return t; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2) |
| { |
| return ((in1.tv_sec < in2.tv_sec) || |
| ((in1.tv_sec == in2.tv_sec) && (in1.tv_usec < in2.tv_usec))); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2) |
| { |
| kwsysProcessTime out; |
| out.tv_sec = in1.tv_sec + in2.tv_sec; |
| out.tv_usec = in1.tv_usec + in2.tv_usec; |
| if(out.tv_usec >= 1000000) |
| { |
| out.tv_usec -= 1000000; |
| out.tv_sec += 1; |
| } |
| return out; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2) |
| { |
| kwsysProcessTime out; |
| out.tv_sec = in1.tv_sec - in2.tv_sec; |
| out.tv_usec = in1.tv_usec - in2.tv_usec; |
| if(out.tv_usec < 0) |
| { |
| out.tv_usec += 1000000; |
| out.tv_sec -= 1; |
| } |
| return out; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| #define KWSYSPE_CASE(type, str) \ |
| cp->ExitException = kwsysProcess_Exception_##type; \ |
| strcpy(cp->ExitExceptionString, str) |
| static void kwsysProcessSetExitException(kwsysProcess* cp, int sig) |
| { |
| switch (sig) |
| { |
| #ifdef SIGSEGV |
| case SIGSEGV: KWSYSPE_CASE(Fault, "Segmentation fault"); break; |
| #endif |
| #ifdef SIGBUS |
| # if !defined(SIGSEGV) || SIGBUS != SIGSEGV |
| case SIGBUS: KWSYSPE_CASE(Fault, "Bus error"); break; |
| # endif |
| #endif |
| #ifdef SIGFPE |
| case SIGFPE: KWSYSPE_CASE(Numerical, "Floating-point exception"); break; |
| #endif |
| #ifdef SIGILL |
| case SIGILL: KWSYSPE_CASE(Illegal, "Illegal instruction"); break; |
| #endif |
| #ifdef SIGINT |
| case SIGINT: KWSYSPE_CASE(Interrupt, "User interrupt"); break; |
| #endif |
| #ifdef SIGABRT |
| case SIGABRT: KWSYSPE_CASE(Other, "Child aborted"); break; |
| #endif |
| #ifdef SIGKILL |
| case SIGKILL: KWSYSPE_CASE(Other, "Child killed"); break; |
| #endif |
| #ifdef SIGTERM |
| case SIGTERM: KWSYSPE_CASE(Other, "Child terminated"); break; |
| #endif |
| #ifdef SIGHUP |
| case SIGHUP: KWSYSPE_CASE(Other, "SIGHUP"); break; |
| #endif |
| #ifdef SIGQUIT |
| case SIGQUIT: KWSYSPE_CASE(Other, "SIGQUIT"); break; |
| #endif |
| #ifdef SIGTRAP |
| case SIGTRAP: KWSYSPE_CASE(Other, "SIGTRAP"); break; |
| #endif |
| #ifdef SIGIOT |
| # if !defined(SIGABRT) || SIGIOT != SIGABRT |
| case SIGIOT: KWSYSPE_CASE(Other, "SIGIOT"); break; |
| # endif |
| #endif |
| #ifdef SIGUSR1 |
| case SIGUSR1: KWSYSPE_CASE(Other, "SIGUSR1"); break; |
| #endif |
| #ifdef SIGUSR2 |
| case SIGUSR2: KWSYSPE_CASE(Other, "SIGUSR2"); break; |
| #endif |
| #ifdef SIGPIPE |
| case SIGPIPE: KWSYSPE_CASE(Other, "SIGPIPE"); break; |
| #endif |
| #ifdef SIGALRM |
| case SIGALRM: KWSYSPE_CASE(Other, "SIGALRM"); break; |
| #endif |
| #ifdef SIGSTKFLT |
| case SIGSTKFLT: KWSYSPE_CASE(Other, "SIGSTKFLT"); break; |
| #endif |
| #ifdef SIGCHLD |
| case SIGCHLD: KWSYSPE_CASE(Other, "SIGCHLD"); break; |
| #elif defined(SIGCLD) |
| case SIGCLD: KWSYSPE_CASE(Other, "SIGCLD"); break; |
| #endif |
| #ifdef SIGCONT |
| case SIGCONT: KWSYSPE_CASE(Other, "SIGCONT"); break; |
| #endif |
| #ifdef SIGSTOP |
| case SIGSTOP: KWSYSPE_CASE(Other, "SIGSTOP"); break; |
| #endif |
| #ifdef SIGTSTP |
| case SIGTSTP: KWSYSPE_CASE(Other, "SIGTSTP"); break; |
| #endif |
| #ifdef SIGTTIN |
| case SIGTTIN: KWSYSPE_CASE(Other, "SIGTTIN"); break; |
| #endif |
| #ifdef SIGTTOU |
| case SIGTTOU: KWSYSPE_CASE(Other, "SIGTTOU"); break; |
| #endif |
| #ifdef SIGURG |
| case SIGURG: KWSYSPE_CASE(Other, "SIGURG"); break; |
| #endif |
| #ifdef SIGXCPU |
| case SIGXCPU: KWSYSPE_CASE(Other, "SIGXCPU"); break; |
| #endif |
| #ifdef SIGXFSZ |
| case SIGXFSZ: KWSYSPE_CASE(Other, "SIGXFSZ"); break; |
| #endif |
| #ifdef SIGVTALRM |
| case SIGVTALRM: KWSYSPE_CASE(Other, "SIGVTALRM"); break; |
| #endif |
| #ifdef SIGPROF |
| case SIGPROF: KWSYSPE_CASE(Other, "SIGPROF"); break; |
| #endif |
| #ifdef SIGWINCH |
| case SIGWINCH: KWSYSPE_CASE(Other, "SIGWINCH"); break; |
| #endif |
| #ifdef SIGPOLL |
| case SIGPOLL: KWSYSPE_CASE(Other, "SIGPOLL"); break; |
| #endif |
| #ifdef SIGIO |
| # if !defined(SIGPOLL) || SIGIO != SIGPOLL |
| case SIGIO: KWSYSPE_CASE(Other, "SIGIO"); break; |
| # endif |
| #endif |
| #ifdef SIGPWR |
| case SIGPWR: KWSYSPE_CASE(Other, "SIGPWR"); break; |
| #endif |
| #ifdef SIGSYS |
| case SIGSYS: KWSYSPE_CASE(Other, "SIGSYS"); break; |
| #endif |
| #ifdef SIGUNUSED |
| # if !defined(SIGSYS) || SIGUNUSED != SIGSYS |
| case SIGUNUSED: KWSYSPE_CASE(Other, "SIGUNUSED"); break; |
| # endif |
| #endif |
| default: |
| cp->ExitException = kwsysProcess_Exception_Other; |
| sprintf(cp->ExitExceptionString, "Signal %d", sig); |
| break; |
| } |
| } |
| #undef KWSYSPE_CASE |
| |
| /*--------------------------------------------------------------------------*/ |
| /* When the child process encounters an error before its program is |
| invoked, this is called to report the error to the parent and |
| exit. */ |
| static void kwsysProcessChildErrorExit(int errorPipe) |
| { |
| /* Construct the error message. */ |
| char buffer[KWSYSPE_PIPE_BUFFER_SIZE]; |
| kwsysProcess_ssize_t result; |
| strncpy(buffer, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); |
| |
| /* Report the error to the parent through the special pipe. */ |
| result=write(errorPipe, buffer, strlen(buffer)); |
| (void)result; |
| |
| /* Terminate without cleanup. */ |
| _exit(1); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Restores all signal handlers to their default values. */ |
| static void kwsysProcessRestoreDefaultSignalHandlers(void) |
| { |
| struct sigaction act; |
| memset(&act, 0, sizeof(struct sigaction)); |
| act.sa_handler = SIG_DFL; |
| #ifdef SIGHUP |
| sigaction(SIGHUP, &act, 0); |
| #endif |
| #ifdef SIGINT |
| sigaction(SIGINT, &act, 0); |
| #endif |
| #ifdef SIGQUIT |
| sigaction(SIGQUIT, &act, 0); |
| #endif |
| #ifdef SIGILL |
| sigaction(SIGILL, &act, 0); |
| #endif |
| #ifdef SIGTRAP |
| sigaction(SIGTRAP, &act, 0); |
| #endif |
| #ifdef SIGABRT |
| sigaction(SIGABRT, &act, 0); |
| #endif |
| #ifdef SIGIOT |
| sigaction(SIGIOT, &act, 0); |
| #endif |
| #ifdef SIGBUS |
| sigaction(SIGBUS, &act, 0); |
| #endif |
| #ifdef SIGFPE |
| sigaction(SIGFPE, &act, 0); |
| #endif |
| #ifdef SIGUSR1 |
| sigaction(SIGUSR1, &act, 0); |
| #endif |
| #ifdef SIGSEGV |
| sigaction(SIGSEGV, &act, 0); |
| #endif |
| #ifdef SIGUSR2 |
| sigaction(SIGUSR2, &act, 0); |
| #endif |
| #ifdef SIGPIPE |
| sigaction(SIGPIPE, &act, 0); |
| #endif |
| #ifdef SIGALRM |
| sigaction(SIGALRM, &act, 0); |
| #endif |
| #ifdef SIGTERM |
| sigaction(SIGTERM, &act, 0); |
| #endif |
| #ifdef SIGSTKFLT |
| sigaction(SIGSTKFLT, &act, 0); |
| #endif |
| #ifdef SIGCLD |
| sigaction(SIGCLD, &act, 0); |
| #endif |
| #ifdef SIGCHLD |
| sigaction(SIGCHLD, &act, 0); |
| #endif |
| #ifdef SIGCONT |
| sigaction(SIGCONT, &act, 0); |
| #endif |
| #ifdef SIGTSTP |
| sigaction(SIGTSTP, &act, 0); |
| #endif |
| #ifdef SIGTTIN |
| sigaction(SIGTTIN, &act, 0); |
| #endif |
| #ifdef SIGTTOU |
| sigaction(SIGTTOU, &act, 0); |
| #endif |
| #ifdef SIGURG |
| sigaction(SIGURG, &act, 0); |
| #endif |
| #ifdef SIGXCPU |
| sigaction(SIGXCPU, &act, 0); |
| #endif |
| #ifdef SIGXFSZ |
| sigaction(SIGXFSZ, &act, 0); |
| #endif |
| #ifdef SIGVTALRM |
| sigaction(SIGVTALRM, &act, 0); |
| #endif |
| #ifdef SIGPROF |
| sigaction(SIGPROF, &act, 0); |
| #endif |
| #ifdef SIGWINCH |
| sigaction(SIGWINCH, &act, 0); |
| #endif |
| #ifdef SIGPOLL |
| sigaction(SIGPOLL, &act, 0); |
| #endif |
| #ifdef SIGIO |
| sigaction(SIGIO, &act, 0); |
| #endif |
| #ifdef SIGPWR |
| sigaction(SIGPWR, &act, 0); |
| #endif |
| #ifdef SIGSYS |
| sigaction(SIGSYS, &act, 0); |
| #endif |
| #ifdef SIGUNUSED |
| sigaction(SIGUNUSED, &act, 0); |
| #endif |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessExit(void) |
| { |
| _exit(0); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| #if !defined(__VMS) |
| static pid_t kwsysProcessFork(kwsysProcess* cp, |
| kwsysProcessCreateInformation* si) |
| { |
| /* Create a detached process if requested. */ |
| if(cp->OptionDetach) |
| { |
| /* Create an intermediate process. */ |
| pid_t middle_pid = fork(); |
| if(middle_pid < 0) |
| { |
| /* Fork failed. Return as if we were not detaching. */ |
| return middle_pid; |
| } |
| else if(middle_pid == 0) |
| { |
| /* This is the intermediate process. Create the real child. */ |
| pid_t child_pid = fork(); |
| if(child_pid == 0) |
| { |
| /* This is the real child process. There is nothing to do here. */ |
| return 0; |
| } |
| else |
| { |
| /* Use the error pipe to report the pid to the real parent. */ |
| while((write(si->ErrorPipe[1], &child_pid, sizeof(child_pid)) < 0) && |
| (errno == EINTR)); |
| |
| /* Exit without cleanup. The parent holds all resources. */ |
| kwsysProcessExit(); |
| return 0; /* Never reached, but avoids SunCC warning. */ |
| } |
| } |
| else |
| { |
| /* This is the original parent process. The intermediate |
| process will use the error pipe to report the pid of the |
| detached child. */ |
| pid_t child_pid; |
| int status; |
| while((read(si->ErrorPipe[0], &child_pid, sizeof(child_pid)) < 0) && |
| (errno == EINTR)); |
| |
| /* Wait for the intermediate process to exit and clean it up. */ |
| while((waitpid(middle_pid, &status, 0) < 0) && (errno == EINTR)); |
| return child_pid; |
| } |
| } |
| else |
| { |
| /* Not creating a detached process. Use normal fork. */ |
| return fork(); |
| } |
| } |
| #endif |
| |
| /*--------------------------------------------------------------------------*/ |
| /* We try to obtain process information by invoking the ps command. |
| Here we define the command to call on each platform and the |
| corresponding parsing format string. The parsing format should |
| have two integers to store: the pid and then the ppid. */ |
| #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) \ |
| || defined(__OpenBSD__) || defined(__GLIBC__) || defined(__GNU__) |
| # define KWSYSPE_PS_COMMAND "ps axo pid,ppid" |
| # define KWSYSPE_PS_FORMAT "%d %d\n" |
| #elif defined(__sun) && (defined(__SVR4) || defined(__svr4__)) /* Solaris */ |
| # define KWSYSPE_PS_COMMAND "ps -e -o pid,ppid" |
| # define KWSYSPE_PS_FORMAT "%d %d\n" |
| #elif defined(__hpux) || defined(__sun__) || defined(__sgi) || defined(_AIX) \ |
| || defined(__sparc) |
| # define KWSYSPE_PS_COMMAND "ps -ef" |
| # define KWSYSPE_PS_FORMAT "%*s %d %d %*[^\n]\n" |
| #elif defined(__QNX__) |
| # define KWSYSPE_PS_COMMAND "ps -Af" |
| # define KWSYSPE_PS_FORMAT "%*d %d %d %*[^\n]\n" |
| #elif defined(__CYGWIN__) |
| # define KWSYSPE_PS_COMMAND "ps aux" |
| # define KWSYSPE_PS_FORMAT "%d %d %*[^\n]\n" |
| #endif |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessKill(pid_t process_id) |
| { |
| #if defined(__linux__) || defined(__CYGWIN__) |
| DIR* procdir; |
| #endif |
| |
| /* Suspend the process to be sure it will not create more children. */ |
| kill(process_id, SIGSTOP); |
| |
| #if defined(__CYGWIN__) |
| /* Some Cygwin versions seem to need help here. Give up our time slice |
| so that the child can process SIGSTOP before we send SIGKILL. */ |
| usleep(1); |
| #endif |
| |
| /* Kill all children if we can find them. */ |
| #if defined(__linux__) || defined(__CYGWIN__) |
| /* First try using the /proc filesystem. */ |
| if((procdir = opendir("/proc")) != NULL) |
| { |
| #if defined(MAXPATHLEN) |
| char fname[MAXPATHLEN]; |
| #elif defined(PATH_MAX) |
| char fname[PATH_MAX]; |
| #else |
| char fname[4096]; |
| #endif |
| char buffer[KWSYSPE_PIPE_BUFFER_SIZE+1]; |
| struct dirent* d; |
| |
| /* Each process has a directory in /proc whose name is the pid. |
| Within this directory is a file called stat that has the |
| following format: |
| |
| pid (command line) status ppid ... |
| |
| We want to get the ppid for all processes. Those that have |
| process_id as their parent should be recursively killed. */ |
| for(d = readdir(procdir); d; d = readdir(procdir)) |
| { |
| int pid; |
| if(sscanf(d->d_name, "%d", &pid) == 1 && pid != 0) |
| { |
| struct stat finfo; |
| sprintf(fname, "/proc/%d/stat", pid); |
| if(stat(fname, &finfo) == 0) |
| { |
| FILE* f = fopen(fname, "r"); |
| if(f) |
| { |
| size_t nread = fread(buffer, 1, KWSYSPE_PIPE_BUFFER_SIZE, f); |
| fclose(f); |
| buffer[nread] = '\0'; |
| if(nread > 0) |
| { |
| const char* rparen = strrchr(buffer, ')'); |
| int ppid; |
| if(rparen && (sscanf(rparen+1, "%*s %d", &ppid) == 1)) |
| { |
| if(ppid == process_id) |
| { |
| /* Recursively kill this child and its children. */ |
| kwsysProcessKill(pid); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| closedir(procdir); |
| } |
| else |
| #endif |
| { |
| #if defined(KWSYSPE_PS_COMMAND) |
| /* Try running "ps" to get the process information. */ |
| FILE* ps = popen(KWSYSPE_PS_COMMAND, "r"); |
| |
| /* Make sure the process started and provided a valid header. */ |
| if(ps && fscanf(ps, "%*[^\n]\n") != EOF) |
| { |
| /* Look for processes whose parent is the process being killed. */ |
| int pid, ppid; |
| while(fscanf(ps, KWSYSPE_PS_FORMAT, &pid, &ppid) == 2) |
| { |
| if(ppid == process_id) |
| { |
| /* Recursively kill this child and its children. */ |
| kwsysProcessKill(pid); |
| } |
| } |
| } |
| |
| /* We are done with the ps process. */ |
| if(ps) |
| { |
| pclose(ps); |
| } |
| #endif |
| } |
| |
| /* Kill the process. */ |
| kill(process_id, SIGKILL); |
| |
| #if defined(__APPLE__) |
| /* On OS X 10.3 the above SIGSTOP occasionally prevents the SIGKILL |
| from working. Just in case, we resume the child and kill it |
| again. There is a small race condition in this obscure case. If |
| the child manages to fork again between these two signals, we |
| will not catch its children. */ |
| kill(process_id, SIGCONT); |
| kill(process_id, SIGKILL); |
| #endif |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| #if defined(__VMS) |
| int decc$feature_get_index(const char* name); |
| int decc$feature_set_value(int index, int mode, int value); |
| static int kwsysProcessSetVMSFeature(const char* name, int value) |
| { |
| int i; |
| errno = 0; |
| i = decc$feature_get_index(name); |
| return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0); |
| } |
| #endif |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Global set of executing processes for use by the signal handler. |
| This global instance will be zero-initialized by the compiler. */ |
| typedef struct kwsysProcessInstances_s |
| { |
| int Count; |
| int Size; |
| kwsysProcess** Processes; |
| } kwsysProcessInstances; |
| static kwsysProcessInstances kwsysProcesses; |
| |
| /* The old SIGCHLD / SIGINT / SIGTERM handlers. */ |
| static struct sigaction kwsysProcessesOldSigChldAction; |
| static struct sigaction kwsysProcessesOldSigIntAction; |
| static struct sigaction kwsysProcessesOldSigTermAction; |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses) |
| { |
| /* Block signals while we update the set of pipes to check. |
| TODO: sigprocmask is undefined for threaded apps. See |
| pthread_sigmask. */ |
| sigset_t newset; |
| sigset_t oldset; |
| sigemptyset(&newset); |
| sigaddset(&newset, SIGCHLD); |
| sigaddset(&newset, SIGINT); |
| sigaddset(&newset, SIGTERM); |
| sigprocmask(SIG_BLOCK, &newset, &oldset); |
| |
| /* Store the new set in that seen by the signal handler. */ |
| kwsysProcesses = *newProcesses; |
| |
| /* Restore the signal mask to the previous setting. */ |
| sigprocmask(SIG_SETMASK, &oldset, 0); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcessesAdd(kwsysProcess* cp) |
| { |
| /* Create a pipe through which the signal handler can notify the |
| given process object that a child has exited. */ |
| { |
| /* Create the pipe. */ |
| int p[2]; |
| if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) |
| { |
| return 0; |
| } |
| |
| /* Store the pipes now to be sure they are cleaned up later. */ |
| cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL] = p[0]; |
| cp->SignalPipe = p[1]; |
| |
| /* Switch the pipe to non-blocking mode so that reading a byte can |
| be an atomic test-and-set. */ |
| if(!kwsysProcessSetNonBlocking(p[0]) || |
| !kwsysProcessSetNonBlocking(p[1])) |
| { |
| return 0; |
| } |
| |
| /* The children do not need this pipe. Set close-on-exec flag on |
| the pipe's ends. */ |
| if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || |
| (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) |
| { |
| return 0; |
| } |
| } |
| |
| /* Attempt to add the given signal pipe to the signal handler set. */ |
| { |
| |
| /* Make sure there is enough space for the new signal pipe. */ |
| kwsysProcessInstances oldProcesses = kwsysProcesses; |
| kwsysProcessInstances newProcesses = oldProcesses; |
| if(oldProcesses.Count == oldProcesses.Size) |
| { |
| /* Start with enough space for a small number of process instances |
| and double the size each time more is needed. */ |
| newProcesses.Size = oldProcesses.Size? oldProcesses.Size*2 : 4; |
| |
| /* Try allocating the new block of memory. */ |
| if((newProcesses.Processes = ((kwsysProcess**) |
| malloc((size_t)(newProcesses.Size)* |
| sizeof(kwsysProcess*))))) |
| { |
| /* Copy the old pipe set to the new memory. */ |
| if(oldProcesses.Count > 0) |
| { |
| memcpy(newProcesses.Processes, oldProcesses.Processes, |
| ((size_t)(oldProcesses.Count) * sizeof(kwsysProcess*))); |
| } |
| } |
| else |
| { |
| /* Failed to allocate memory for the new signal pipe set. */ |
| return 0; |
| } |
| } |
| |
| /* Append the new signal pipe to the set. */ |
| newProcesses.Processes[newProcesses.Count++] = cp; |
| |
| /* Store the new set in that seen by the signal handler. */ |
| kwsysProcessesUpdate(&newProcesses); |
| |
| /* Free the original pipes if new ones were allocated. */ |
| if(newProcesses.Processes != oldProcesses.Processes) |
| { |
| free(oldProcesses.Processes); |
| } |
| |
| /* If this is the first process, enable the signal handler. */ |
| if(newProcesses.Count == 1) |
| { |
| /* Install our handler for SIGCHLD. Repeat call until it is not |
| interrupted. */ |
| struct sigaction newSigAction; |
| memset(&newSigAction, 0, sizeof(struct sigaction)); |
| #if KWSYSPE_USE_SIGINFO |
| newSigAction.sa_sigaction = kwsysProcessesSignalHandler; |
| newSigAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; |
| # ifdef SA_RESTART |
| newSigAction.sa_flags |= SA_RESTART; |
| # endif |
| #else |
| newSigAction.sa_handler = kwsysProcessesSignalHandler; |
| newSigAction.sa_flags = SA_NOCLDSTOP; |
| #endif |
| sigemptyset(&newSigAction.sa_mask); |
| while((sigaction(SIGCHLD, &newSigAction, |
| &kwsysProcessesOldSigChldAction) < 0) && |
| (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)); |
| } |
| } |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessesRemove(kwsysProcess* cp) |
| { |
| /* Attempt to remove the given signal pipe from the signal handler set. */ |
| { |
| /* Find the given process in the set. */ |
| kwsysProcessInstances newProcesses = kwsysProcesses; |
| int i; |
| for(i=0; i < newProcesses.Count; ++i) |
| { |
| if(newProcesses.Processes[i] == cp) |
| { |
| break; |
| } |
| } |
| if(i < newProcesses.Count) |
| { |
| /* Remove the process from the set. */ |
| --newProcesses.Count; |
| for(; i < newProcesses.Count; ++i) |
| { |
| newProcesses.Processes[i] = newProcesses.Processes[i+1]; |
| } |
| |
| /* If this was the last process, disable the signal handler. */ |
| if(newProcesses.Count == 0) |
| { |
| /* Restore the signal handlers. Repeat call until it is not |
| interrupted. */ |
| while((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) && |
| (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. |
| This is safe because the signal handler has been removed. */ |
| newProcesses.Size = 0; |
| free(newProcesses.Processes); |
| newProcesses.Processes = 0; |
| } |
| |
| /* Store the new set in that seen by the signal handler. */ |
| kwsysProcessesUpdate(&newProcesses); |
| } |
| } |
| |
| /* Close the pipe through which the signal handler may have notified |
| the given process object that a child has exited. */ |
| kwsysProcessCleanupDescriptor(&cp->SignalPipe); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessesSignalHandler(int signum |
| #if KWSYSPE_USE_SIGINFO |
| , siginfo_t* info, void* ucontext |
| #endif |
| ) |
| { |
| int i, j, procStatus, old_errno = errno; |
| #if KWSYSPE_USE_SIGINFO |
| (void)info; |
| (void)ucontext; |
| #endif |
| |
| /* Signal all process objects that a child has terminated. */ |
| switch(signum) |
| { |
| case SIGCHLD: |
| for(i=0; i < kwsysProcesses.Count; ++i) |
| { |
| /* Set the pipe in a signalled state. */ |
| char buf = 1; |
| kwsysProcess* cp = kwsysProcesses.Processes[i]; |
| kwsysProcess_ssize_t pipeStatus= |
| 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 |
| /* Re-Install our handler. Repeat call until it is not interrupted. */ |
| { |
| struct sigaction newSigAction; |
| struct sigaction &oldSigAction; |
| memset(&newSigAction, 0, sizeof(struct sigaction)); |
| newSigChldAction.sa_handler = kwsysProcessesSignalHandler; |
| newSigChldAction.sa_flags = SA_NOCLDSTOP; |
| sigemptyset(&newSigAction.sa_mask); |
| 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)); |
| } |
| #endif |
| |
| errno = old_errno; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_ResetStartTime(kwsysProcess* cp) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| /* Reset start time. */ |
| cp->StartTime = kwsysProcessTimeGetCurrent(); |
| } |