/*
 *
 * 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 1996 The Ohio State University
 *	GDB
 *
 *	$Id: flatd.c,v 6.2 1999/06/12 17:11:31 kmeyer1 Exp $
 * 
 *	Function:	- OTB flat memory server
 *			- provides symbolic access to memory
 *			- "flattens" memory in a distributed system in
 *			  order to support broadcasting operations
 *			- memory blocks emulated as temporary files
 */
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <events.h>
#include <flatreq.h>
#include <net.h>
#include <portable.h>
#include <preq.h>
#include <priority.h>
#include <terror.h>

/*
 * global functions
 */
void			(*(fl_init()))();
void			(*(flatd()))();
void			flwipe();
char			*flforget();

/*
 * local functions
 */
static void		flqclean();
static void		flqget();
static void		flqload();

/*
 * external functions
 */
extern void		_lam_atexit();
extern char		*killname();

/*
 * static variables
 */
static int		flcount;	/* total # of created files */

static struct flreq	*request;	/* request from client */
static struct flreply	*reply;		/* reply to client */

static struct nmsg	nhq;		/* request desc. from client */
static struct nmsg	nhr;		/* reply desc. to client */

static char		buf[MAXNMSGLEN];/* input data buffer for request */
static struct flregion	fltable[FLMAX];	/* flat address table */

/*
 *	fl_init
 *
 *	Function:	- flatd initialization
 */
void (*(
fl_init()))()

{
	int		i;			/* index */

	flcount = 0;
	request = (struct flreq *) nhq.nh_data;
	reply = (struct flreply *) nhr.nh_data;

	nhr.nh_flags = NREEL;
	nhr.nh_type = 0;

	_lam_atexit(flwipe);
/*
 * Initialize table.
 */
	for (i = 0; i < FLMAX; ++i) {
		fltable[i].fl_addr = EMPTY;
	}
/*
 * Attach to kernel.
 */
	if (lpattach("flatd")) lampanic("flatd (lpattach)");
/*
 * Receive first request.
 */
	nhq.nh_event = EVFLATD;
	nhq.nh_type = 0;
	nhq.nh_flags = 0;
	nhq.nh_length = MAXNMSGLEN;
	nhq.nh_msg = buf;

	if (nrecv(&nhq)) lampanic("flatd (nrecv)");

	return((void (*)()) flatd);
}

/*
 *	flatd
 *
 *	Function:	- server loop
 *			- replies message & receives next request
 */
void
(*(flatd()))()

{
	nhr.nh_node = request->flq_src_node;
	nhr.nh_event = request->flq_src_event;

	switch(request->flq_req) {

		case FLQLOAD:
		flqload();
		break;

		case FLQGET:
		case FLQFORGET:
		flqget();
		break;

		case FLQCLEAN:
		flqclean();
		break;
	}

	nhq.nh_event = EVFLATD;
	nhq.nh_type = 0;
	nhq.nh_flags = 0;
	nhq.nh_length = MAXNMSGLEN;
	nhq.nh_msg = buf;

	if (nrecv(&nhq)) lampanic("flatd (nrecv)");

	return((void (*)()) flatd);
}

/*
 *	flqload
 *
 *	Function:	- loads a file and mark it with a tag
 *			- creates file in temporary directory
 */
static void
flqload()

