/*-
 * rmt.c --
 *	Functions to handle the exportation of targets using the
 *	LSF Load Sharing Facility.
 *
 * Copyright (c) 1988 by the Regents of the University of California
 * Copyright (c) 1988 by Adam de Boor
 * Copyright (c) 1995 by Andreas Stolcke
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appears in all copies.  Neither the University of California nor
 * Adam de Boor nor Andreas Stolcke makes any representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Interface:
 *	Rmt_Init  	    	Initialize things for this module
 *
 *	Rmt_AddServer	    	Add the given name as the address of
 *	    	  	    	an export server.
 *
 *	Rmt_Begin 	    	Prepare to export another job and tell
 *	    	  	    	if it can actually be exported.
 *
 *	Rmt_Exec  	    	Execute the given shell with argument vector
 *	    	  	    	elsewhere.
 *
 *	Rmt_LastID	    	Return an unique identifier for the last
 *	    	  	    	job exported.
 *
 *	Rmt_Done  	    	Take note that a remote job has finished.
 *
 *	Rmt_Defer		Defer a job for later execution.
 *
 */
#ifndef lint
static char *rcsid =
"$Id: rmt.c,v 1.9 1995/08/19 17:58:30 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <stdio.h>
#include    <errno.h>

extern int errno;

#include    "make.h"
#include    "job.h"

extern int maxJobs;

static char 	  	cwd[MAXPATHLEN];	/* current working directory */
static Boolean		canExport;		/* can use LSF */

static Boolean		exportSame = FALSE;	/* use only same-type hosts */
static Boolean		exportLocal = FALSE;	/* try local host first */
static Boolean		exportExclusive = FALSE;/* exclusive host use 
						 * (NOT SUPPORTED) */

static char *resources = (char *)0;		/* Resource spec for LSF */
static char **hosts;				/* pool of hosts to use */
static Boolean *hostsBusy;			/* usage status */
static int numHosts;				/* how many there are */
static int nextHost;				/* next host to use */

/*-
 *-----------------------------------------------------------------------
 * Rmt_Init --
 *	Initialize this module...
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	LSF is initialized, success is recorded.
 *
 *-----------------------------------------------------------------------
 */
