/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1995 The Ohio State University
 *	GDB
 *
 *	$Log: loadd.c,v $
 *	Revision 6.2  1999/06/12 17:11:38  kmeyer1
 *	added copyright
 *	
 *	Revision 6.1  1996/11/23 19:26:22  nevin
 *	Ohio Release
 *	
 * Revision 6.0  96/02/29  13:33:16  gdburns
 * Ohio Release
 * 
 * Revision 5.2.1.2  96/02/24  14:40:38  gdburns
 * general modernization
 * 
 * Revision 5.2.1.1  94/09/20  14:02:15  gdburns
 * Remove extraneous sys/file.h.
 * 
 * Revision 5.2  94/08/22  13:52:20  gdburns
 * Ohio Release
 * 
 * Revision 5.1  94/05/18  10:43:18  gdburns
 * Ohio Release
 * 
 * Revision 2.3  94/04/22  12:30:06  gdburns
 * Ohio Release
 *
 *	Function	- load daemon
 *			- loads local file onto remote node
 *			- written like a state machine to accomodate
 *			  operation as an internal process
 */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <debug.h>
#include <events.h>
#include <flatreq.h>
#include <ksignal.h>
#include <lreq.h>
#include <net.h>
#include <preq.h>
#include <priority.h>
#include <rreq.h>
#include <terror.h>

/*
 * global functions
 */
void			(*(l_init()))();

/*
 * external functions
 */
char			*_path_env_find();

/*
 * local variables
 */
static struct lreq	*lq;		/* loadd request */
static struct lreply	*lr;		/* loadd reply */
static struct flreq	*flq;		/* flatd request */
static struct flreply	*flr;		/* flatd reply */
static char		*filebuf;	/* file memory buffer */
static char		fname[FNAMELEN];/* name of file to load */
static int		mask;		/* signal mask */
static int4		ldlength;	/* file size */
static struct nmsg	lqhead;		/* loadd request msg */
static struct nmsg	lrhead;		/* loadd reply msg */
static struct nmsg	flqhead;	/* flatd request msg */
static struct nmsg	flrhead;	/* flatd reply msg */

/*
 * local functions
 */
static void		(*(loadd()))();
static void		(*(lflreply()))();
static void		(*(lflsync()))();
static void		(*(lsendr()))();

/*
 *	l_init
 *
 *	Function:	- loadd initialization
 */
void (*(
l_init()))()

{
	lq = (struct lreq *) lqhead.nh_data;
	lr = (struct lreply *) lrhead.nh_data;
	flq = (struct flreq *) flqhead.nh_data;
	flr = (struct flreply *) flrhead.nh_data;
/*
 * Attach to kernel.
 */
	if (lpattach("loadd")) lampanic("loadd (lpattach)");
/*
 * Receive first request.
 */
	lqhead.nh_event = EVLOADD;
	lqhead.nh_type = 0;
	lqhead.nh_flags = 0;
	lqhead.nh_length = FNAMELEN;
	lqhead.nh_msg = fname;

	if (nrecv(&lqhead)) lampanic("loadd (nrecv)");

	return((void (*)()) loadd);
}

/*
 *	loadd
 *
 *	Function:	- services next loadd request
 */
static void (*(
loadd()))()

