/* pcgi-wrapper.c - Persistent CGI wrapper module

Copyright (c) 1998, Digital Creations, Fredericksburg, VA, USA.  All
rights reserved. This software includes contributions from Jeff Bauer.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

  o Redistributions of source code must retain the above copyright
    notice, this list of conditions, and the disclaimer that follows.

  o Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions, and the following disclaimer in
    the documentation and/or other materials provided with the
    distribution.

  o All advertising materials mentioning features or use of this
    software must display the following acknowledgement:

      This product includes software developed by Digital Creations
      and its contributors.

  o Neither the name of Digital Creations nor the names of its
    contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.


THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS *AS IS* AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  - 2.0a5  29 October 1998
    - (brian) fixed arg type mismatch for semctl

  - 2.0a4  10 August 1998
    - (jeff) fixed Win32 local socket host address issues

  - 2.0a4  8 August 1998
    - (jeff) added Win32 support
*/
#include "pcgi.h"

#ifdef CREOSOTE
#include "creosote.h"  /* used for debugging */
char spewbuf[1024];    /* yes, it's a global, but only for debugging */
#endif

static char _id_[]="$Id: pcgi-wrapper.c,v 1.9 2000/08/01 15:59:44 brian Exp $";

/* Globals, OR: "I'll know I'll hate myself in the morning" */
extern char **environ;
int CloseFileDescriptors = 0;

#if UNIX
int g_lock = 0;
#endif