void
Rmt_Init()
{
    int status;

    if (!noExport) {
	/*
	 * Need to be root to create priviledged LSF sockets
	 */
	Main_Access(ACCESS_MAKE);

	status = ls_initrex(maxJobs <= 0 ? 1 : maxJobs, KEEPUID);

	if (DEBUG(RMT)) {
	    Debug ("ls_initrex returned %d\n", status);
	}

	if (status < 0) {
	    Error("Rmt_Init: %s", ls_sysmsg());
	    canExport = FALSE;
	} else {
	    int i;
	    int options = OK_ONLY;
	    numHosts = status;

	    if (!GETWD(cwd)) {
		Fatal("Couldn't get current directory: %s", strerror(errno));
	    }
	    if (NormPath(cwd, sizeof(cwd)) < 0) {
		Fatal("Could not normalize path %s: %s", cwd, strerror(errno));
	    }

	    if (exportSame) {
		options |= DFT_FROMTYPE;
	    }

	    /*
	     * Find a set of candidate hosts.
	     */
	    hosts = ls_placereq(resources, &numHosts, options, (char *)0);
	    if (DEBUG(RMT)) {
		Debug ("ls_placereq(%s) returned %d hosts\n",
			resources ? resources : "", numHosts);
	    }
	    hostsBusy = (Boolean *)emalloc(numHosts * sizeof(Boolean));
	    for (i = 0; i < numHosts; i++) {
		hostsBusy[i] = FALSE;
	    }

	    /*
	     * Set working directory on all hosts
	     */
	    for (i = 0; i < numHosts; i++) {
		if (ls_chdir(hosts[i], cwd) < 0) {
		    Error("%s: %s", hosts[i], cwd, ls_sysmsg());
		    hosts[i] = (char *)0;
		}
	    }
	
	    canExport = TRUE;
	}

	Main_Access(ACCESS_USER);
    }
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_AddServer --
 *	Add a server to the list of those known.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The string is appended to the resource requirements.
 *	(No checking for correct syntax wrt to LSF specs is done.)
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Rmt_AddServer (name)
    char    *name;
{
    if (DEBUG(RMT)) {
	if (name) {
	    Debug ("Rmt_AddServer: adding resource spec %s\n", name);
	} else {
	    Debug ("Rmt_AddServer: resetting resource spec\n");
	}
    }
    if (name == (char *)0) {
	/*
	 * Reset to default
	 */
	resources = (char *)0;
	exportSame = FALSE;
	exportLocal = FALSE;
	exportExclusive = FALSE;
    } else if (strcmp(name, "SAME") == 0) {
	exportSame = TRUE;
    } else if (strcmp(name, "USELOCAL") == 0) {
	exportLocal = TRUE;
    } else if (strcmp(name, "EXCLUSIVE") == 0) {
	exportExclusive = TRUE;	/* no supported */
    } else {
	if (resources) {
	    resources = Str_Concat(resources, name, STR_ADDSPACE|STR_DOFREE);
	} else {
	    resources = Str_New(name);
	}
    }
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_ReExport --
 *	Supposed to re-export a job that's come home.
 *
 * Results:
 *	FALSE if job couldn't be re-exported and TRUE if it could.
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
Boolean
Rmt_ReExport(pid)
    int	    pid;
{
    return(FALSE);
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_Begin --
 *	Prepare to export a job.
 *
 * Results:
 *	TRUE if the job can be exported. FALSE if it cannot.
 *
 * Side Effects:
 *	LSF is asked for host to use, whose name is remembered.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
Boolean
Rmt_Begin (file, argv, gn)
    char    	  *file;
    char    	  **argv;
    GNode   	  *gn;
{
    if (!canExport) {
	return (FALSE);
    } else {
	int i;

	/* 
	 * Check exportation restrictions
	 */
	if ((exportLocal || (gn->type & OP_USELOCAL)) &&
	    !(gn->type & OP_EXPORT) &&
	    (nLocal < maxLocal)) {
	    return FALSE;		/* forces local execution */
	}

	/*
	 * Look for a free host in the pool
	 */
	for (i = 0; i < numHosts; i++) {
	    if (hosts[i] && !hostsBusy[i]) {
		break;
	    }
	}
	if (i == numHosts) {
	    return FALSE;
	} else {
	    nextHost = i;
	    return (TRUE);
	}
    }
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_Exec --
 *	Execute a process elsewhere.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	LSF starts the job.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Rmt_Exec (file, args, traceMe)
    char    *file;
    char    **args;
    Boolean traceMe;
{
    int status;

    if (!beSilent) {
	fprintf(stderr, "*** exporting to %s\n", hosts[nextHost]);
	fflush(stderr);
    }

    args[0] = file;
    status = ls_rexecv(hosts[nextHost], args, REXF_CLNTDIR);

     /*
      * We only get here if the export failed.
      */
    Error ("%s@%s: %s", file, hosts[nextHost], ls_sysmsg());

    /*
     * Run locally as a last resort
     */
    (void)execvp (file, args);
    Error ("%s: %s", file, strerror(errno));
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_LastID --
 *	Return an unique identifier for the last job exported with Rmt_Exec
 *
 * Results:
 *	Some sort of identifier.
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */
int
Rmt_LastID(pid)
    int	    	  pid;	    /* PID of job last exported */
{
    hostsBusy[nextHost] = TRUE;
    return nextHost;
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_Done --
 *	Register the completion of a remote job.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Rmt_Done (id)
    int	    id;
{
    hostsBusy[id] = FALSE;
}

/*-
 *-----------------------------------------------------------------------
 * Rmt_Defer --
 *	Defer a job for remote execution.
 *
 * Results:
 *	FALSE (not supported).
 *
 * Side Effects:
 *	None.
 *	
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
Boolean
Rmt_Defer (job)
    Job		*job;
{
    return (FALSE);
}

