Welcome
Example Code
C and C++
Argument Parsing
Create Subprocess
Create Thread
Is Subprocess Done
Non-Blocking File (Unix)
Mutex (Lock)
TCP Server
Wait For File (Unix)
Python
Non-Blocking Read

Monju System Architects



Code - C++ - Create Subprocess
Download: createSubprocess.cpp (the linked-to file is properly tabbed)
Requires: isSubprocessDone.cpp, parseArgument.h, parseArgument.cpp, subprocess.h
//  Copyright (c) 2007, Monju System Architects
//
//  Released under the Monju-Public copyright license:
//
//  Permission is hereby granted, free of charge, to any person or entity
//  obtaining a copy of this software and associated documentation files
//  (the "Software"), to deal in the Software without restriction,
//  including without limitation the rights to use, copy, modify, merge,
//  publish, distribute, sublicense, and/or sell copies and modifications
//  of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be
//  included in all copies or substantial portions of published, sold, or
//  ownership-transferred versions of the Software. The notices are not
//  required for binary-only releases or binary-only derivatives of the
//  Software, or documentation provided with binary-only releases and
//  binary-only derivatives.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
//  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.




//  NOTES:
//      * The Windows version is untested.
//
//  TODO:
//      * Add optional environment.




//  INCLUDES.
#include "public/subprocess.h"
#include "public/parseArgument.h"

#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
    #include <Windows.h>
#else
    #include <unistd.h>
#endif





//  createSubprocess()
//
//  Create a subprocess with "pipes" to the child stdin, stderr, and stdout.
//  Returns the process ID.
#ifdef _WIN32
PROCESS_INFO createSubprocess(const char*     commandLine, 
                              STANDARD_PIPE*  pStdin, 
                              STANDARD_PIPE*  pStdout, 
                              STANDARD_PIPE*  pStderr) {

    //  SOME TEMPORARY HANDLES.
    HANDLE childStandardInputRead   = NULL;
    HANDLE childStandardInputWrite  = NULL;
    HANDLE childStandardOutputRead  = NULL;
    HANDLE childStandardOutputWrite = NULL;
    HANDLE childStandardErrorRead   = NULL;
    HANDLE childStandardErrorWrite  = NULL;

    //  CREATE THE SECURITY ATTRIBUTES OBJECT.
    SECURITY_ATTRIBUTES securityAttributes;
    memset(&securityAttributes, sizeof(SECURITY_ATTRIBUTES), 0);
    securityAttributes.nLength              = sizeof(SECURITY_ATTRIBUTES);
    securityAttributes.bInheritHandle       = true;
    securityAttributes.lpSecurityDescriptor = NULL;

    //  GET A HANDLE TO THE CURRENT PROCESS.
    HANDLE currentProcessHandle = GetCurrentProcess();

    //  CREATE PIPES.
    if (!CreatePipe(&childStandardInputRead,
                    pStdin,
                    &securityAttributes,
                    0)) {
        throw("CreatePipe(): failed to create child standard input.");
    }
    if (!CreatePipe(pStdout,
                    &childStandardOutputWrite,
                    &securityAttributes,
                    0)) {
        throw("CreatePipe(): failed to create child standard output.");
    }
    if (!CreatePipe(pStderr,
                    &childStandardErrorWrite,
                    &securityAttributes,
                    0)) {
        throw("CreatePipe(): failed to create child standard error.");
    }

    //  MAKE PIPES INHERITABLE.
    if (pStdin != NULL) {
        if (!SetHandleInformation(childStandardInputRead, HANDLE_FLAG_INHERIT, 0)) {
            throw("SetHandleInformation(): failed to set standard input info."); 
        }
        if (!SetHandleInformation(*pStdin, HANDLE_FLAG_INHERIT, 0)) {
            throw("SetHandleInformation(): failed to set standard input info."); 
        }
    }
    if (pStdout != NULL) {
        if (!SetHandleInformation(*pStdout, HANDLE_FLAG_INHERIT, 0)) {
            throw("SetHandleInformation(): failed to set standard output info."); 
        }
        if (!SetHandleInformation(childStandardOutputWrite, HANDLE_FLAG_INHERIT, 0)) {
            throw("SetHandleInformation(): failed to set standard output info."); 
        }
    }
    if (pStderr != NULL) {
        if (!SetHandleInformation(pStderr, HANDLE_FLAG_INHERIT, 0)) {
            throw("SetHandleInformation(): failed to set standard error info."); 
        }
        if (!SetHandleInformation(childStandardErrorWrite, HANDLE_FLAG_INHERIT, 0)) {
            throw("SetHandleInformation(): failed to set standard error info."); 
        }
    }

    //  SET UP ARGUMENTS FOR CreateProcess().
    PROCESS_INFORMATION processInfo;
    STARTUPINFO         startupInfo;
    ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
    startupInfo.cb = sizeof(STARTUPINFO);
    if (pStdin) {
        startupInfo.hStdInput = childStandardInputRead;
    }
    if (pStdout) {
        startupInfo.hStdOutput = childStandardOutputWrite;
    }
    if (pStderr) {
        startupInfo.hStdError = childStandardErrorWrite;
    }

    //  WINDOWS REQUIRES A WRITABLE COMMAND LINE FIELD.
    int         length = strlen(commandLine);
    char        commandLineCopy[length+1];
    memcpy(commandLineCopy, commandLine, (length+1) * sizeof(char));

    //  CREATE THE CHILD PROCESS.
    LPVOID      pEnvironment    = NULL;  //  INHERIT.
    LPCTSTR     pWorkingDir     = NULL;  //  INHERIT.
    if (!CreateProcess(NULL, 
                       commandLineCopy, 
                       NULL, 
                       NULL, 
                       TRUE, 
                       0,
                       pEnvironment, 
                       pWorkingDir, 
                       &startupInfo,
                       &processInfo)) {
        throw("CreateProcess(): failed to create child process.");
    }

}
PROCESS_INFO createSubprocess(char* const*    arguments,
                              STANDARD_PIPE*  pStdin,
                              STANDARD_PIPE*  pStdout,
                              STANDARD_PIPE*  pStderr) {
    int         totalLength = 0;
    int         numberOfArguments;
    int         onArgument;
    const char* pArgument;

    //  FOR WINDOWS, DETERMINE THE TOTAL LENGTH OF THE ARGUMENTS.
    pArgument = arguments[0];
    for(onArgument=0; pArgument != NULL; onArgument++) {
        totalLength += strlen(pArgument);
        pArgument = arguments[onArgument];
    }
    numberOfArguments = onArgument;

    //  ALLOCATE THE BUFFER.  WE MIGHT HAVE A LOT OF ESCAPING, SO ALLOCATE FOR THAT.
    char pCommandLine[(totalLength * 2) + (onArgument * 2) + 1];

    //  FILL THE BUFFER.  ESCAPE ALL ARGUMENTS.
    int onWrite=0;
    for(onArgument=0; onArgument<numberOfArguments; onArgument++) {
        pCommandLine[onWrite++] = '\"';
        pArgument = arguments[onArgument];
        for (int onChar=0; onChar<strlen(pArgument); onChar++) {
            if (pArgument[onChar] == '\0') {
                break;
            }
            if (pArgument[onChar] == '\"') {
                pCommandLine[onWrite++] = '\\';
            }
            else if (pArgument[onChar] == '\\') {
                pCommandLine[onWrite++] = '\\';
            }
            pCommandLine[onWrite++] = pArgument[onChar];
        }
        pCommandLine[onWrite++] = '\"';
        if (onArgument < (numberOfArguments - 1)) {
            pCommandLine[onWrite++] = ' ';
        }
    }

    //  CALL THE OTHER FUNCTION.
    return createSubprocess(pCommandLine, pStdin, pStdout, pStderr);
}



