static char rcsid[] = "system.c,v 1.49 1996/01/05 20:28:35 duane Exp";
/*
 *  system.c - system(3) routines for Essence system.
 *
 *  DEBUG: section  82, level 1         Common utilities system(3) routines
 *
 *  Darren Hardy, hardy@cs.colorado.edu, February 1994
 *
 *  ----------------------------------------------------------------------
 *  Copyright (c) 1994, 1995.  All rights reserved.
 *  
 *    The Harvest software was developed by the Internet Research Task
 *    Force Research Group on Resource Discovery (IRTF-RD):
 *  
 *          Mic Bowman of Transarc Corporation.
 *          Peter Danzig of the University of Southern California.
 *          Darren R. Hardy of the University of Colorado at Boulder.
 *          Udi Manber of the University of Arizona.
 *          Michael F. Schwartz of the University of Colorado at Boulder.
 *          Duane Wessels of the University of Colorado at Boulder.
 *  
 *    This copyright notice applies to software in the Harvest
 *    ``src/'' directory only.  Users should consult the individual
 *    copyright notices in the ``components/'' subdirectories for
 *    copyright information about other software bundled with the
 *    Harvest source code distribution.
 *  
 *  TERMS OF USE
 *    
 *    The Harvest software may be used and re-distributed without
 *    charge, provided that the software origin and research team are
 *    cited in any use of the system.  Most commonly this is
 *    accomplished by including a link to the Harvest Home Page
 *    (http://harvest.cs.colorado.edu/) from the query page of any
 *    Broker you deploy, as well as in the query result pages.  These
 *    links are generated automatically by the standard Broker
 *    software distribution.
 *    
 *    The Harvest software is provided ``as is'', without express or
 *    implied warranty, and with no support nor obligation to assist
 *    in its use, correction, modification or enhancement.  We assume
 *    no liability with respect to the infringement of copyrights,
 *    trade secrets, or any patents, and are not responsible for
 *    consequential damages.  Proper use of the Harvest software is
 *    entirely the responsibility of the user.
 *  
 *  DERIVATIVE WORKS
 *  
 *    Users may make derivative works from the Harvest software, subject 
 *    to the following constraints:
 *  
 *      - You must include the above copyright notice and these 
 *        accompanying paragraphs in all forms of derivative works, 
 *        and any documentation and other materials related to such 
 *        distribution and use acknowledge that the software was 
 *        developed at the above institutions.
 *  
 *      - You must notify IRTF-RD regarding your distribution of 
 *        the derivative work.
 *  
 *      - You must clearly notify users that your are distributing 
 *        a modified version and not the original Harvest software.
 *  
 *      - Any derivative product is also subject to these copyright 
 *        and use restrictions.
 *  
 *    Note that the Harvest software is NOT in the public domain.  We
 *    retain copyright, as specified above.
 *  
 *  HISTORY OF FREE SOFTWARE STATUS
 *  
 *    Originally we required sites to license the software in cases
 *    where they were going to build commercial products/services
 *    around Harvest.  In June 1995 we changed this policy.  We now
 *    allow people to use the core Harvest software (the code found in
 *    the Harvest ``src/'' directory) for free.  We made this change
 *    in the interest of encouraging the widest possible deployment of
 *    the technology.  The Harvest software is really a reference
 *    implementation of a set of protocols and formats, some of which
 *    we intend to standardize.  We encourage commercial
 *    re-implementations of code complying to this set of standards.  
 *  
 */
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
#include "util.h"
#ifdef HAVE_SETRLIMIT
#include <sys/time.h>
#include <sys/resource.h>
#endif


static void redirect_stdout();
static void redirect_stdin();

/*
 *  do_system() - calls system(3).
 */
int do_system(cmd)
     char *cmd;
{
    Debug(82, 1, ("RUNNING as shell: %s\n", cmd));
    return (system(cmd));
}

/*
 *  run_cmd() - simplified system(3).  Parses the command, will redirect
 *  stdout and then fork/exec() to save a sh process.
 */
int run_cmd(cmd)
     char *cmd;
{
    int pid, status = 0;

    Debug(82, 1, ("run_cmd: RUNNING: %s\n", cmd));

/*
 * PURFIY:
 * use fork() here instead of vfork().  With vfork parent and child
 * share memory space.  In the child we strdup a bunch of argv's
 * which would otherwise never get free'd causing a memory leak in
 * the parent.
 */
    if ((pid = fork()) < 0) {
	log_errno("run_cmd: fork");
	return (1);
    }
    if (pid == 0) {		/* child */
	char *argv[64], buf[BUFSIZ];
	int i;

	memset(argv, '\0', sizeof(char *) * 64);
	parse_argv(argv, cmd);
	for (i = 0; argv[i] != NULL; i++) {
	    if (argv[i][0] == '>' && argv[i + 1] != NULL) {
		argv[i] = NULL;
		redirect_stdout(argv[++i]);
	    }
	    if (argv[i][0] == '<' && argv[i + 1] != NULL) {
		argv[i] = NULL;
		redirect_stdin(argv[++i]);
	    }
	}
	execvp(argv[0], argv);
	sprintf(buf, "execvp: %s", argv[0]);
	log_errno(buf);
	_exit(1);
    }
    /* parent */
    (void) waitpid(pid, &status, (int) NULL);
    if (WIFSIGNALED(status))
	return -1;
    return (WEXITSTATUS(status));
}

