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.
This commit is contained in:
Brad King 2009-06-10 11:48:34 -04:00
parent 1eec4fe6ad
commit 312ca9670f
1 changed files with 76 additions and 9 deletions

View File

@ -67,6 +67,12 @@ do.
#undef __BEOS__ #undef __BEOS__
#endif #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 #if defined(KWSYS_C_HAS_PTRDIFF_T) && KWSYS_C_HAS_PTRDIFF_T
typedef ptrdiff_t kwsysProcess_ptrdiff_t; typedef ptrdiff_t kwsysProcess_ptrdiff_t;
#else #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 * pipes' file handles to be non-blocking and just poll them directly
* without select(). * without select().
*/ */
#if !defined(__BEOS__) #if !defined(__BEOS__) && !defined(__VMS)
# define KWSYSPE_USE_SELECT 1 # define KWSYSPE_USE_SELECT 1
#endif #endif
@ -170,6 +176,7 @@ static void kwsysProcessRestoreDefaultSignalHandlers(void);
static pid_t kwsysProcessFork(kwsysProcess* cp, static pid_t kwsysProcessFork(kwsysProcess* cp,
kwsysProcessCreateInformation* si); kwsysProcessCreateInformation* si);
static void kwsysProcessKill(pid_t process_id); static void kwsysProcessKill(pid_t process_id);
static int kwsysProcessSetVMSFeature(char* name, int value);
static int kwsysProcessesAdd(kwsysProcess* cp); static int kwsysProcessesAdd(kwsysProcess* cp);
static void kwsysProcessesRemove(kwsysProcess* cp); static void kwsysProcessesRemove(kwsysProcess* cp);
#if KWSYSPE_USE_SIGINFO #if KWSYSPE_USE_SIGINFO
@ -720,6 +727,13 @@ void kwsysProcess_Execute(kwsysProcess* cp)
return; 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 /* Save the real working directory of this process and change to
the working directory for the child processes. This is needed the working directory for the child processes. This is needed
to make pipe file paths evaluate correctly. */ to make pipe file paths evaluate correctly. */
@ -759,7 +773,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
{ {
/* Create the pipe. */ /* Create the pipe. */
int p[2]; int p[2];
if(pipe(p) < 0) if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0)
{ {
kwsysProcessCleanup(cp, 1); kwsysProcessCleanup(cp, 1);
return; return;
@ -1185,11 +1199,24 @@ static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length,
else if (n == 0) /* EOF */ else if (n == 0) /* EOF */
{ {
/* We are done reading from this pipe. */ /* We are done reading from this pipe. */
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); #if defined(__VMS)
--cp->PipesLeft; if(!cp->CommandsLeft)
#endif
{
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
--cp->PipesLeft;
}
} }
else if (n < 0) /* error */ else if (n < 0) /* error */
{ {
#if defined(__VMS)
if(!cp->CommandsLeft)
{
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
--cp->PipesLeft;
}
else
#endif
if((errno != EINTR) && (errno != EAGAIN)) if((errno != EINTR) && (errno != EAGAIN))
{ {
strncpy(cp->ErrorMessage,strerror(errno), strncpy(cp->ErrorMessage,strerror(errno),
@ -1565,6 +1592,11 @@ static int kwsysProcessSetNonBlocking(int fd)
return flags >= 0; 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, static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
kwsysProcessCreateInformation* si, int* readEnd) kwsysProcessCreateInformation* si, int* readEnd)
@ -1616,7 +1648,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
{ {
/* Create the pipe. */ /* Create the pipe. */
int p[2]; int p[2];
if(pipe(p) < 0) if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0)
{ {
return 0; return 0;
} }
@ -1674,7 +1706,14 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
} }
/* Fork off a child process. */ /* 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); cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si);
#endif
if(cp->ForkPIDs[prIndex] < 0) if(cp->ForkPIDs[prIndex] < 0)
{ {
return 0; return 0;
@ -1682,6 +1721,10 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
if(cp->ForkPIDs[prIndex] == 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 pipe. */ /* Close the read end of the error reporting pipe. */
close(si->ErrorPipe[0]); close(si->ErrorPipe[0]);
@ -1711,14 +1754,21 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
/* Restore all default signal handlers. */ /* Restore all default signal handlers. */
kwsysProcessRestoreDefaultSignalHandlers(); kwsysProcessRestoreDefaultSignalHandlers();
#endif
/* Execute the real process. If successful, this does not return. */ /* Execute the real process. If successful, this does not return. */
execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]); execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]);
/* TODO: What does VMS do if the child fails to start? */
/* Failure. Report error to parent and terminate. */ /* Failure. Report error to parent and terminate. */
kwsysProcessChildErrorExit(si->ErrorPipe[1]); 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. */ /* A child has been created. */
++cp->CommandsLeft; ++cp->CommandsLeft;
@ -2266,9 +2316,6 @@ static pid_t kwsysProcessFork(kwsysProcess* cp,
if(cp->OptionDetach) if(cp->OptionDetach)
{ {
/* Create an intermediate process. */ /* Create an intermediate process. */
#ifdef __VMS
#define fork vfork
#endif
pid_t middle_pid = fork(); pid_t middle_pid = fork();
if(middle_pid < 0) 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. /* Global set of executing processes for use by the signal handler.
This global instance will be zero-initialized by the compiler. */ This global instance will be zero-initialized by the compiler. */
@ -2477,7 +2544,7 @@ static int kwsysProcessesAdd(kwsysProcess* cp)
{ {
/* Create the pipe. */ /* Create the pipe. */
int p[2]; int p[2];
if(pipe(p) < 0) if(pipe(p KWSYSPE_VMS_NONBLOCK) < 0)
{ {
return 0; return 0;
} }