/*
 *
 * 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 1997 The Ohio State University
 *	GDB/RBD
 *
 *	$Id: clientio.c,v 6.11 1999/09/11 18:18:17 jsquyres Exp $
 *
 *	Function:	- interface between kernel runtime request library
 *			  and native environment
 */

#include <lam_config.h>
#include <sfh.h>

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>

#include <freq.h>
#include <kio.h>
#include <kreq.h>
#include <laminternal.h>
#include <terror.h>
#include <typical.h>

/*
 * global functions
 */
void			_cio_bail();		/* terminate self */
void			_cio_cleanup();		/* mach. dep. cleanup */
int			_cio_init();		/* initialize kernel I/O */
int			_cio_kreq();		/* make a kernel request */
int			_cio_kreqback();	/* 1st half of kreq */
int			_cio_kreqfront();	/* 2nd half of kreq */
int			_cio_send();		/* send data */
int			_cio_recv();		/* recv data */

/*
 * external variables
 */
struct kio_t		_kio;			/* Kernel I/O block */
struct fclient		_ufd[FUMAX];		/* user file desc. */

/*
 * local variables
 */
static int		sd_kernel;		/* kernel socket */

/*
 *	_cio_init
 *
 *	Function:	- initializes kernel interface
 *			- establishes UDP INET domain socket to communicate
 *			  with the kernel and other Trollius processes
 *	Returns:	- 0 or ERROR
 */
int
_cio_init()

{
	char		*p;

	(void) _lam_signal(SIGPIPE, 0);
	if ((p = sockname()) == 0) return(LAMERROR);
	sd_kernel = sfh_sock_open_clt_unix_stm(p);
	free(p);

	if (sd_kernel < 0) {

	    if ((errno == ENOENT) || (errno == ENXIO)) {
		errno = ENOKERNEL;
	    }

	    return(LAMERROR);
	}

	if (sfh_sock_set_buf_size(sd_kernel, SFH_UNIX, SO_SNDBUF, 
	    		KPKTLEN + sizeof(struct nmsg))) {
		return(LAMERROR);
	}

	if (sfh_sock_set_buf_size(sd_kernel, SFH_UNIX, SO_RCVBUF, 
	    		KPKTLEN + sizeof(struct nmsg))) {
		return(LAMERROR);
	}
/*
 * Set default stdio node and descriptors.
 */
	_kio.ki_ionode = NOTNODEID;
	_kio.ki_stdin = 0;
	_kio.ki_stdout = 1;
	_kio.ki_stderr = 2;

	if ((p = getenv("TROLLIUSFD"))) {
		_kio.ki_ionode = stoi((unsigned char *) p);
		p = strchr(p, ':') + 1;
		_kio.ki_stdin = stoi((unsigned char *) p);
		p = strchr(p, ':') + 1;
		_kio.ki_stdout = stoi((unsigned char *) p);
		p = strchr(p, ':') + 1;
		_kio.ki_stderr = stoi((unsigned char *) p);
	}

	_kio.ki_pid = -1;
/*
 * Get any inherited runtime flags.
 */
	_kio.ki_rtf = (p = getenv("TROLLIUSRTF")) 
			? stoi((unsigned char *) p) : 0;
/*
 * Have we been forked?
 */
	if ((p = getenv("LAMKENYAPID"))) {
		if (getppid() != stoi((unsigned char *) p)) {
			_kio.ki_rtf &= ~(RTF_WAIT | RTF_FLAT);
			_kio.ki_rtf |= RTF_FORKED;
		}
	}
/*
 * Some APIs want to know the total world population, a-priori.
 */
	_kio.ki_world = (p = getenv("LAMWORLD")) ? stoi((unsigned char *) p) : 0;

	_kio.ki_parent = (p = getenv("LAMPARENT")) 
				? stoi((unsigned char *) p) : 0;
/*
 * Set the LAM job identifier.
 */
	if ((p = getenv("LAMJOBID"))) {
		_kio.ki_jobid.jid_node = stoi((unsigned char *) p);
		p = strchr(p, ':') + 1;
		_kio.ki_jobid.jid_pid = stoi((unsigned char *) p);
	}

	return(0);
}

/*
 *	_cio_kreq
 *
 *	Function:	- makes a kernel request
 *	Accepts:	- kernel request ptr
 *			- kernel reply ptr
 *	Returns:	- 0 or ERROR
 */
