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__
#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;
}