/*
 *
 * 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
 *
 *	$Id: pqdetach.c,v 6.8 1999/09/21 03:00:31 jsquyres Exp $
 * 
 *	Function:	- OTB kenyad
 *			- attaches and detaches kenya processes
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <t_types.h>
#include <unistd.h>

#include <args.h>
#include <events.h>
#include <kio.h>
#include <laminternal.h>
#include <net.h>
#include <preq.h>
#include <terror.h>
#include <typical.h>

/*
 * external variables
 */
extern struct pprocess	ptable[];	/* kenya process table */
extern struct preq	pq;		/* kenya request */
extern struct preply	pr;		/* kenya reply */
extern int		diedfd;
extern int		diesigfd;

/*
 * external functions
 */
extern void		psendr();
extern int		pfind();

/*
 * global functions
 */
void			childdetach();
void			childtrap();
void			pdetachindex();
void			pqattach();
void			pqdetach();

void
pqdetach()

{
	int		idetach;	/* descriptor index of target */
/*
 * Find the process descriptor for the given identifier.
 */
	idetach = pfind(pq.pq_pid);
	pr.pr_reply = (idetach < 0) ? ltot(ENOTPROCESS) : 0;
/*
 * Send reply of detach operation.
 */
	psendr((char *) &pr, sizeof(pr), 0);

	if (pr.pr_reply == 0) {
		pdetachindex(idetach);
	}
}

/*
 *	pdetachindex
 *
 *	Function:	- detaches a process identified by table index
 *	Accepts:	- table index
 */
void
pdetachindex(int idetach)
{
	int		i;
	int		found = FALSE;	/* found load module flag */
	struct pprocess	*pdetach;	/* descriptor ptr of target */
	struct nmsg	nhead;		/* reply message */

	pdetach = ptable + idetach;
/*
 * Flush IO.
 */
	if (pdetach->p_rtf & RTF_IO) {
		fflush(stderr);
		fflush(stdout);
	}

	if (pdetach->p_rtf & RTF_WAIT) {
/*
 * Reply to the parent process. This may not be the process which made the
 * kenyad request so we must save and restore the requester's synchronization.
 */
		pr.pr_reply = ltot(pq.pq_status);
		pr.pr_nodeid = ltot(getnodeid());
		pr.pr_pid = ltot(pdetach->p_pid);

		LAM_ZERO_ME(nhead);
		nhead.nh_node = pdetach->p_nodeid;
		nhead.nh_event = pdetach->p_event & 0xBFFFFFFF;
		nhead.nh_type = 1;
		nhead.nh_flags = NREEL;
		nhead.nh_length = sizeof(pr);
		nhead.nh_msg = (char *) &pr;

		if (nsend(&nhead))
		    lampanic("kenyad (nsend)");
	}
/*
 * Detach target process.
 */
	pdetach->p_pid = NOTPROCESS;
	pdetach->p_status = NOTSTATUS;

	if (pdetach->p_argc) {
		for (i = 0; i < pdetach->p_argc; i++)
		  free((char *) pdetach->p_argv[i]);
		free((char *) pdetach->p_argv);
	}
/*
 * Check to see if anybody else is using the load module.
 */
	if (pdetach->p_rtf & RTF_FLAT) {

		for (i = 0; (i < PMAX) && !found; ++i) {

			if ((ptable[i].p_pid != NOTPROCESS) &&
					((ptable + i) != pdetach) &&
					(! strcmp(ptable[i].p_loadpt,
					pdetach->p_loadpt))) {

				found = TRUE;
			}
		}

		if (!found) {
/*
 * Remove the load module file and free the filename buffer.
 */
			unlink(pdetach->p_loadpt);
		}
	}

	free(pdetach->p_loadpt);
}

/*
 *	pqattach
 *
 *	Function:	- attaches a process to kenyad
 */
void
pqattach()

