From 312ca9670f4061660c68f2ca879ba5dcd18ec1a1 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 10 Jun 2009 11:48:34 -0400 Subject: [PATCH] ENH: Teach KWSys Process basic VMS support This achieves basic process execution on OpenVMS. We use work-arounds for different fork()/exec() behavior and a lack of select(). VMS emulates fork/exec using setjmp/longjmp to evaluate the child and parent return cases from fork. Therefore both must be invoked from the same function. Since select() works only for sockets we use the BeOS-style polling implementation. However, non-blocking reads on empty pipes cannot be distinguished easily from the last read on a closed pipe. Therefore we identify end of data by an empty read after the child terminates. --- Source/kwsys/ProcessUNIX.c | 85 ++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index d4d7fa1bd..ec0d11da1 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -67,6 +67,12 @@ do. #undef __BEOS__ #endif +#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 @@ -100,7 +106,7 @@ static inline void kwsysProcess_usleep(unsigned int msec) * pipes' file handles to be non-blocking and just poll them directly * without select(). */ -#if !defined(__BEOS__) +#if !defined(__BEOS__) && !defined(__VMS) # define KWSYSPE_USE_SELECT 1 #endif @@ -170,6 +176,7 @@ static void kwsysProcessRestoreDefaultSignalHandlers(void); static pid_t kwsysProcessFork(kwsysProcess* cp, kwsysProcessCreateInformation* si); static void kwsysProcessKill(pid_t process_id); +static int kwsysProcessSetVMSFeature(char* name, int value); static int kwsysProcessesAdd(kwsysProcess* cp); static void kwsysProcessesRemove(kwsysProcess* cp); #if KWSYSPE_USE_SIGINFO @@ -720,6 +727,13 @@ void kwsysProcess_Execute(kwsysProcess* cp) return; } + /* Make sure pipes behave like streams on VMS. */ + if(!kwsysProcessSetVMSFeature("DECC$STREAM_PIPE", 1)) + { + kwsysProcessCleanup(cp, 1); + 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. */ @@ -759,7 +773,7 @@ void kwsysProcess_Execute(kwsysProcess* cp) { /* Create the pipe. */ int p[2]; - if(pipe(p) < 0) + if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { kwsysProcessCleanup(cp, 1); return; @@ -1185,11 +1199,24 @@ static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length, else if (n == 0) /* EOF */ { /* We are done reading from this pipe. */ - kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); - --cp->PipesLeft; +#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), @@ -1565,6 +1592,11 @@ static int kwsysProcessSetNonBlocking(int fd) 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, int* readEnd) @@ -1616,7 +1648,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, { /* Create the pipe. */ int p[2]; - if(pipe(p) < 0) + if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { return 0; } @@ -1674,7 +1706,14 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, } /* 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. */ + cp->ForkPIDs[prIndex] = vfork(); +#else cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si); +#endif if(cp->ForkPIDs[prIndex] < 0) { return 0; @@ -1682,6 +1721,10 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, 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 pipe. */ close(si->ErrorPipe[0]); @@ -1711,14 +1754,21 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, /* Restore all default signal handlers. */ kwsysProcessRestoreDefaultSignalHandlers(); +#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? */ /* 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 + /* A child has been created. */ ++cp->CommandsLeft; @@ -2266,9 +2316,6 @@ static pid_t kwsysProcessFork(kwsysProcess* cp, if(cp->OptionDetach) { /* Create an intermediate process. */ -#ifdef __VMS -#define fork vfork -#endif pid_t middle_pid = fork(); if(middle_pid < 0) { @@ -2436,6 +2483,26 @@ static void kwsysProcessKill(pid_t process_id) } } +/*--------------------------------------------------------------------------*/ +#if defined(__VMS) +int decc$feature_get_index(char *name); +int decc$feature_set_value(int index, int mode, int value); +static int kwsysProcessSetVMSFeature(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); +} +#else +static int kwsysProcessSetVMSFeature(char* name, int value) +{ + (void)name; + (void)value; + return 1; +} +#endif + /*--------------------------------------------------------------------------*/ /* Global set of executing processes for use by the signal handler. This global instance will be zero-initialized by the compiler. */ @@ -2477,7 +2544,7 @@ static int kwsysProcessesAdd(kwsysProcess* cp) { /* Create the pipe. */ int p[2]; - if(pipe(p) < 0) + if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { return 0; }