int
_cio_kreq(pkq, pkr)

struct kreq		*pkq;
struct kreply		*pkr;

{
	if (_cio_kreqfront(pkq) < 0) {
		return(ERROR);
	}

	return(_cio_kreqback(pkr));
}

/*
 *	_cio_kreqfront
 *
 *	Function:	- front half of a kernel request
 *			- sends the request structure to the kernel
 *			- verifies kernel connection
 *	Accepts:	- kernel request ptr
 *	Returns:	- client/kernel socket or ERROR
 */
int
_cio_kreqfront(pkq)

struct kreq		*pkq;

{
/*
 * Send the request to the kernel.
 */
	if (mwrite(sd_kernel, (char *) pkq, sizeof(struct kreq)) <
			sizeof(struct kreq)) {

		if (errno == EPIPE) {

			if (pkq->kq_req != KQATTACH) {
				exit(ENOKERNEL);
			} else {
				errno = ENOKERNEL;
			}
		}

		return(LAMERROR);
	}

	return(sd_kernel);
}

/*
 *	_cio_kreqback
 *
 *	Function:	- back half of a kernel request
 *			- receives the reply structure from the kernel
 *	Accepts:	- kernel reply ptr
 *	Returns:	- 0 or ERROR
 */
int
_cio_kreqback(pkr)

struct kreply		*pkr;

{
	if (mread(sd_kernel, (char *) pkr, sizeof(struct kreply)) <
			sizeof(struct kreply)) {

		if (errno == EEOF) exit(ENOKERNEL);

		return(LAMERROR);
	}

	return(0);
}

/*
 *	_cio_recv
 *
 *	Function:	- receives a message from another process
 *			  through the kernel
 *	Accepts:	- kernel msg desc.
 *	Returns:	- 0 or ERROR
 */
int
_cio_recv(pkmsg)

struct kmsg		*pkmsg;

{
	int4		r;
	int4		totlen;
	struct iovec	iov[2];
	char		*sv_msg;

	if (pkmsg->k_flags & KPROBE) {
		return(0);
	}

	totlen = pkmsg->k_length;

	if (pkmsg->k_flags & KNMSG) {
		sv_msg = ((struct nmsg *) (pkmsg->k_msg))->nh_msg;
		iov[0].iov_base = pkmsg->k_msg;
		iov[0].iov_len = sizeof(struct nmsg);
		iov[1].iov_base = sv_msg;
		iov[1].iov_len = pkmsg->k_length;
		totlen += sizeof(struct nmsg);

		r = mreadv(sd_kernel, iov, 2);
		((struct nmsg *) (pkmsg->k_msg))->nh_msg = sv_msg;
	} else {
		r = mread(sd_kernel, pkmsg->k_msg, pkmsg->k_length);
	}

	if (r < totlen) {

		if (errno == EEOF) {
			errno = ENOKERNEL;
		}

		return(LAMERROR);
	}

	return(0);
}

/*
 *	_cio_send
 *
 *	Function:	- sends a message to another process
 *			  through the kernel
 *	Accepts:	- kernel msg desc.
 *	Returns:	- 0 or ERROR
 */
int
_cio_send(pkmsg)

struct kmsg		*pkmsg;

{
	int4		r;
	int4		totlen;
	struct iovec	iov[2];

	totlen = pkmsg->k_length;

	if (pkmsg->k_flags & KNMSG) {
		iov[0].iov_base = pkmsg->k_msg;
		iov[0].iov_len = sizeof(struct nmsg);
		iov[1].iov_base = ((struct nmsg *) (pkmsg->k_msg))->nh_msg;
		iov[1].iov_len = pkmsg->k_length;
		totlen += sizeof(struct nmsg);

		r = mwritev(sd_kernel, iov, 2);
	} else {
		r = mwrite(sd_kernel, pkmsg->k_msg, pkmsg->k_length);
	}

	if (r < totlen) {

		if (errno == EPIPE) {
			errno = ENOKERNEL;
		}

		return(LAMERROR);
	}

	return(0);
}

/*
 *	_cio_cleanup
 *
 *	Function:	- closes communication sockets
 */
void
_cio_cleanup()

{
	close(sd_kernel);
}

/*
 *	_cio_bail
 *
 *	Function:	- terminates the process
 *	Accepts:	- exit status
 */
void
_cio_bail(status)

int			status;

{
	exit(status);
}