{
	int		fd;			/* load file descriptor */
	int		i;			/* favourite index */
	char		*buffer;		/* formatting buffer */
	
	nhr.nh_msg = (char *) 0;
	nhr.nh_length = 0;
/*
 * Search for an existing request.
 */
	for (i = 0; (i < FLMAX); ++i) {

		if ((fltable[i].fl_addr != EMPTY) &&
			(fltable[i].fl_tag == request->flq_tag) &&
			(fltable[i].fl_src_node == request->flq_src_node) &&
			(fltable[i].fl_src_event == request->flq_src_event)) {

			break;
		}
	}
/*
 * If it is a new request, we must create an entry and send an ACK.
 */
	if (i >= FLMAX) {

		for (i = 0; (i < FLMAX) && (fltable[i].fl_addr != EMPTY); ++i);

		if (i >= FLMAX) {
			reply->flr_reply = ENOFLDESCRIPTORS;
			if (nsend(&nhr)) lampanic("flatd (nsend)");
			return;
		}
/*
 * Make sure we can open the file before the user starts pumping data our way.
 */
		fltable[i].fl_tag = request->flq_tag;
		fltable[i].fl_src_node = request->flq_src_node;
		fltable[i].fl_src_event = request->flq_src_event;
		fltable[i].fl_byteswritten = 0;
		fltable[i].fl_status = 0;
/*
 * Create the temporary filename.
 */
		fltable[i].fl_addr = malloc(FLPATHLEN);

		if (fltable[i].fl_addr == 0) {
			fltable[i].fl_addr = EMPTY;
			reply->flr_reply = errno;
			if (nsend(&nhr)) lampanic("flatd (nsend)");
			return;
		}

		if ((buffer = killname()) == 0) {
			free(fltable[i].fl_addr);
			fltable[i].fl_addr = EMPTY;
			reply->flr_reply = errno;
			if (nsend(&nhr)) lampanic("flatd (nsend)");
			return;
		}

		strcpy(fltable[i].fl_addr, buffer);
		sprintf(buffer, "-flatd%d", flcount++);
		strcat(fltable[i].fl_addr, buffer);
		free(buffer);

		fd = open(fltable[i].fl_addr,
			  O_WRONLY | O_CREAT | O_APPEND, 0775);

		if (fd < 0) {
			fltable[i].fl_addr = EMPTY;
			reply->flr_reply = errno;
		} else {
			reply->flr_reply = 0;
			close(fd);
		}

		if (nsend(&nhr)) lampanic("flatd (nsend)");
		return;
	}
/*
 * If we get this far, then it was an existing request which must be continued.
 */
	if (fltable[i].fl_status == 0) {
		fd = open(fltable[i].fl_addr, O_WRONLY | O_APPEND, 0775);

		if (fd < 0) {
			reply->flr_reply = errno;
			fltable[i].fl_status = errno;
		}
/*
 * Write this chunk of data.
 */
		else if (write(fd, buf, nhq.nh_length) < nhq.nh_length) {
			fltable[i].fl_status = errno;
			close(fd);
			unlink(fltable[i].fl_addr);
		}
/*
 * Close even if this isn't the end of the file to conserve descriptors.
 */
		close(fd);
	}
		
	fltable[i].fl_byteswritten += nhq.nh_length;

	if (fltable[i].fl_byteswritten == request->flq_ldlength) {
		reply->flr_reply = fltable[i].fl_status;
		if (nsend(&nhr)) lampanic("flatd (nsend)");
	}
}

/*
 *	flqget
 *
 *	Function:	- finds the address associated with a given tag
 *			- FLQFORGET also cleans out the fltable entry
 *			  associated with the tag
 */
static void
flqget()

{
	int		i;		/* favourite index */

	for (i = 0; (i < FLMAX) && ((fltable[i].fl_tag != request->flq_tag)
		|| (fltable[i].fl_addr == EMPTY)); ++i);

	if (i >= FLMAX) {
		reply->flr_reply = EBADTAG;
	} else {
		reply->flr_reply = 0;
		nhr.nh_msg = fltable[i].fl_addr;
		nhr.nh_length = FLPATHLEN;
	}

	if (nsend(&nhr)) lampanic("flatd (nsend)");

	if (request->flq_req == FLQFORGET) {
		free(fltable[i].fl_addr);
		fltable[i].fl_addr = EMPTY;
	}
}

/*
 *	flqclean
 *
 *	Function:	- removes tag and associated file
 */
static void
flqclean()

{
	int		i;

	for (i = 0; (i < FLMAX) && ((fltable[i].fl_tag != request->flq_tag)
			|| (fltable[i].fl_addr == EMPTY)); ++i);

	if (i >= FLMAX) {
		reply->flr_reply = EBADTAG;
	} else {
		reply->flr_reply = 0;
		unlink(fltable[i].fl_addr);
		fltable[i].fl_addr = EMPTY;
	}

	if (nsend(&nhr)) lampanic("flatd (nsend)");
}

/*
 *	flforget
 *
 *	Function:	- internal process access to FLQFORGET service
 *	Accepts:	- flat tag
 *	Returns:	- local address
 */
char *
flforget(tag)

int4			tag;

{
	int		i;
	char		*temp;

	for (i = 0; (i < FLMAX) && ((fltable[i].fl_tag != tag)
			|| (fltable[i].fl_addr == EMPTY)); ++i);

	if (i >= FLMAX) {
		errno = EBADTAG;
		return(0);
	} else {
		temp = fltable[i].fl_addr;
		fltable[i].fl_addr = EMPTY;
		return(temp);
	}
}

/*
 *	flwipe
 *
 *	Function:	- clean up everything
 *			- should become a request someday
 */
void
flwipe()

{
	int		i;

	for (i = 0; i < FLMAX; ++i) {

		if (fltable[i].fl_addr != EMPTY) {
			unlink(fltable[i].fl_addr);
			fltable[i].fl_addr = EMPTY;
		}
	}
}