static void redirect_stdout(filename)
     char *filename;
{
    int fd;

    if (filename == NULL)
	return;
    if ((fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
	log_errno(filename);
	return;
    }
    close(1);
    dup2(fd, 1);		/* make stdout */
}

static void redirect_stdin(filename)
     char *filename;
{
    int fd;

    if (filename == NULL)
	return;
    if ((fd = open(filename, O_RDONLY)) < 0) {
	log_errno(filename);
	return;
    }
    close(0);
    dup2(fd, 0);		/* make stdin */
}

static int dsl_pid = -1;
static int dsl_timeout = 0;
/* 
 *  dsl_alarm_handler - We want to kill the dsl_pid process here.
 *  The parent is blocked on the wait4 call until the child process
 *  is killed.  So, on the first pass through we send the child a 
 *  SIGTERM to kill it, then reset the alarm for 5 seconds.  After
 *  5 seconds if the child has not died then we send it the uncatchable
 *  SIGKILL to kill it for good.  After the parent exits from wait4()
 *  it will disable the alarm.
 */
static void dsl_alarm_handler()
{
    if (dsl_timeout == 1) {
	(void) kill(dsl_pid, SIGKILL);
    } else {
	dsl_timeout = 1;
	(void) kill(dsl_pid, SIGTERM);
	alarm(5);
    }
}

/*
 *  do_system_lifetime() - calls system(3).  Only lives for lifetime seconds.
 *  return 0 on error (if 'cmd' exceeds lifetime and must be killed).
 *  return 1 on success
 */
int do_system_lifetime(cmd, lifetime)
     char *cmd;
     int lifetime;
{
    int rc = 1;
    char *argv[64];
    static char buf[BUFSIZ];
    int i;

    Debug(82, 1, ("do_system_lifetime: RUNNING: %s\n", cmd));
    Debug(82, 1, ("do_system_lifetime: Lifetime is %d seconds.\n", lifetime));

    /*
     *  PURFIY:
     *  use fork() here instead of vfork().  With vfork parent and child
     *  share memory space.  In the child we strdup a bunch of argv's
     *  which would otherwise never get free'd causing a memory leak in
     *  the parent.
     */
    if ((dsl_pid = fork()) < 0) {
	log_errno("fork");
	return (0);
    }
    if (dsl_pid) {		/* parent */
	/* Set the alarm to lifetime seconds */
	alarm(0);		/* reset timer */
	dsl_timeout = 0;
	signal(SIGALRM, dsl_alarm_handler);
	alarm(lifetime);	/* start timer */

	/* SIGALRM will NOT interrupt waitpid - kill child in handler */
	(void) waitpid(dsl_pid, (int *) NULL, (int) NULL);

	/* reset the alarm */
	alarm(0);
	signal(SIGALRM, SIG_DFL);

	/* if it timed out, then log what happened */
	if (dsl_timeout) {
	    Log("WARNING: Child process (pid %d) exceeded lifetime of %d seconds.  Killed.\n", dsl_pid, lifetime);
	    rc = 0;
	}
	return rc;
    }
    /* child process */
    memset(argv, '\0', sizeof(char *) * 64);
    parse_argv(argv, cmd);
    for (i = 0; argv[i] != NULL; i++) {
	if (argv[i][0] == '>' && argv[i + 1] != NULL) {
	    argv[i] = NULL;
	    redirect_stdout(argv[++i]);
	} else if (argv[i][0] == '<' && argv[i + 1] != NULL) {
	    argv[i] = NULL;
	    redirect_stdin(argv[++i]);
	}
    }
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CPU)
    {
	struct rlimit rlp;
	/* 
	 *  CPU time isn't the same as real time; oh well 
	 *  But we can use this as a hard limit anyway.
	 */
	rlp.rlim_cur = rlp.rlim_max = lifetime;
	(void) setrlimit(RLIMIT_CPU, &rlp);
    }
#endif
    execvp(argv[0], argv);
    sprintf(buf, "execvp: %s", argv[0]);
    log_errno(buf);
    _exit(1);
    /* NOTREACHED */
}

/*
 *  close_all_fds_except() - closes all of the file descriptors starting 
 *  with start, except the fds in e[] which is terminated by a seminal of -1.
 */
void close_all_fds_except(start, e)
     int start, *e;
{
    int i, j, skip;

    Debug(82, 1, ("Closing all file descs starting with %d,except\n", start));

#if   defined(HAVE_GETDTABLESIZE)
    for (i = start; i < getdtablesize(); i++) {
#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
	for (i = start; i < sysconf(_SC_OPEN_MAX); i++) {
#elif defined(OPEN_MAX)
	    for (i = start; i < OPEN_MAX; i++) {
#else
    for (i = start; i < 64; i++) {
#endif
	skip = 0;
	for (j = 0; e[j] != -1; j++) {
	    if (i == e[j]) {
		skip = 1;
		break;
	    }
	}
	if (skip == 0) {
	    (void) close(i);
	}
    }
}

void close_all_fds(start)
     int start;
{
    int e[1];

    e[0] = -1;
    close_all_fds_except(start, e);
}

/* 
 *  setsocket_linger - sets the LINGER time on the given socket.
 */
void setsocket_linger(s, t)
     int s, t;
{
    struct linger l;

    l.l_onoff = 1;
    l.l_linger = t;
    if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &l,
	    sizeof(l)) < 0)
	log_errno("setsockopt (SO_LINGER)");
}
