about summary refs log tree commit diff
path: root/lib/libsystem.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libsystem.c')
-rw-r--r--lib/libsystem.c276
1 files changed, 231 insertions, 45 deletions
diff --git a/lib/libsystem.c b/lib/libsystem.c
index fd3c52ec..bf2416a4 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -1,18 +1,23 @@
 /*=============================================================================
                                  pm_system
 ===============================================================================
-   This is the library subroutine pm_system().  It is just like Standard C
-   Library system(), except that you can supply routines for it to run to
-   generate the Standard Input for the executed shell command and to accept
-   the Standard Output from it.  system(), by contrast, always sets up the
-   current Standard Input and Standard Output as the Standard Input and
-   Standard Output of the shell command.
+   This is the pm_system() family of subroutines.
+
+   pm_system() is just like Standard C Library system(), except that you can
+   supply routines for it to run to generate the Standard Input for the
+   executed shell command and to accept the Standard Output from it.
+   system(), by contrast, always sets up the current Standard Input and
+   Standard Output as the Standard Input and Standard Output of the shell
+   command.
+
+   pm_system_lp() and pm_system_vp() are similar, but exec an OS-level program
+   (i.e. exec a program) rather than run a shell command.
 
    By Bryan Henderson, San Jose CA  2002.12.14.
 
    Contributed to the public domain.
 =============================================================================*/
-#define _XOPEN_SOURCE
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE  /* Make SIGWINCH defined on OpenBSD */
 
 #include <stdarg.h>
@@ -26,6 +31,7 @@
 
 #include "netpbm/pm_c_util.h"
 #include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 #include "pm.h"
 #include "pm_system.h"
 