#else  // #ifndef _WIN32
PROCESS_INFO createSubprocess(const char*     commandLine,
                              STANDARD_PIPE*  pStdin,
                              STANDARD_PIPE*  pStdout,
                              STANDARD_PIPE*  pStderr) {
    char* pStrings[1000];
    int   position = 0;
    int   onArgument = 0;
    char  string[2000];
    int   stringLength;
    int   commandLineLength = strlen(commandLine);
    int   childPid;

    //  ALLOCATE AND POPULATE THE ARGUMENT STRINGS.
    try {
        while (position < commandLineLength) {
            parseArgument(commandLine, string, &position, 2000);
            stringLength            = strlen(string) + 1;
            pStrings[onArgument]    = (char*)malloc(stringLength * sizeof(char));
            memcpy(pStrings[onArgument], string, (stringLength * sizeof(char)));
            onArgument++;
            if (onArgument >= 1000) {
                throw("createSubprocess(): buffer overflowed.");
            }
        }

        //  NULL TERMINATE THE LIST.
        pStrings[onArgument] = NULL;

        //  CREATE THE SUBPROCESS.
        childPid = createSubprocess((char* const*)pStrings, pStdin, pStdout, pStderr);
    }

    //  CLEAN UP.
    catch(...) {
        while(onArgument > 0) {
            onArgument --;
            free(pStrings[onArgument]);
        }
        throw;
    }
    while(onArgument > 0) {
        onArgument --;
        free(pStrings[onArgument]);
    }

    //  RETURN THE CHILD PROCESS ID.
    return childPid;
}
PROCESS_INFO createSubprocess(char* const* arguments, int* pStdin, int* pStdout, int* pStderr) {
    int standardInput[2];
    int standardOutput[2];
    int standardError[2];
    int pid;

    //  CREATE STDIN, STDOUT, AND STDERR PIPES.
    if ( pipe(standardInput) < 0 ) {
        throw("createSubprocess(): pipe() failed (1).");
    }
    if ( pipe(standardOutput) < 0 ) {
        close(standardInput[0]);
        close(standardInput[1]);
        throw("createSubprocess(): pipe() failed (2).");
    }
    if ( pipe(standardError) < 0 ) {
        close(standardInput[0]);
        close(standardInput[1]);
        close(standardOutput[0]);
        close(standardOutput[1]);
        throw("createSubprocess(): pipe() failed (3).");
    }

    //  FORK.
    pid = fork();
    if (pid > 0) {   // parent
        close(standardInput[0]);
        close(standardOutput[1]);
        close(standardError[1]);
        *pStdin   = standardInput[1];
        *pStderr  = standardOutput[0];
        *pStdout  = standardOutput[0];
        return pid;
    }
    else if (pid == 0) { // child
        close(standardInput[1]);
        close(standardOutput[0]);
        close(standardOutput[0]);
        dup(standardInput[0]);
        dup(standardOutput[1]);
        dup(standardOutput[1]);
        close(0);
        close(1);
        close(2);
        execvp(arguments[0], arguments);
        exit(1);
    }

    //  HANDLE FORK ERROR.
    close(standardInput[0]);
    close(standardInput[1]);
    close(standardOutput[0]);
    close(standardOutput[1]);
    close(standardOutput[0]);
    close(standardOutput[1]);
    throw("createSubprocess(): fork() failed.");
}
#endif // #indef _WIN32