{
	struct pprocess	*pattach;	/* new process ptr */
	int		iattach;	/* index of new process */
/*
 * Is the process already attached?
 */
	iattach = pfind(pq.pq_pid);

	if (iattach >= 0) {
		pr.pr_reply = 0;
		pattach = ptable + iattach;
		pattach->p_rtf = pq.pq_rtf & ~RTF_KENYA;
/*
 * If the request doesn't specify a new name, ignore the name.
 */
		if (pq.pq_name[0] != '\0') {
			free(pattach->p_argv[0]);
			pattach->p_argv[0] = malloc((unsigned)
					strlen(pq.pq_name) + 1);
			if (pattach->p_argv[0] == 0)
					lampanic("kenyad (malloc)");
			strcpy(pattach->p_argv[0], pq.pq_name);
			pattach->p_argc = 1;
		}
		else
			pattach->p_argc = 0;
	}
/*
 * Find a slot in the process table.
 */
	else if ((iattach = pfind((int4) NOTPROCESS)) < 0) {
		pr.pr_reply = ltot(ENOPDESCRIPTORS);
	}
/*
 * Fill in new kenya descriptor.
 */
	else {
		pattach = ptable + iattach;

		pattach->p_loadpt = malloc((unsigned) strlen(pq.pq_name) + 1);
		if (pattach->p_loadpt == 0) lampanic("kenyad (malloc)");

		strcpy(pattach->p_loadpt, pq.pq_name);

		if (pq.pq_name[0] != '\0') {
			pattach->p_argc = 1;
			pattach->p_argv = argvbreak(pq.pq_name, 0xa);
			if (pattach->p_argv == 0)
					lampanic("kenyad (pack_argv)");
		}
		else {
			pattach->p_argc = 0;
		}

		pattach->p_pid = pq.pq_pid;
		pattach->p_rtf = pq.pq_rtf;
		pattach->p_nodeid = NOTNODEID;
		pattach->p_event = NOTEVENT;
		pr.pr_reply = 0;
	}
}

/*
 *	childtrap
 *
 *	Function:	- traps child process termination
 *			- stores exit status in process table
 */
void
childtrap()

{
	int		i;		/* index in process table */
	int		pid;		/* child process ID */
	int		status;		/* child exit status */

	while (1) {
		pid = waitpid(-1, &status, WNOHANG);
		
		if (pid <= 0) {
			return;
		}

		i = pfind((int4) pid);

		if (i >= 0) {
			ptable[i].p_status = (int4) (WIFEXITED(status) ?
					WEXITSTATUS(status) : 1);
			if (diesigfd >= 0) {
				mwrite(diesigfd, (char *) &i, 1);
			}
		}
	}
}

/*
 *	childdetach
 *
 *	Function:	- detaches child process indicated via SIGCHLD
 */
void
childdetach()

{
	int		i;

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

		if (ptable[i].p_status != NOTSTATUS) {
			pq.pq_status = ptable[i].p_status;
			pdetachindex(i);
		}
	}
}

/*
 *	detachindex
 *
 *	Function:	- detaches a process identified by table index
 *	Accepts:	- table index
 */
static void
detachindex(int idetach)
{
	struct nmsg	nhead;
	struct preply	reply;			/* kenyad reply */
	struct pprocess	*pdetach;
	int		found = FALSE;		/* found load module flag */
	int		i;

	pdetach = ptable + idetach;
/*
 * Flush IO.
 */
	if (pdetach->p_rtf & RTF_IO) {
	    fflush(stderr);
	    fflush(stdout);
	}

	LAM_ZERO_ME(nhead);
	if (pdetach->p_rtf & RTF_WAIT) {
/*
 * Reply to the parent process. This may not be the process which made the
 * kenyad request so we must save and restore the requester's synchronization.
 */
	    nhead.nh_node = pdetach->p_nodeid;
	    nhead.nh_event = pdetach->p_event & 0xBFFFFFFF;
	    nhead.nh_type = 1;
	    nhead.nh_flags = NREEL;
	    nhead.nh_length = sizeof(reply);
	    nhead.nh_msg = (char *) &reply;

	    reply.pr_nodeid = ltot(getnodeid());
	    reply.pr_reply = ltot(pdetach->p_status);
	    reply.pr_pid = ltot(pdetach->p_pid);

	    if (nsend(&nhead))
		lampanic("kenyad (nsend)");
	}
/*
 * Detach target process.
 */
	pdetach->p_pid = NOTPROCESS;
	pdetach->p_status = NOTSTATUS;

	if (pdetach->p_argc) {
	    for (i = 0; i < pdetach->p_argc; i++)
	      free((char *) pdetach->p_argv[i]);
	    free((char *) pdetach->p_argv);
	}
/*
 * Check to see if anybody else is using the load module.
 */
	if (pdetach->p_rtf & RTF_FLAT) {
	    for (i=0; (i<PMAX) && !found; i++) {
		if ((ptable[i].p_pid != NOTPROCESS) && ((ptable + i) != pdetach)
		        && (!strcmp(ptable[i].p_loadpt, pdetach->p_loadpt))) {

		    found = TRUE;
		}
	    }

	    if (!found) {
/*
 * Remove the load module file and free the filename buffer.
 */
		unlink(pdetach->p_loadpt);
	    }
	}

	free(pdetach->p_loadpt);
}

/*
 *	childdeath
 *
 *	Function:	- handle child death
 *			- this is only used when kenyad is internal
 */
void
childdeath(void)
{
	int	i;

	read(diedfd, &i, sizeof(i));

	for (i=0; i<PMAX; i++) {
	    if (ptable[i].p_status != NOTSTATUS) {
		detachindex(i);
	    }
	}
}