{
	struct stat	status;		/* store file status */
	int		fdesc;		/* local file descriptor */
	char		*fullname;	/* full path filename */

	db(("loadd: dest_node: %X src_node: %X file: %s\n",
			lq->lq_dest_node, lq->lq_src_node, fname));
/*
 * Locate the file.
 */
	fullname = _path_env_find(fname, R_OK | X_OK);

	if (fullname == 0) {
		lr->lr_reply = errno;
		lsendr();
		return((void (*)()) loadd);
	}
/*
 * Get file information.
 */
	if (stat(fullname, &status)) {
		lr->lr_reply = errno;
		lsendr();
		free(fullname);
		return((void (*)()) loadd);
	}

	if (status.st_size == 0) {
		lr->lr_reply = EINVAL;
		lsendr();
		free(fullname);
		return((void (*)()) loadd);
	}

	ldlength = status.st_size;
/*
 * Open file to be loaded.
 */
	if ((fdesc = open(fullname, O_RDONLY)) < 0) {
		lr->lr_reply = EINVAL;
		lsendr();
		free(fullname);
		return((void (*)()) loadd);
	}

	free(fullname);
/*
 * Allocate space for the file.
 */
	if ((filebuf = malloc((unsigned) ldlength)) == 0)
			lampanic("loadd (malloc)");
/*
 * Read in the file.
 */
	if (read(fdesc, filebuf, ldlength) < 0) lampanic("loadd (read)");
/*
 * Close the file.
 */
	if (close(fdesc)) lampanic("loadd (close)");
/*
 * Load file on destination node.
 */
	flq->flq_src_node = ((lq->lq_dest_node == LOCAL) ||
			(getrtype(lq->lq_dest_node) & NT_CAST)) ?
			lq->lq_dest_node : getnodeid();
	flq->flq_src_event = -kbgetpid();
	flq->flq_req = FLQLOAD;
	flq->flq_ldlength = ldlength;
	flq->flq_malength = ldlength;
	flq->flq_tag = lq->lq_tag;

	flqhead.nh_node = lq->lq_dest_node;
	flqhead.nh_event = EVFLATD;
	flqhead.nh_type = 0;
	flqhead.nh_flags = 0;
	flqhead.nh_length = 0;
	flqhead.nh_msg = 0;

	mask = ksigblock(sigmask(SIGUDIE) | sigmask(SIGARREST));

	if (nsend(&flqhead)) {

		if (errno == EBADNODE) {
			lr->lr_reply = errno;
			lsendr();
			free(filebuf);
			return((void (*)()) loadd);
		} else {
			lampanic("loadd (nsend)");
		}
	}

	flrhead.nh_event = -kbgetpid();
	flrhead.nh_type = 0;
	flrhead.nh_flags = 0;
	flrhead.nh_length = 0;
	flrhead.nh_msg = 0;
	
	if (nrecv(&flrhead)) lampanic("loadd (nrecv)");

	return((void (*)()) lflreply);
}

/*
 *	lflreply
 *
 *	Function:	- responds to reply from flatd
 *			- replies to loadd client if flatd error
 *			- ships file to flatd if ok
 */
static void (*(
lflreply()))()

{
	if (flr->flr_reply) {
		ksigsetmask(mask);
		lr->lr_reply = flr->flr_reply;
		return((void (*)()) lsendr);
	}
/*
 * Load file buffer to destination node.
 */
	flqhead.nh_msg = filebuf;

	while (ldlength > 0) {
		flqhead.nh_length = (ldlength > MAXNMSGLEN) ?
				MAXNMSGLEN : ldlength;

		if (nsend(&flqhead)) lampanic("loadd (nsend)");

		ldlength -= flqhead.nh_length;
		flqhead.nh_msg += flqhead.nh_length;
	}
/*
 * Receive a reply that ensures that all data has been fully loaded.
 */
	flrhead.nh_event = -kbgetpid();
	flrhead.nh_type = 0;
	flrhead.nh_flags = 0;
	flrhead.nh_length = 0;
	flrhead.nh_msg = 0;

	if (nrecv(&flrhead)) lampanic("loadd (nrecv)");

	return((void (*)()) lflsync);
}

/*
 *	lflsync
 *
 *	Function:	- receives "all done" message from flatd
 *			- replies to loadd client
 *	Returns:	- next function to execute
 */
static void (*(
lflsync()))()

{
	ksigsetmask(mask);
	lr->lr_reply = flr->flr_reply;
	free(filebuf);
	lsendr();
	return((void (*)()) loadd);
}

/*
 *	lsendr
 *
 *	Function:	- sends a reply back to the client
 *	Returns:	- next function to execute
 */
static void (*(
lsendr()))()

{
	lrhead.nh_node = lq->lq_src_node;
	lrhead.nh_event = lq->lq_src_event;
	lrhead.nh_type = 0;
	lrhead.nh_flags = 0;
	lrhead.nh_length = 0;
	lrhead.nh_msg = 0;

	if (nsend(&lrhead)) lampanic("loadd (nsend)");
/*
 * Receive next request.
 */
	lqhead.nh_event = EVLOADD;
	lqhead.nh_type = 0;
	lqhead.nh_flags = 0;
	lqhead.nh_length = FNAMELEN;
	lqhead.nh_msg = fname;

	if (nrecv(&lqhead)) lampanic("loadd (nrecv)");

	return((void (*)()) loadd);
}
