From a7df43157443608716ad22badc18e5ba5f26e0f3 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 25 Aug 2007 22:42:49 +0000 Subject: Add pm_system_lp(), pm_system_vp() git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@383 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- lib/libsystem.c | 242 ++++++++++++++++++++++++++++++++++++++------------------ lib/pm_system.h | 17 +++- 2 files changed, 179 insertions(+), 80 deletions(-) diff --git a/lib/libsystem.c b/lib/libsystem.c index 59e05a5f..f0a1996a 100644 --- a/lib/libsystem.c +++ b/lib/libsystem.c @@ -14,6 +14,7 @@ =============================================================================*/ #define _XOPEN_SOURCE +#include #include #include #include @@ -22,6 +23,8 @@ #include #include +#include "pm_c_util.h" +#include "mallocvar.h" #include "pm.h" #include "pm_system.h" @@ -30,21 +33,25 @@ static void -execProgram(const char * const shellCommand, - int const inputPipeFd, - int const outputPipeFd) { +execProgram(const char * const progName, + const char ** const argArray, + int const stdinFd, + int const stdoutFd) { /*---------------------------------------------------------------------------- - Run the shell command 'shellCommand', supplying to the shell - 'inputPipeFd' as its Standard Input and 'outputPipeFd' as its + Run the program 'progName' with arguments argArray[], in a child process + with 'stdinFd' as its Standard Input and 'stdoutFd' as its Standard Output. But leave Standard Input and Standard Output as we found them. + + Note that stdinFd or stdoutFd may actually be Standard Input and + Standard Output already. -----------------------------------------------------------------------------*/ int stdinSaveFd, stdoutSaveFd; int rc; - /* Make inputPipeFd Standard Input. - Make outputPipeFd Standard Output. + /* Make stdinFd Standard Input. + Make stdoutFd Standard Output. */ stdinSaveFd = dup(STDIN); stdoutSaveFd = dup(STDOUT); @@ -52,10 +59,10 @@ execProgram(const char * const shellCommand, close(STDIN); close(STDOUT); - dup2(inputPipeFd, STDIN); - dup2(outputPipeFd, STDOUT); + dup2(stdinFd, STDIN); + dup2(stdoutFd, STDOUT); - rc = execl("/bin/sh", "sh", "-c", shellCommand, NULL); + rc = execvp(progName, (char **)argArray); close(STDIN); close(STDOUT); @@ -65,10 +72,12 @@ execProgram(const char * const shellCommand, close(stdoutSaveFd); if (rc < 0) - pm_error("Unable to exec the shell. Errno=%d (%s)", - errno, strerror(errno)); + pm_error("Unable to exec '%s' " + "(i.e. the program did not run at all). " + "execvp() errno=%d (%s)", + progName, errno, strerror(errno)); else - pm_error("INTERNAL ERROR. execl() returns, but does not fail."); + pm_error("INTERNAL ERROR. execvp() returns, but does not fail."); } @@ -108,31 +117,47 @@ createPipeFeeder(void pipeFeederRtn(int, void *), static void -spawnProcessor(const char * const shellCommand, - int const stdinFd, - int * const stdoutFdP, - pid_t * const pidP) { +spawnProcessor(const char * const progName, + const char ** const argArray, + int const stdinFd, + int * const stdoutFdP, + pid_t * const pidP) { /*---------------------------------------------------------------------------- - Create a process to run a shell that runs command 'shellCommand'. - Pass file descriptor 'stdinFd' to the shell as Standard Input. - Set up a pipe and pass it to the shell as Standard Output. Return - as *stdoutFdP the file descriptor of the other end of that pipe, - from which Caller can suck the shell's Standard Output. + Create a process to run program 'progName' with arguments + argArray[] (terminated by NULL element). Pass file descriptor + 'stdinFd' to the shell as Standard Input. + + if 'stdoutFdP' is NULL, have that process write its Standard Output to + the current process' Standard Output. + + If 'stdoutFdP' is non-NULL, set up a pipe and pass it to the new + process as Standard Output. Return as *stdoutFdP the file + descriptor of the other end of that pipe, from which Caller can + suck the program's Standard Output. -----------------------------------------------------------------------------*/ + bool const pipeStdout = !stdoutFdP; int stdoutpipe[2]; pid_t rc; - - pipe(stdoutpipe); + + if (pipeStdout) + pipe(stdoutpipe); rc = fork(); if (rc < 0) { pm_error("fork() of processor process failed. errno=%d (%s)\n", errno, strerror(errno)); } else if (rc == 0) { - /* The second child */ - close(stdoutpipe[0]); + /* The program child */ + + int stdoutFd; + + if (pipeStdout) { + close(stdoutpipe[0]); + stdoutFd = stdoutpipe[1]; + } else + stdoutFd = STDOUT; - execProgram(shellCommand, stdinFd, stdoutpipe[1]); + execProgram(progName, argArray, stdinFd, stdoutFd); close(stdinFd); close(stdoutpipe[1]); @@ -140,8 +165,11 @@ spawnProcessor(const char * const shellCommand, } else { /* The parent */ pid_t const processorpid = rc; - close(stdoutpipe[1]); - *stdoutFdP = stdoutpipe[0]; + + if (pipeStdout) { + close(stdoutpipe[1]); + *stdoutFdP = stdoutpipe[0]; + } *pidP = processorpid; } } @@ -191,94 +219,150 @@ cleanupFeederProcess(pid_t const feederPid) { void -pm_system(void stdinFeeder(int, void *), - void * const feederParm, - void stdoutAccepter(int, void *), - void * const accepterParm, - const char * const shellCommand) { +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) { /*---------------------------------------------------------------------------- - 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'. + Run a program in a child process. 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 + But if 'stdinFeeder' is NULL, just feed the program our own Standard Input. And if 'stdoutFeeder' is NULL, just send its Standard Output to our own Standard Output. -----------------------------------------------------------------------------*/ /* If 'stdinFeeder' is non-NULL, we create a child process to run - 'stdinFeeder' and create a pipe between from that process as the - shell's Standard Input. + 'stdinFeeder' and create a pipe from that process as the + program's Standard Input. - If 'stdoutFeeder' is non-NULL, we create a child process to run - the shell and create a pipe between the shell's Standard Output - and the current process, and then the current process runs - 'stdoutAccepter' to read the data from that pipe. + We create another child process to run the program. + + If 'stdoutFeeder' is non-NULL, we create a pipe between the + program process and the current process and have the program + write its Standard Output to that pipe. The current process + runs 'stdoutAccepter' to read the data from that pipe. - But if 'stdoutFeeder' is NULL, we just run the shell in the - current process. + But if 'stdoutFeeder' is NULL, we just tell the program process + to write to the current process' Standard Output. - So there can be 1, 2, or 3 processes involved depending on - parameters. + So there are two processes when stdinFeeder is NULL and three when + stdinFeeder is non-null. */ - int shellStdinFd; + int progStdinFd; pid_t feederPid; + pid_t processorPid; if (stdinFeeder) - createPipeFeeder(stdinFeeder, feederParm, &shellStdinFd, &feederPid); + createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid); else { - shellStdinFd = STDIN; + progStdinFd = STDIN; feederPid = 0; } if (stdoutAccepter) { - int shellStdoutFd; - pid_t processorPid; + int progStdoutFd; - /* Make a child process to run the shell and pipe back to us its + /* Make a child process to run the program and pipe back to us its Standard Output */ - spawnProcessor(shellCommand, shellStdinFd, - &shellStdoutFd, &processorPid); + spawnProcessor(progName, argArray, progStdinFd, + &progStdoutFd, &processorPid); - /* The shell process has cloned our 'shellStdinFd'; we have no + /* The child process has cloned our 'progStdinFd'; we have no more use for our copy. */ - close(shellStdinFd); - /* Dispose of the stdout from that shell */ - (*stdoutAccepter)(shellStdoutFd, accepterParm); - close(shellStdoutFd); - - cleanupProcessorProcess(processorPid); + close(progStdinFd); + /* Dispose of the stdout from that child */ + (*stdoutAccepter)(progStdoutFd, accepterParm); + close(progStdoutFd); } else { - /* Run a child process for the shell that sends its Standard Output + /* Run a child process for the program that sends its Standard Output to our Standard Output */ - int const stdinSaveFd = dup(STDIN); - int rc; + spawnProcessor(progName, argArray, STDIN, NULL, &processorPid); + } - dup2(shellStdinFd, STDIN); - - rc = system(shellCommand); + cleanupProcessorProcess(processorPid); + + if (feederPid) + cleanupFeederProcess(feederPid); +} + + + +void +pm_system_lp(const char * const progName, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + ...) { +/*---------------------------------------------------------------------------- + same as pm_system_vp() except with arguments as variable arguments + instead of an array. +-----------------------------------------------------------------------------*/ + va_list args; + bool endOfArgs; + const char ** argArray; + unsigned int n; + + va_start(args, accepterParm); - close(STDIN); - dup2(stdinSaveFd, STDIN); + endOfArgs = FALSE; + argArray = NULL; + + for (endOfArgs = FALSE, argArray = NULL, n = 0; + !endOfArgs; + ) { + const char * const arg = va_arg(args, const char *); - if (rc < 0) - pm_error("Unable to invoke the shell. Errno=%d (%s)", - errno, strerror(errno)); - else if (rc != 0) - pm_message("WARNING: Shell process completion code = %d", rc); + REALLOCARRAY(argArray, n+1); + + argArray[n++] = arg; + + if (!arg) + endOfArgs = TRUE; } - if (feederPid) - cleanupFeederProcess(feederPid); + va_end(args); + + pm_system_vp(progName, argArray, + stdinFeeder, feederParm, stdoutAccepter, accepterParm); + + free(argArray); } +void +pm_system(void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(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. +-----------------------------------------------------------------------------*/ + + pm_system_lp("/bin/sh", + stdinFeeder, feederParm, stdoutAccepter, accepterParm, + "sh", "-c", shellCommand, NULL); +} + + void pm_feed_from_memory(int const pipeToFeedFd, diff --git a/lib/pm_system.h b/lib/pm_system.h index 0605f888..a7560f48 100644 --- a/lib/pm_system.h +++ b/lib/pm_system.h @@ -9,6 +9,22 @@ extern "C" { #endif +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); + +void +pm_system_lp(const char * const progName, + void stdinFeeder(int, void *), + void * const feederParm, + void stdoutAccepter(int, void *), + void * const accepterParm, + ...); + void pm_system(void stdinFeeder(int, void *), void * const feederParm, @@ -16,7 +32,6 @@ pm_system(void stdinFeeder(int, void *), void * const accepterParm, const char * const shellCommand); - struct bufferDesc { /* This is just a parameter for the routines below */ unsigned int size; -- cgit 1.4.1