#if PCGI_WRAPPER_MAIN
int main(int argc, char *argv[])
{   pcgiResource resource;
    pcgiResource *r=&resource;
    long l          =0;
    char t[HDRLEN+1]={0};
    char *estatus   =NULL;
    char *emsg      =NULL;
    char *p         =NULL;
    char **env;

#ifdef CREOSOTE
    initializeMrCreosote();
    /* spew("pcgi-wrapper main()"); */
#endif

#ifdef CLOSE_FDS
    CloseFileDescriptors = 1;
#endif

#ifdef WIN32
    _setmode(fileno(stdin), O_BINARY);
    _setmode(fileno(stdout), O_BINARY);
#endif

    /*
    // Initialize resource info
    */
    memset(r,0,sizeof(pcgiResource));
    r->conn=-1;

    if (argc < 2) 
    {
        onError(E_500, "No pcgi info file given!", r);
    }

    strcpy(r->sw_info, argv[1]);
    if ((pcgiParseInfo(r)) < 0)
    {
        onError(E_500, "Error parsing pcgi info file", r);
    }

    /*
    // Copy environment to resource
    */
    for(env=environ; *env != 0; env++)
    {
        r->sz_env+=(strlen(*env)+sizeof(char));
    }
    if((r->p_env=malloc(((sizeof(char) * r->sz_env)+(sizeof(char)*HDRLEN))))==NULL)
    {
        onError(E_500, "Error allocating env", r);
    }
    p=r->p_env;
    sprintf(p, HDRFMT, r->sz_env);

    if (p[0] != '0')
    {
        if (!r->errmsg[0])
            strcpy(r->errmsg, ERR123_BAD_ENV_HEADER);
        onError(E_500, "Error allocating env", r);
    }

    p+=HDRLEN;
    for(env=environ; *env != 0; env++)
    {   l=strlen(*env);
        memcpy(p, *env, l);
        p+=l;
        *p=0;
        p++;
    }

    /*
    // Copy stdin to resource
    */
    p=getenv("CONTENT_LENGTH");
    if((p != NULL) && ((r->sz_input=atol(p)) > 0))
    {  
        if((r->p_input=malloc(((sizeof(char) * r->sz_input)+
                               (sizeof(char)*HDRLEN))))==NULL)
        {
            onError(E_500, "Error allocating stdin", r);
        }
        p=r->p_input;
        sprintf(p, HDRFMT, r->sz_input);

        if (p[0] != '0')
        {
            if (!r->errmsg[0])
                strcpy(r->errmsg, ERR124_BAD_STDIN_HEADER);
            onError(E_500, "Error allocating stdin", r);
        }

        p+=HDRLEN;
        if(pcgiRead(STDIN_FILENO, p, r->sz_input) != r->sz_input)
        {
            onError(E_500, "Error reading stdin", r);
        }
    }
    else
    {   
        if((r->p_input=malloc(sizeof(char)*HDRLEN))==NULL)
        {
            onError(E_500, "Error allocating stdin", r);
        }
        sprintf(r->p_input, HDRFMT, 0);
    }

    /*
    // Attempt to connect
    */
    if ((r->conn = pcgiConnect(r)) < 0)
    {   
        if(pcgiVerifyProc(r) < 0)
        {   
            if(pcgiStartProc(r) < 0) 
            {
                if (!r->errmsg[0])
                    strcpy(r->errmsg, ERR101_FAILURE_DURING_START); 
                onError(E_500, "Failed to start resource", r);
            }
            if ((r->conn=pcgiConnect(r)) < 0)
            {
                if (!r->errmsg[0])
                    strcpy(r->errmsg, ERR102_FAILURE_DURING_CONNECT); 
                onError(E_503, strerror(errno), r);
            }
        }
        else
        {   
            if (!r->errmsg[0])
                strcpy(r->errmsg, ERR103_UNABLE_VERIFY_RUNNING);
            onError(E_503, "pcgiVerifyProc failed", r);
        }
    } 

    /*
    // Send environment and stdin
    */
#ifdef WIN32
    if (pcgiWriteSocket(r->conn,r->p_env,(r->sz_env+HDRLEN)) != (r->sz_env+HDRLEN))
#endif
#ifdef UNIX
    if (pcgiWrite(r->conn,r->p_env,(r->sz_env+HDRLEN)) != (r->sz_env+HDRLEN))
#endif
    {
        if (!r->errmsg[0])
            strcpy(r->errmsg, ERR104_ENVIRONMENT_SEND);
        onError(E_500, "Error sending env", r);
    }

#ifdef WIN32
    if (pcgiWriteSocket(r->conn,r->p_input,(r->sz_input+HDRLEN)) != (r->sz_input+HDRLEN))
#endif
#ifdef UNIX
    if (pcgiWrite(r->conn,r->p_input,(r->sz_input+HDRLEN)) != (r->sz_input+HDRLEN))
#endif

    {
        if (!r->errmsg[0])
            strcpy(r->errmsg, ERR105_STDIN_SEND);
        onError(E_500, "Error sending stdin", r);
    }

#ifdef WIN32
shutdown(r->conn, 1); /* shutdown the socket so we can receive */
#endif

    /*
    // Receive stdout and stderr
    */
    t[0]='\0';
#ifdef WIN32
    if (!pcgiReadSocket(r->conn, t, HDRLEN))
#endif
#ifdef UNIX
    if (!pcgiRead(r->conn, t, HDRLEN))
#endif
    {
        if (!r->errmsg[0])
            strcpy(r->errmsg, ERR106_STDOUT_READ_HEADER);
        onError(E_503, strerror(errno), r);
    }

    if (strlen(t) <= 0)
    {
        if (!r->errmsg[0])
            sprintf(r->errmsg, "%s (%d)", ERR107_BAD_STDOUT_STRLEN, strlen(t));
       onError(E_503, strerror(errno), r);
    }

    if (t[0] != '0')
    {
      /* XXX - Later: process this as out-of-bound stdin data */
        sprintf(r->errmsg, "t[0] = %d", (int) t[0]);
        onError(E_500, "unexpected out-of-bound data in stdin", r);
    }
    else
    {
        r->sz_output=atol(t);
        l=(sizeof(char) * r->sz_output) + sizeof(char);
        r->p_output=(char *)malloc(l);
        memset(r->p_output,0,l);
#ifdef WIN32
        if (pcgiReadSocket(r->conn,r->p_output,r->sz_output) != r->sz_output)
#endif
#ifdef UNIX
        if (pcgiRead(r->conn,r->p_output,r->sz_output) != r->sz_output)
#endif
        {
            if (!r->errmsg[0])
                strcpy(r->errmsg, ERR108_STDOUT_READ_BODY);
            onError(E_500, "Error receiving stdout", r);
        }
    }

    t[0]='\0';
#ifdef WIN32
    if (!pcgiReadSocket(r->conn, t, HDRLEN))
#endif
#ifdef UNIX
    if (!pcgiRead(r->conn, t, HDRLEN))
#endif
    {
        if (!r->errmsg[0])
            strcpy(r->errmsg, ERR109_STDERR_READ_HEADER);
        onError(E_500, "Error receiving stderr size", r);
    }
    if (strlen(t) <= 0)
    {
        if (!r->errmsg[0])
            sprintf(r->errmsg, "%s (%d)", ERR110_BAD_STDERR_STRLEN, strlen(t));
        onError(E_500, "Error receiving stderr size", r);
    }

    if (t[0] != '0')
    {
      /* XXX - Later: process this as out-of-bound stderr data */
        sprintf(r->errmsg, "t[0] = %d", (int) t[0]);
        onError(E_500, "unexpected out-of-bound data in stderr", r);
    }
    else
    {
        r->sz_error=atol(t);
        if (r->sz_error > 0)
        {   
            l=(sizeof(char) * r->sz_error) + sizeof(char);
            r->p_error=(char *)malloc(l);
            memset(r->p_error,0,l);
#ifdef WIN32
            if (pcgiReadSocket(r->conn, r->p_error, r->sz_error) != r->sz_error)
#endif
#ifdef UNIX
            if (pcgiRead(r->conn, r->p_error, r->sz_error) != r->sz_error)
#endif
            {
                if (!r->errmsg[0])
                    strcpy(r->errmsg, ERR111_STDERR_READ_BODY);
                onError(E_500, "Error receiving stderr", r);
            }
        }
    }
#ifdef UNIX
    close(r->conn);
#endif
#ifdef WIN32
    closesocket(r->conn);
#endif

    /*
    // Return stdout and stderr to server
    */
    if (r->sz_output != 0)
    {
        if (pcgiWrite(STDOUT_FILENO,r->p_output,r->sz_output) != r->sz_output)
        {
            if (!r->errmsg[0])
                strcpy(r->errmsg, "(112) Error returning stdout to server"); 
            onError(E_500, ERR112_STDOUT_TO_SERVER, r);
        }
        free(r->p_output);
        r->p_output=NULL;
    }

    if (r->sz_error != 0)
    {   
        if (pcgiWrite(STDERR_FILENO,r->p_error,r->sz_error) != r->sz_error)
        {
            if (!r->errmsg[0])
                strcpy(r->errmsg, ERR113_STDOUT_TO_SERVER);
            onError(E_500, "Error returning stderr", r);
        }
        free(r->p_error);
        r->p_error=NULL;
    }

    /*
    // Free env & input buffers, release lock if needed
    */
    free(r->p_env);
    r->p_env=NULL;
    free(r->p_input);
    r->p_input=NULL;
    cleanup();
    fflush(stdout);
    fflush(stderr);
    /*exit(0);*/
    return 0;
}
#endif /* end of main(): PCGI_WRAPPER_MAIN */

