From 48d72f120ca02990fbf567ddbf2321e4196cfa43 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
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.
---
 ProcessUNIX.c | 85 +++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 76 insertions(+), 9 deletions(-)

diff --git a/ProcessUNIX.c b/ProcessUNIX.c
index 49ad380..0d3946e 100644
--- a/ProcessUNIX.c
+++ b/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;
     }
-- 
GitLab