/* See COPYING.txt for the full license governing this code. */
/**
 *  \file linux_process.c
 * 
 *  Source file for the process API on linux.
 */


#include <SDL.h>
#include <SDL_test.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

#include "SDL_visualtest_process.h"
#include "SDL_visualtest_harness_argparser.h"
#include "SDL_visualtest_parsehelper.h"

#if defined(__LINUX__)

static void
LogLastError(char* str)
{
    char* error = (char*)strerror(errno);
    if(!str || !error)
    	return;
    SDLTest_LogError("%s: %s", str, error);
}

int
SDL_LaunchProcess(char* file, char* args, SDL_ProcessInfo* pinfo)
{
    pid_t pid;
    char** argv;

    if(!file)
    {
        SDLTest_LogError("file argument cannot be NULL");
        return 0;
    }
    if(!pinfo)
    {
        SDLTest_LogError("pinfo cannot be NULL");
        return 0;
    }
    pid = fork();
    if(pid == -1)
    {
        LogLastError("fork() failed");
        return 0;
    }
    else if(pid == 0)
    {
        /* parse the arguments string */
        argv = SDLVisualTest_ParseArgsToArgv(args);
        argv[0] = file;
        execv(file, argv);
        LogLastError("execv() failed");
        return 0;
    }
    else
    {
        pinfo->pid = pid;
        return 1;
    }

    /* never executed */
    return 0;
}

int
SDL_GetProcessExitStatus(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps)
{
    int success, status;
    if(!pinfo)
    {
        SDLTest_LogError("pinfo argument cannot be NULL");
        return 0;
    }
    if(!ps)
    {
        SDLTest_LogError("ps argument cannot be NULL");
        return 0;
    }
    success = waitpid(pinfo->pid, &status, WNOHANG);
    if(success == -1)
    {
        LogLastError("waitpid() failed");
        return 0;
    }
    else if(success == 0)
    {
        ps->exit_status = -1;
        ps->exit_success = 1;
    }
    else
    {
        ps->exit_success = WIFEXITED(status);
        ps->exit_status = WEXITSTATUS(status);
    }
    return 1;
}

int
SDL_IsProcessRunning(SDL_ProcessInfo* pinfo)
{
    int success;

    if(!pinfo)
    {
        SDLTest_LogError("pinfo cannot be NULL");
        return -1;
    }

    success = kill(pinfo->pid, 0);
    if(success == -1)
    {
        if(errno == ESRCH) /* process is not running */
            return 0;
        else
        {
            LogLastError("kill() failed");
            return -1;
        }
    }
    return 1;
}

int
SDL_QuitProcess(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps)
{
    int success, status;

    if(!pinfo)
    {
        SDLTest_LogError("pinfo argument cannot be NULL");
        return 0;
    }
    if(!ps)
    {
        SDLTest_LogError("ps argument cannot be NULL");
        return 0;
    }

    success = kill(pinfo->pid, SIGQUIT);
    if(success == -1)
    {
        LogLastError("kill() failed");
        return 0;
    }

    success = waitpid(pinfo->pid, &status, 0);
    if(success == -1)
    {
        LogLastError("waitpid() failed");
        return 0;
    }

    ps->exit_success = WIFEXITED(status);
    ps->exit_status = WEXITSTATUS(status);
    return 1;
}

int
SDL_KillProcess(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps)
{
    int success, status;

    if(!pinfo)
    {
        SDLTest_LogError("pinfo argument cannot be NULL");
        return 0;
    }
    if(!ps)
    {
        SDLTest_LogError("ps argument cannot be NULL");
        return 0;
    }

    success = kill(pinfo->pid, SIGKILL);
    if(success == -1)
    {
        LogLastError("kill() failed");
        return 0;
    }
    success = waitpid(pinfo->pid, &status, 0);
    if(success == -1)
    {
        LogLastError("waitpid() failed");
        return 0;
    }

    ps->exit_success = WIFEXITED(status);
    ps->exit_status = WEXITSTATUS(status);
    return 1;
}

#endif