/*
// onError: fatal pcgi error
 */
void onError(char *estatus, char *emsg, pcgiResource *r)
{
    FILE *f;
    time_t now;
#ifdef VERSION
    /* If VERSION isn't defined as a string, everything will blow up here
       during compilation. */
    char *pcgi_version="pcgi-wrapper-version " VERSION;
#else
    char *pcgi_version = "";
#endif
    char *displayError = "";

    if (r != NULL)
    {
#ifdef UNIX
        if(r->conn != -1)    close(r->conn);
        cleanup();
#endif
#ifdef WIN32
        if (r->conn)
        {
            shutdown(r->conn, 1);
            closesocket(r->conn);
            WSACleanup();
        }
#endif
        if (r->errlog[0])
        {   
            if((f=fopen(r->errlog, "ab")) != NULL)
            {
                now = time(NULL); 
                fprintf(f, "%s  pcgi-wrapper: %s  %s\n", 
                        ctime(&now), emsg, r->errmsg);
                fclose(f);
            }
        }

        if(r->p_env!=NULL)   free(r->p_env);
        if(r->p_input!=NULL) free(r->p_input);
    }
    if (r->displayErrors)
      displayError = r->errmsg;
    printf(errorHtml, estatus, displayError, emsg, pcgi_version);
    fflush(stdout);
    fflush(stderr);

    exit(0);
}

/*
// pcgiWrite: write n bytes to a an fd
*/
long pcgiWrite(pcgi_socket fd, const char *ptr, long nbytes)
{   
    long done    = 0;
    long notdone = nbytes;
    const char *cptr = (const char *)ptr;

    while (notdone > 0)
    {   
        done = write(fd, cptr, notdone);
        if (done <= 0) 
        {
            return (done);
        }
        notdone -= done;
        cptr += done;
    }
    return ((nbytes - notdone));
}

