
/*
 * See the files COPYING and README for copyright information and how to reach 
 * the author.
 *
 *  $Id: pipe.c,v 1.5 2004/12/05 18:50:34 lordjaxom Exp $
 */

#include "pipe.h"
#include <sys/select.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

void cBurnPipe::DefaultProc(void *Data)
{
    const char *cmd = (const char *)Data;
    execl("/bin/sh", "sh", "-c", cmd, NULL);
    _exit(127);
    //execl("/bin/sh", "sh", "-c", "printenv", NULL);
}

cBurnPipe::cBurnPipe(void)
{
    mUsed = 0;
    mInput = -1;
    mPid = -1;
    mDir = NULL;
}

cBurnPipe::~cBurnPipe()
{
    free(mDir);
}

bool cBurnPipe::Launch(const char *Command)
{
    Dprintf("starting: sh -c '%s'\n", Command);
    return Launch(DefaultProc, (void *)Command);
}

bool cBurnPipe::Launch(BurnProc Procedure, void *Data)
{
    int piped[2];

    if (pipe(piped) == -1) {
        esyslog("BURN: Couldn't start subprocesses: %m");
        return false;
    }

    if ((mPid = fork()) == -1) {
        esyslog("BURN: Couldn't start subprocesses: %m");
        close(piped[0]);
        close(piped[1]);
        return false;
    }

    if (mPid == 0) {
        // child process
        if (mDir != NULL) {
            chdir(mDir);
        }
        close(STDOUT_FILENO);
        dup(piped[1]);
        close(STDERR_FILENO);
        dup(piped[1]);
        close(STDIN_FILENO);
        close(piped[0]);

        // code stolen from VDRs therad.c
        // TODO: use vdr's cPipe
        int MaxPossibleFileDescriptors = getdtablesize();
        for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++) {
            close(i);           //close all dup'ed filedescriptors
        }

        Procedure(Data);
        esyslog("BURN: Couldn't start subprocess: %m");
        _exit(127);
    }

    close(piped[1]);
    mInput = piped[0];
    return true;
}

bool cBurnPipe::GetLineFromBuffer(char *&Line)
{
    char *ptr;
    if ((ptr = (char *)memchr(mBuffer, '\n', mUsed)) != NULL ||
            (ptr = (char *)memchr(mBuffer, '\r', mUsed)) != NULL)
    {
        *ptr = '\0';
        Line = strdup(mBuffer);
        mUsed -= strlen(mBuffer) + 1;
        memmove(mBuffer, ptr + 1, mUsed);
        return true;
    }
    return false;
}

bool cBurnPipe::GetLine(char *&Line)
{
    fd_set rfds;

    if (GetLineFromBuffer(Line)) {
        return true;
    }

    FD_ZERO(&rfds);
    FD_SET(mInput, &rfds);

    if (select(mInput + 1, &rfds, NULL, NULL, NULL) == -1) {
        if (errno != EINTR) {
            esyslog("BURN: Couldn't watch subprocesses: %m");
        }
        return false;
    }

    if (FD_ISSET(mInput, &rfds)) {
        int res;
        if ((res = read(mInput, mBuffer + mUsed,
                sizeof(mBuffer) - mUsed)) == -1)
        {
            esyslog("BURN: Couldn't watch subprocess: %m");
            return false;
        }
        mUsed += res;

        if (!GetLineFromBuffer(Line)) {
            Line = strdup("");
        }

        if (res == 0) {
            Line = NULL;
        }
    }
    return true;
}

bool cBurnPipe::Exit(bool Wait)
{
    int res, status;
    close(mInput);
    if (Wait) {
        waitpid(mPid, &status, 0);
        return WEXITSTATUS(status) == 0;
        //mStatus = WEXITSTATUS(status) == 0 ? rsOk : rsError;
    } else if ((res = waitpid(mPid, &status, WNOHANG)) == 0) {
        // process didn't stop yet, kill it
        kill(mPid, SIGKILL);
        waitpid(mPid, &status, WNOHANG);
        //mStatus = rsError;
        return false;
    } else if (res == -1) {
        esyslog("BURN: Subprocess lost: %m (This shouldn't happen!)");
        //mStatus = rsError;
        return false;
    } else
        //mStatus = WEXITSTATUS(status) == 0 ? rsOk : rsError;
        return WEXITSTATUS(status) == 0;
}