@@ -164,7 +170,7 @@ spawnProcessor(const char *  const progName,
 /*----------------------------------------------------------------------------
    Create a process to run program 'progName' with arguments
    argArray[] (terminated by NULL element).  Pass file descriptor
-   'stdinFd' to the shell as Standard Input.
+   'stdinFd' to the process as Standard Input.
 
    if 'stdoutFdP' is NULL, have that process write its Standard Output to
    the current process' Standard Output.
@@ -313,26 +319,44 @@ signalName(unsigned int const signalClass) {
 
 
 
-static void
-cleanupProcessorProcess(pid_t const processorPid) {
+const char *
+pm_termStatusDesc(int const termStatusArg) {
+/*----------------------------------------------------------------------------
+   English description of  process termination status 'termStatus'.
+-----------------------------------------------------------------------------*/
+    const char * retval;
+
+    /* WIFEXITED, etc. do not work with a constant argument in older GNU C
+       library.  Compilation fails with "attempt to assign read-only
+       location".  This is because The GNU C library has some magic to allow
+       for a BSD 'union wait' (instead of int) argument to WIFEXITED.  The
+       magic involves defining a variable with 'typeof' the argument and
+       assigning to that variable.
+       
+       To work around this, we make sure the argument is not constant.
+    */
 
-    int terminationStatus;
-    waitpid(processorPid, &terminationStatus, 0);
+    int termStatus = termStatusArg;
 
-    if (WIFEXITED(terminationStatus)) {
-        int const exitStatus = WEXITSTATUS(terminationStatus);
+    if (WIFEXITED(termStatus)) {
+        int const exitStatus = WEXITSTATUS(termStatus);
 
-        if (exitStatus != 0)
-            pm_message("Shell process exited with abnormal exit status %u.  ",
-                       exitStatus);
-    } else if (WIFSIGNALED(terminationStatus)) {
-        pm_message("Shell process was killed by a Class %u (%s) signal.",
-                   WTERMSIG(terminationStatus),
-                   signalName(WTERMSIG(terminationStatus)));
+        if (exitStatus == 0)
+            pm_asprintf(&retval, "Process exited normally");
+        else
+            pm_asprintf(&retval,
+                        "Process exited with abnormal exit status %u.  ",
+                        exitStatus);
+    } else if (WIFSIGNALED(termStatus)) {
+        pm_asprintf(&retval, "Process was killed by a Class %u (%s) signal.",
+                    WTERMSIG(termStatus),
+                    signalName(WTERMSIG(termStatus)));
     } else {
-        pm_message("Shell process died, but its termination status "
-                   "0x%x  doesn't make sense", terminationStatus);
+        pm_asprintf(&retval, "Process died, but its termination status "
+                    "0x%x  doesn't make sense", termStatus);
     }
+
+    return retval;
 }
 
 
@@ -347,7 +371,7 @@ cleanupFeederProcess(pid_t const feederPid) {
         if (WTERMSIG(status) == SIGPIPE)
             pm_message("WARNING: "
                        "Standard Input feeder process was terminated by a "
-                       "SIGPIPE signal because the shell command closed its "
+                       "SIGPIPE signal because the program closed its "
                        "Standard Input before the Standard Input feeder was "
                        "through feeding it.");
         else
@@ -369,12 +393,13 @@ cleanupFeederProcess(pid_t const feederPid) {
 
 
 void
-pm_system_vp(const char *    const progName,
-             const char **   const argArray,
-             void stdinFeeder(int, void *),
-             void *          const feederParm,
-             void stdoutAccepter(int, void *),
-             void *          const accepterParm) {
+pm_system2_vp(const char *    const progName,
+              const char **   const argArray,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP) {
 /*----------------------------------------------------------------------------
    Run a program in a child process.  Feed its Standard Input with a
    pipe, which is fed by the routine 'stdinFeeder' with parameter
@@ -387,6 +412,9 @@ pm_system_vp(const char *    const progName,
 
    Run the program 'progName' with arguments argArray[] (terminated by NULL
    element).  That includes arg0.
+
+   Return as *termStatusP the termination status of the processor process
+   (the one running the program named 'progName').
 -----------------------------------------------------------------------------*/
     /* If 'stdinFeeder' is non-NULL, we create a child process to run
        'stdinFeeder' and create a pipe from that process as the
@@ -416,6 +444,7 @@ pm_system_vp(const char *    const progName,
         */
     pid_t feederPid;
     pid_t processorPid;
+    int termStatus;
 
     if (stdinFeeder) {
         createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid);
@@ -452,10 +481,118 @@ pm_system_vp(const char *    const progName,
         close(progStdinFd);
     }
 
-    cleanupProcessorProcess(processorPid);
+    waitpid(processorPid, &termStatus, 0);
 
     if (feederPid) 
         cleanupFeederProcess(feederPid);
+
+    *termStatusP = termStatus;
+}
+
+
+
+void
+pm_system2_lp(const char *    const progName,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP,
+              ...) {
+/*----------------------------------------------------------------------------
+  Same as pm_system_vp() except with arguments as variable arguments
+  instead of an array.
+
+  N.B. the first variable argument is the program's arg 0; the last
+  variable argument must be NULL.
+-----------------------------------------------------------------------------*/
+    va_list args;
+    bool endOfArgs;
+    const char ** argArray;
+    unsigned int n;
+
+    va_start(args, termStatusP);
+
+    endOfArgs = FALSE;
+    argArray = NULL;
+
+    for (endOfArgs = FALSE, argArray = NULL, n = 0;
+         !endOfArgs;
+        ) {
+        const char * const arg = va_arg(args, const char *);
+        
+        REALLOCARRAY(argArray, n+1);
+
+        argArray[n++] = arg;
+
+        if (!arg)
+            endOfArgs = TRUE;
+    }
+
+    va_end(args);
+
+    pm_system2_vp(progName, argArray,
+                  stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                  termStatusP);
+
+    free(argArray);
+}
+
+
+
+void
+pm_system2(void stdinFeeder(int, void *),
+           void *          const feederParm,
+           void stdoutAccepter(int, void *),
+           void *          const accepterParm,
+           const char *    const shellCommand,
+           int *           const termStatusP) {
+/*----------------------------------------------------------------------------
+   Run a shell and have it run command 'shellCommand'.  Feed its
+   Standard Input with a pipe, which is fed by the routine
+   'stdinFeeder' with parameter 'feederParm'.  Process its Standard
+   Output with the routine 'stdoutAccepter' with parameter 'accepterParm'.
+
+   But if 'stdinFeeder' is NULL, just feed the shell our own Standard
+   Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
+   to our own Standard Output.
+
+   Return as *termStatusP the termination status of the processor process
+   (the one running the program named 'progName').
+-----------------------------------------------------------------------------*/
+    pm_system2_lp("/bin/sh", 
+                  stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                  termStatusP,
+                  "sh", "-c", shellCommand, NULL);
+}
+
+
+
+void
+pm_system_vp(const char *    const progName,
+             const char **   const argArray,
+             void stdinFeeder(int, void *),
+             void *          const feederParm,
+             void stdoutAccepter(int, void *),
+             void *          const accepterParm) {
+/*----------------------------------------------------------------------------
+   Same as pm_system2_vp(), except instead of returning the termination
+   status, we just issue a message (pm_message) describing it.
+-----------------------------------------------------------------------------*/
+    int termStatus;
+
+    pm_system2_vp(progName, argArray,
+                  stdinFeeder, feederParm,
+                  stdoutAccepter, accepterParm,
+                  &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
+
+        pm_message("%s", msg);
+
+        pm_strfree(msg);
+    }
 }
 
 
@@ -468,8 +605,11 @@ pm_system_lp(const char *    const progName,
              void *          const accepterParm,
              ...) {
 /*----------------------------------------------------------------------------
-  same as pm_system_vp() except with arguments as variable arguments
+  Same as pm_system_vp() except with arguments as variable arguments
   instead of an array.
+
+  N.B. the first variable argument is the program's arg 0; the last
+  variable argument must be NULL.
 -----------------------------------------------------------------------------*/
     va_list args;
     bool endOfArgs;
@@ -511,19 +651,62 @@ pm_system(void stdinFeeder(int, void *),
           void *          const accepterParm,
           const char *    const shellCommand) {
 /*----------------------------------------------------------------------------
-   Run a shell and have it run command 'shellCommand'.  Feed its
-   Standard Input with a pipe, which is fed by the routine
-   'stdinFeeder' with parameter 'feederParm'.  Process its Standard
-   Output with the routine 'stdoutAccepter' with parameter 'accepterParm'.
-
-   But if 'stdinFeeder' is NULL, just feed the shell our own Standard
-   Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
-   to our own Standard Output.
+   Same as pm_system2(), except instead of returning the termination status,
+   we just issue a message (pm_message) describing it.
 -----------------------------------------------------------------------------*/
+    int termStatus;
+
+    pm_system2(stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+               shellCommand,
+               &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
+
+        pm_message("%s", msg);
+
+        pm_strfree(msg);
+    }
+}
+
+
+
+void
+pm_feed_null(int    const pipeToFeedFd,
+             void * const feederParm) {
+
+}
 
-    pm_system_lp("/bin/sh", 
-                 stdinFeeder, feederParm, stdoutAccepter, accepterParm,
-                 "sh", "-c", shellCommand, NULL);
+
+
+void
+pm_accept_null(int    const pipetosuckFd,
+               void * const accepterParm ) {
+
+    size_t const bufferSize = 4096;
+
+    unsigned char * buffer;
+
+    MALLOCARRAY(buffer, bufferSize);
+
+    if (buffer) {
+        bool eof;
+
+        for (eof = false; !eof; ) {
+            ssize_t rc;
+
+            rc = read(pipetosuckFd, buffer, bufferSize);
+
+            if (rc < 0) {
+                /* No way to report the problem; just say we're done */
+                eof = true;
+            } else if (rc == 0)
+                /* eof */
+                eof = true;
+        }
+        free(buffer);
+    }
+    close(pipetosuckFd);
 }
 
 
@@ -532,7 +715,7 @@ void
 pm_feed_from_memory(int    const pipeToFeedFd,
                     void * const feederParm) {
 
-    struct bufferDesc * const inputBufferP = feederParm;
+    pm_bufferDesc * const inputBufferP = feederParm;
     
     FILE * const outFileP = fdopen(pipeToFeedFd, "w");
     
@@ -556,7 +739,7 @@ void
 pm_accept_to_memory(int             const pipetosuckFd,
                     void *          const accepterParm ) {
 
-    struct bufferDesc * const outputBufferP = accepterParm;
+    pm_bufferDesc * const outputBufferP = accepterParm;
     
     FILE * const inFileP = fdopen(pipetosuckFd, "r");
 
@@ -570,3 +753,6 @@ pm_accept_to_memory(int             const pipetosuckFd,
     if (outputBufferP->bytesTransferredP)
         *(outputBufferP->bytesTransferredP) = bytesTransferred;
 }
+
+
+