#ifdef WIN32
long pcgiWriteSocket(pcgi_socket fd, const char *ptr, long nbytes)
{
    /* The successful completion of a send does not indicate */
    /* that the data was successfully delivered.  */
    int count;
    int req;
    long bytesSent = 0;

/* begin superKludge(tm) - only for testing, do not leave in place XXX */
/*     if (STDOUT_FILENO == (int)fd) */
/*     { */
/*         printf("%s", ptr); */
/*         return 0; */
/*     } */
/* end superKludge(tm) - only for testing, do not leave in place XXX */

    while ( bytesSent < nbytes )
    {
        req = nbytes - bytesSent;
        count = send(fd, ptr + bytesSent, req, 0);
        if (SOCKET_ERROR == count)
        {
            return(-1);
        }
        bytesSent += (long) count;
    }

    return(bytesSent);
}
#endif

/*
// pcgiRead: read n bytes from an fd
*/
long pcgiRead(pcgi_socket fd, char *ptr, long n)
{   
    long done    = 0;
    long notdone = n;
    char *cptr   = ptr;

    while (notdone > 0)
    {   
        done = read(fd, cptr, notdone);
        if (done < 0)       
            return (done);   /* ERROR */
        else if (done == 0) 
            break;           /*  EOF  */
        notdone -= done;
        cptr += done;
    }
    return ((n - notdone));
}

#ifdef WIN32
long pcgiReadSocket(pcgi_socket fd, char *ptr, long nbytes)
{
    int count;
    int req;
    long bytesRecv = 0;

    while ( bytesRecv < nbytes )
    {
        req = nbytes - bytesRecv;
        count = recv(fd, ptr + bytesRecv, req, 0);
        if (SOCKET_ERROR == count)
        {
            /* TODO: add error reporting by calling WSAGetLastError */
            return(-1);
        }
        bytesRecv += (long) count;
    }
    return(bytesRecv);
}
#endif

/*
// pcgiVerifyProc: check to see if a resource is running
*/
int pcgiVerifyProc(pcgiResource *r)
{   FILE *f;
    char p[10];

    memset(p,0,10);
    if (r->procid == 0)
    {   if((f=fopen(r->procpath, "r")) == NULL)
            return(-1);
        if(fgets(p, HDRLEN, f) == NULL)
            return(-1);
        fclose(f);
        if(!(strlen(p) > 0))
            return(-1);
        r->procid=atoi(p);
    }
#ifdef UNIX
   return(kill(r->procid, 13));
#endif
#ifdef WIN32
    if ((NULL == OpenProcess(PROCESS_VM_READ, FALSE, r->procid)))
    {
        return(-1);
    }
    return(0);
#endif
}

/*
// pcgiConnect: return fd of a connected socket
*/
#ifdef UNIX
pcgi_socket pcgiConnect(pcgiResource *r)
{   
    struct sockaddr_un s;
    pcgi_socket fd;
    int addrlen=0;
    int connected=-1;
    int attempted=0;
    int retry=10;
    int delay=1;

    memset((char *) &s, 0, sizeof(s));
    s.sun_family=AF_UNIX;
    strcpy(s.sun_path, r->sockpath);

    addrlen = sizeof(struct sockaddr_un);  /* force to use sizeof(s) */

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
    {
        if (!r->errmsg[0])
            strcpy(r->errmsg, ERR114_UNABLE_TO_OPEN_SOCKET);
        return (-1);
    }
    while ((connected < 0) && (attempted <= retry))
    {   
        if ((connected=connect(fd, (struct sockaddr *) &s, addrlen)) < 0)
        {   
            if((errno!=ECONNREFUSED) && (errno!=ENOENT))
            {   
                if (!r->errmsg[0])
                    strcpy(r->errmsg, ERR115_CONNECTION_REFUSED);
                return(-1);
            }
            sleep(delay);
            attempted++;
        }
    }
    if (!(connected < 0)) 
    {
        if (!r->errmsg[0])
            sprintf(r->errmsg, "%s, fd=%d", ERR116_UNABLE_TO_CONNECT, fd);
        return (fd);
    }
    return (connected);
}
#endif  /* UNIX pcgiConnect */


