/* ============================================================================
 * << File: call_script.c >>
 * -------------------------
 *  Authors: Emiliano Castagnari (aka Torian) <ecastag@fi.uba.ar>
 *           Diego Essaya <dessaya@fi.uba.ar>
 *     Date: 14/01/04
 *
 *  Description:
 *    Functions for calling script(1) as a child process and take its
 *    output using a pipe.
 * ============================================================================
 * ============================================================================
 *
 * ChangeLog: View Changelog
 *
 * ============================================================================
 * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari
 * 
 * This file is part of gems.
 * 
 * gems is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * gems is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * ============================================================================
 */

#include <unistd.h>		/* fork(), pipe() */
#include <signal.h>		/* kill()   */
#include <sys/wait.h>	/* wait()   */
#include <string.h>		/* strcat() */
#include <stdlib.h>		/* exit()   */
#include <errno.h>		/* error values - variable errno   */

#include "defaults.h"
#include "version_server.h"
#include "log.h"

#include "call_script.h"

#ifndef DEV_FD_DIR
#define DEV_FD_DIR "/dev/fd/"
#endif

/* PID of script. If zero, it means that script is not running. */
static pid_t script_pid = 0;

/* start_script()
 *
 * Runs script(1) as a child process and returns in *input_fd the file
 * descriptor from which script's output data can be read.
 */
status_t start_script(char *script_bin, int *input_fd)
{
	/* Steps:
	 *   - Create a pipe
	 *   - fork()
	 *     * Child  -> exec(script -fq /dev/fd/nn)
	 *                 where nn is pipefd[1].
	 *     * Father -> read data from pipefd[0]
	 *
	 * FIXME: This function supposes the existence of the /dev/fd/ directory.
	 * Is this portable?...
	 */

	int pipefd[2];
	char fname[20] = DEV_FD_DIR;
	char num_str[5];
	/* We'll pass 3 arguments to script, so we need a 4 element array of
	 * strings, the last one being NULL */
	char *script_argv[] = {NULL, SCRIPT_OPTIONS, NULL, NULL};

	script_argv[0] = script_bin;

	g_log(LOG_RUNSCRIPT, script_bin);

	/* Create the pipe: */
	if (pipe(pipefd) == -1)
	{
		perror(APPNAME);
		return EXIT_FAIL;
	}

	switch ((script_pid = fork()))
	{
	case -1:
		perror(APPNAME);
		return EXIT_FAIL;
	case 0:	/* CHILD - run script */
		/* Close the side of the pipe that is not used: */
		close(pipefd[0]);
		/* Get the name of the /dev/fd/xx file associated with the pipe */
		sprintf(num_str, "%d", pipefd[1]);
		strcat(fname, num_str);
		script_argv[2] = fname;
		/* Execute script: */
		execv(script_argv[0], script_argv);
		perror(APPNAME);
		exit(-1);
	}

	/* FATHER */

	/* Close the side of the pipe that is not used: */
	close(pipefd[1]);

	*input_fd = pipefd[0];
	return CONTINUE;
}


/* kill_script()
 *
 * If script is running, this function sends the SIGTERM signal to it, and
 * wait()s.
 */
void kill_script()
{
	if (script_pid > 0)
	{
		if (kill(script_pid, SIGTERM) == -1)
		{
			if (errno != ESRCH) perror(APPNAME);
		}
		else if (wait(NULL) == -1)
			perror(APPNAME);
		script_pid = 0;
		g_log(LOG_ENDSCRIPT);
	}
}

/* vim: set ts=4 sw=4: */
