diff options
Diffstat (limited to 'Source/kwsys/ProcessUNIX.c')
-rw-r--r-- | Source/kwsys/ProcessUNIX.c | 207 |
1 files changed, 185 insertions, 22 deletions
diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index 6888d59..42c9dad 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -86,6 +86,7 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanupDescriptor(int* pfd); static int kwsysProcessCreate(kwsysProcess* cp, int index, kwsysProcessCreateInformation* si, int* readEnd); +static int kwsysProcessSetupOutputPipeFile(int* p, const char* name); static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, kwsysProcessTime* timeoutTime); static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, @@ -164,6 +165,15 @@ struct kwsysProcess_s /* 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; + + /* The real working directory of this process. */ + int RealWorkingDirectoryLength; + char* RealWorkingDirectory; }; /*--------------------------------------------------------------------------*/ @@ -198,6 +208,9 @@ void kwsysProcess_Delete(kwsysProcess* cp) /* 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); @@ -322,19 +335,19 @@ void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout) } /*--------------------------------------------------------------------------*/ -void kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) +int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) { if(!cp) { - return; + return 0; } if(cp->WorkingDirectory == dir) { - return; + return 1; } if(cp->WorkingDirectory && dir && strcmp(cp->WorkingDirectory, dir) == 0) { - return; + return 1; } if(cp->WorkingDirectory) { @@ -344,8 +357,45 @@ void kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) 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 pipe, const char* file) +{ + char** pfile; + if(!cp) + { + return 0; + } + switch(pipe) + { + 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 = malloc(strlen(file)+1); + if(!*pfile) + { + return 0; + } + strcpy(*pfile, file); + } + return 1; } /*--------------------------------------------------------------------------*/ @@ -423,6 +473,27 @@ void kwsysProcess_Execute(kwsysProcess* cp) return; } + /* 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, 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); + } + } + /* We want no special handling of SIGCHLD. Repeat call until it is not interrupted. */ memset(&newSigChldAction, 0, sizeof(struct sigaction)); @@ -463,6 +534,20 @@ void kwsysProcess_Execute(kwsysProcess* cp) } } + /* Replace the stderr pipe with a file if requested. In this case + the select call will report that stderr is closed immediately. */ + if(cp->PipeFileSTDERR) + { + if(!kwsysProcessSetupOutputPipeFile(&si.StdErr, cp->PipeFileSTDERR)) + { + kwsysProcessCleanup(cp, 1); + kwsysProcessCleanupDescriptor(&si.StdErr); + kwsysProcessCleanupDescriptor(&si.TermPipe); + return; + } + } + + /* The timeout period starts now. */ cp->StartTime = kwsysProcessTimeGetCurrent(); cp->TimeoutTime.tv_sec = -1; @@ -479,11 +564,11 @@ void kwsysProcess_Execute(kwsysProcess* cp) /* Release resources that may have been allocated for this process before an error occurred. */ - kwsysProcessCleanupDescriptor(&readEnd); - if(i > 0) + if(i > 0 || si.StdIn > 0) { kwsysProcessCleanupDescriptor(&si.StdIn); } + kwsysProcessCleanupDescriptor(&readEnd); kwsysProcessCleanupDescriptor(&si.StdOut); kwsysProcessCleanupDescriptor(&si.StdErr); kwsysProcessCleanupDescriptor(&si.TermPipe); @@ -500,6 +585,16 @@ void kwsysProcess_Execute(kwsysProcess* cp) kwsysProcessCleanupDescriptor(&si.StdErr); kwsysProcessCleanupDescriptor(&si.TermPipe); + /* 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; @@ -895,6 +990,22 @@ static int kwsysProcessInitialize(kwsysProcess* cp) } memset(cp->CommandExitCodes, 0, sizeof(int)*cp->NumberOfCommands); + /* Allocate memory to save the real working directory. */ + { +#if defined(MAXPATHLEN) + cp->RealWorkingDirectoryLength = MAXPATHLEN; +#elif defined(PATH_MAX) + cp->RealWorkingDirectoryLength = PATH_MAX; +#else + cp->RealWorkingDirectoryLength = 4096; +#endif + cp->RealWorkingDirectory = malloc(cp->RealWorkingDirectoryLength); + if(!cp->RealWorkingDirectory) + { + return 0; + } + } + return 1; } @@ -928,6 +1039,12 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error) } } } + + /* Restore the working directory. */ + if(cp->RealWorkingDirectory) + { + while((chdir(cp->RealWorkingDirectory) < 0) && (errno == EINTR)); + } } /* Restore the SIGCHLD handler. */ @@ -940,6 +1057,11 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error) free(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) @@ -971,6 +1093,21 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index, si->StdIn = *readEnd; *readEnd = 0; } + else if(cp->PipeFileSTDIN) + { + /* Open a file for the child's stdin to read. */ + si->StdIn = open(cp->PipeFileSTDIN, O_RDONLY); + if(si->StdIn < 0) + { + return 0; + } + + /* Set close-on-exec flag on the pipe's end. */ + if(fcntl(si->StdIn, F_SETFD, FD_CLOEXEC) < 0) + { + return 0; + } + } else { si->StdIn = 0; @@ -995,6 +1132,16 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index, } } + /* Replace the stdout pipe with a file if requested. In this case + the select call will report that stdout is closed immediately. */ + if(index == cp->NumberOfCommands-1 && cp->PipeFileSTDOUT) + { + if(!kwsysProcessSetupOutputPipeFile(&si->StdOut, cp->PipeFileSTDOUT)) + { + return 0; + } + } + /* Create the error reporting pipe. */ if(pipe(si->ErrorPipe) < 0) { @@ -1020,7 +1167,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index, close(si->ErrorPipe[0]); /* Setup the stdin, stdout, and stderr pipes. */ - if(index > 0) + if(index > 0 || si->StdIn > 0) { dup2(si->StdIn, 0); } @@ -1038,20 +1185,6 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index, /* Restore all default signal handlers. */ kwsysProcessRestoreDefaultSignalHandlers(); - /* Change to the working directory specified, if any. */ - if(cp->WorkingDirectory) - { - /* Some platforms specify that the chdir call may be - interrupted. Repeat the call until it finishes. */ - int r; - while(((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR)); - if(r < 0) - { - /* Failure. Report error to parent and terminate. */ - kwsysProcessChildErrorExit(si->ErrorPipe[1]); - } - } - /* Execute the real process. If successful, this does not return. */ execvp(cp->Commands[index][0], cp->Commands[index]); @@ -1091,7 +1224,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index, } /* Successfully created this child process. */ - if(index > 0) + if(index > 0 || si->StdIn > 0) { /* The parent process does not need the input pipe read end. */ kwsysProcessCleanupDescriptor(&si->StdIn); @@ -1104,6 +1237,36 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index, } /*--------------------------------------------------------------------------*/ +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 (permissions 644). */ + if((fout = open(name, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 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; +} + +/*--------------------------------------------------------------------------*/ /* 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, |