#ifdef WIN32
pcgi_socket pcgiConnect(pcgiResource *r)
{
    pcgi_socket destSocket;
    SOCKADDR_IN destSockAddr;
    unsigned long destAddr;
    int connected = SOCKET_ERROR;
    int attempted = 0;
    int status;
    WSADATA WsaData;

    char hostName[MAXSZ];
    LPHOSTENT hostEnt;
    SOCKADDR_IN hostAddr;

    /* initialize the Windows Socket DLL */
    /* TODO: move this startup code outside of pcgiConnect */
    if (0 != WSAStartup(MAKEWORD(2, 0), &WsaData))
    {
        return(-1);
    }

    /* destAddr = inet_addr("192.168.2.43"); */
    hostAddr.sin_addr.s_addr = INADDR_ANY;  /* init local address */

    /*  Bug: assumes r->sockhost is the host name, we should also
        check to see if it is the xxx.xxx.xxx.xxx octet string and
        act accordingly */

    if (r->sockhost[0])
        strcpy(hostName, r->sockhost);
    else if (SOCKET_ERROR == gethostname(hostName, MAXSZ))
        return(-1);
    hostEnt = gethostbyname((LPSTR) hostName);
    if (hostEnt)
    {
        /* resolve hostname for local address */
        hostAddr.sin_addr.s_addr = *((u_long FAR*) (hostEnt->h_addr));
    }
    if (INADDR_ANY == hostAddr.sin_addr.s_addr)
    {
        return(-1);
    }
    destAddr = hostAddr.sin_addr.s_addr;

    memcpy(&destSockAddr.sin_addr, &destAddr, sizeof(destAddr));
    destSockAddr.sin_port = htons((short)r->sockport);
    destSockAddr.sin_family = AF_INET;

    /* create socket */
    destSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == destSocket)
    {
        return(-1);
    }

    /* Connect */
    /*     The code below blithely duplicates the Unix version. */
    /*     TODO:  add better recovery/error handling. */

    while ((SOCKET_ERROR == connected) && (attempted <= CONNRETRY))
    {
        connected = connect(destSocket, (LPSOCKADDR) &destSockAddr, sizeof(destSockAddr));

        if (SOCKET_ERROR == connected)
        {
            status = WSAGetLastError();
            if ((status != WSAECONNREFUSED) && (status != ENOENT))
            {
                return(-1);
            }
            sleep(CONNDELAY);
            attempted++;
        }
    }
    if (SOCKET_ERROR == connected)
    {
        closesocket(destSocket);
        WSACleanup();
        return(-1);
    }

    return(destSocket);
}
#endif  /* Win32 pcgiConnect */


#ifdef UNIX
static sig_atomic_t sigflag;
static sigset_t nmask, omask, zmask;
#endif

#ifdef UNIX
void pcgiSIG(int s)
{   
    sigflag=1; 
    return;
}
#endif

void cleanup()
{   
#ifdef UNIX
    UNION_SEMUN arg;
    arg.val=0;
    if (g_lock > 0) 
    {
        semctl(g_lock, 1, IPC_RMID, arg);
    }
#endif
}

