#ifndef lint
static	char	*sccsid = "@(#)spawn.c	1.6	(Berkeley) 2/6/88";
#endif

#include "../common/conf.h"

#include "common.h"

#include <signal.h>

#ifdef XFER_TIMEOUT
static int	xfer_lines;
static int	old_xfer_lines;
#endif

static char	tempfile[256];

/*
 * spawn -- create a child process with the input from the client
 * as stdin.
 *
 *	Parameters:	"path" is the path of the program to invoke.
 *			"name" is the name to call the program.
 *			"flag" is a single flag to be passed to the program.
 *			"cont_code" is the response code to transmit
 *			on successful startup.
 *			"err_code" is the response code to transmit when
 *			something goes wrong.
 *
 *	Returns:	-1 on non-zero return from child,
 *			0 on error before fork/exec,
 *			1 otherwise.
 *
 *	Side effects:	Creates and removes temporary file;
 *			accepts input from client; forks and execs.
 *			Can time out if XFER_TIMEOUT is defined.
 */

spawn(path, name, flag, cont_code, err_code, errbuf)
	char		*path;
	char		*name;
	char		*flag;
	int		cont_code;
	int		err_code;
	char		*errbuf;
{
	char		line[NNTP_STRLEN];
	register char	*cp;
	int		i, fd;
	int		fds[2];
	int		pid, npid;
	int		exit_status;
#ifdef XFER_TIMEOUT
	int		xfer_timeout();
	int		(*otimeout)();
#endif
#ifdef USG
	int		status;
#else not USG
	union wait	status;
#endif not USG
	register FILE	*fp;

	(void) strcpy(tempfile, "/tmp/rpostXXXXXX");
	(void) mktemp(tempfile);

	fp = fopen(tempfile, "w");
	if (fp == NULL) {
		printf("%d Cannot create temporary file.\r\n", err_code);
		(void) fflush(stdout);
		return (0);
	} else {
		printf("%d Ok\r\n", cont_code);
		(void) fflush(stdout);
	}

#ifdef XFER_TIMEOUT
	xfer_lines = old_xfer_lines = 0;
	otimeout = signal(SIGALRM, xfer_timeout);
	(void) alarm(XFER_TIMEOUT);
#endif

	while (fgets(line, sizeof(line), stdin) != NULL) {
#ifdef XFER_TIMEOUT
		xfer_lines++;
#endif
		if ((cp = index(line, '\r')) != NULL)
			*cp = '\0';
		else if ((cp = index(line, '\n')) != NULL)
			*cp = '\0';

		if (line[0] == '.' && line[1] == '\0')
			break;

		if (line[0] == '.')
			fputs(line+1, fp);
		else
			fputs(line, fp);
		putc('\n', fp);
	}
	(void) fclose(fp);

#ifdef XFER_TIMEOUT
	(void) alarm(0);
	(void) signal(SIGALRM, otimeout);
#endif

	/* See if the connection got closed somehow... */

	if (line[0] != '.' && line[1] != '\0') {
		(void) unlink(tempfile);
#ifdef SYSLOG
# ifdef LOG
		syslog(LOG_ERR, "%s spawn: EOF before period on line by itself",
			hostname);
# else
		syslog(LOG_ERR, "spawn: EOF before period on line by itself");
# endif
#endif
		return (0);
	}
		
#ifdef POSTER
	(void) chown(tempfile, uid_poster, gid_poster);
#endif

	/* Set up a pipe so we can see errors from rnews */

	if (pipe(fds) < 0) {
#ifdef SYSLOG
		syslog(LOG_ERR, "spawn: pipe: %m");
#endif
		(void) unlink(tempfile);
		return (-1);
	}

	/*
	 * Ok, now we have the article in "tempfile".  We
	 * should be able to fork off, close fd's 0 to 31 (or
	 * whatever), open "tempfile" for input, thus making
	 * it stdin, and then execl the inews.  We think.
	 */

	pid = vfork();
	if (pid == 0) {		/* We're in child */
#ifdef POSTER
		(void) setuid(uid_poster);
		(void) setgid(gid_poster);
#endif

		/* Set up stdout and stderr for child */

		if (fds[1] != 1) {
			(void) dup2(fds[1], 1);
			(void) close(fds[1]);
		}
		(void) dup2(1, 2);

		for (i = 3; i < 10; ++i) /* XXX but getdtablesize is too big */
			(void) close(i);

		fd = open(tempfile, O_RDONLY);
		if (fd != 0) {
			(void) dup2(fd, 0);
			(void) close(fd);
		}

		execl(path, name, flag, (char *) NULL);
		fprintf(stderr, "spawn: execl ");
		perror(path);
		_exit(-1);	/* Error */
	} else {
		(void) close(fds[1]);
		fp = fdopen(fds[0], "r");
		if (fp == NULL) {
			printf("%d Cannot fdopen %s pipe\r\n", err_code, path);
			(void) fflush(stdout);
#ifdef SYSLOG
			syslog(LOG_ERR, "spawn: pipe: %m");
#endif
			(void) unlink(tempfile);
			return (0);
		}

		if (errbuf)
			*errbuf = '\0';

		while (fgets(line, sizeof (line), fp) != NULL) {
			if (line[0] != '\n') {
				if (errbuf) {
					if (cp = index(line, '\n'))
						*cp = '\0';
					(void) strcat(errbuf, line);
					(void) strcat(errbuf, "\\");
				}
#ifdef SYSLOG
				syslog(LOG_ERR, "%s: %s", path, line);
#endif
			}
		}

		while ((npid = wait(&status)) > 0)
			if (npid == pid) {
#ifdef USG
				exit_status = (status >> 8) & 0xff;
#else not USG
				exit_status = status.w_T.w_Retcode;
#endif not USG
				break;
			}

		(void) fclose(fp);
		(void) unlink(tempfile);
		(void) fflush(stdout);
		if (npid < 0) {
#ifdef SYSLOG
			syslog(LOG_ERR, "spawn: wait pid %d: %m", pid);
#endif
			return (-1);
		}

#ifdef SYSLOG
		if (exit_status != 0)
			syslog(LOG_ERR, "spawn: %s exit status %d",
				path, exit_status);
#endif
			
		return (exit_status ? -1 : 1);
	}
}

#ifdef XFER_TIMEOUT

xfer_timeout()
{
	if (old_xfer_lines < xfer_lines) {
		old_xfer_lines = xfer_lines;
		(void) alarm(XFER_TIMEOUT);
		return;
	}

	/* Timed out. */

	printf("%d timeout after %d seconds, closing connection.\r\n",
		ERR_FAULT, XFER_TIMEOUT);
	fflush(stdout);

#ifdef LOG
	syslog(LOG_ERR, "%s transfer_timeout", hostname);
#endif LOG

	(void) unlink(tempfile);

	exit(1);
}

#endif XFER_TIMEOUT