/*
// pcgiStartProc: manages starting a pcgi resource.
*/
#ifdef UNIX
int pcgiStartProc(pcgiResource *r)
{   
    pid_t pid;
    char  *p = NULL;
    int   i = 0;
    UNION_SEMUN arg;
    arg.val=0;

    if ((p=strrchr(r->sw_exe, PATHSEP))==NULL)
    {   
        p = r->sw_exe;
    }
    else
    {   
        p++;
    }

    /* Set up signal handlers to coordinate timing */
    signal(SIGUSR1, pcgiSIG);
    signal(SIGUSR2, pcgiSIG);
    sigemptyset(&zmask);
    sigemptyset(&nmask);
    sigaddset(&nmask, SIGUSR1);
    sigaddset(&nmask, SIGUSR2);
    sigprocmask(SIG_BLOCK, &nmask, &omask);

    /* Make sure another wrapper isn't already doing a restart */
    if ((r->lock=semget(101, 1, 0700 | IPC_CREAT | IPC_EXCL)) == -1)
    {
        if (errno == EACCES)
            strcpy(r->errmsg, ERR117_LOCK_ERROR_EACCES);
        else if (errno == EEXIST)
            strcpy(r->errmsg, ERR118_LOCK_ERROR_EEXIST);
        else if (errno == EINVAL)
            strcpy(r->errmsg, ERR119_LOCK_ERROR_EINVAL);
        else if (errno == ENOENT)
            strcpy(r->errmsg, ERR120_LOCK_ERROR_ENOENT);
        else if (errno == ENOSPC)
            strcpy(r->errmsg, ERR121_LOCK_ERROR_ENOSPC);
        else
            sprintf(r->errmsg, "%s, %d", ERR122_LOCK_ERROR_OTHER, errno);
        return(-1);
    }

    /* Keep us from dying without cleaning up our semaphore by
       setting signal handlers to ensure that the sem will always
       be released. We don't set handlers for SIGUSR1 SIGUSR2 or
       SIGCHLD, because we need those to coordinate with the child
       process(es) that we fork. */
    g_lock = r->lock;

    for(i=0; i<32; i++)
    {   
        if ((i!=SIGUSR1) && (i!=SIGUSR2) && (i != SIGCHLD))
        {
            signal(i, (void *)cleanup);
        }
    }

    /* If the old socket file exists and we have write permission,
       attempt to remove it. */
    if (r->sockport == 0 && access(r->sockpath, W_OK) == 0)
    {
        unlink(r->sockpath);
    }

    /* Fork a child which forks a child -- this is so that
       init will inherit the grandchild (so it won't die) */
    if ((pid = fork()) < 0)
    {   
        semctl(r->lock, 1, IPC_RMID, arg);
        return(-1);
    }
    else if (pid == 0)
    {   
        if ((pid = fork()) < 0) 
        {
            return(-1);
        }
        else if (pid > 0)
        {   /* Let child know we're going away so it can start */
            kill(pid, SIGUSR1);
            exit(0);
        }

        setsid();
        chdir("/");

        /* Wait for parent to go away */
        while(sigflag == 0) 
            sigsuspend(&zmask);
        sigflag = 0;
        sigprocmask(SIG_SETMASK, &omask, NULL);

        /* Platform oddities... */
        if (CloseFileDescriptors)
        {
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
        }

        execl(r->sw_exe,
              p,
              r->pubpath,
              (char *)0);
        exit(0);           
    }

    /* Wait for the first child to finish */
    if (waitpid(pid, NULL, 0) < 0)
    {   semctl(r->lock, 1, IPC_RMID, arg);
        return(-1);
    }

    /*
    // Release restart lock!
    */
    semctl(r->lock, 1, IPC_RMID, arg);

    /*
    // Reset signal handlers
    */
    for(i=0; i<10; i++) 
    {
        signal(i, SIG_DFL);
    }

    sleep(2);
    return (0);
}
#endif  /* UNIX pcgiStartProc */

#ifdef WIN32
int pcgiStartProc(pcgiResource *r)
{
    HANDLE mutex = 0;
    char pythonExecutable[256];
    char command_line[1024];
    BOOL createProcessStatus;
    STARTUPINFO StartupInfo;
    PROCESS_INFORMATION ProcessInfo;
    int waitStatus;

    GetStartupInfo(&StartupInfo);

    /* Is another wrapper attempting a restart? */
    mutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
    if (NULL == mutex)
    {
        //TODO: add error reporting
        return(-1);
    }

    waitStatus = WaitForSingleObject(mutex, (AUTODELAY * 1000));
    if (WAIT_TIMEOUT == waitStatus)
    {
        //TODO: add error reporting
        return(-1);
    }
    else if (WAIT_FAILED == waitStatus)
    {
        //TODO: add error handling by calling GetLastError()
        return(-1);
    }

    /*
    Travel beyond this point requires calling the
    ReleaseMutex function.  Fortunately, the Win32
    mutex is automatically released when a process
    terminates, but we could still encounter a
    situation where the process is hung or stalled ...
    */

  if (r->sw_exe[0])
      strcpy(pythonExecutable, r->sw_exe);
  else
    strcpy(pythonExecutable, "python");

    sprintf(command_line, "%s %s", pythonExecutable, r->pubpath);

    createProcessStatus = CreateProcess(
        NULL,
        command_line,
        NULL,
        NULL,
        FALSE,
        DETACHED_PROCESS,
        NULL,
        NULL,
        &StartupInfo,
        &ProcessInfo);

     /* wait a little bit to give the published
     application enough time to initialize itself */

//Yech!!!
    sleep(AUTODELAY); // <<<-- use a ReadySignal(tm), if available :)

    ReleaseMutex(mutex);
    CloseHandle(mutex);

    if (0 == createProcessStatus)
    {
        //TODO: report more information about failure
        return(-1);
    }
    else
    {
        r->procid = ProcessInfo.dwProcessId; /* assign process id */
        return(0);
    }
}
#endif
