
/*
 *         PVM version 3.3:  Parallel Virtual Machine System
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * 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 appear in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM version 3 was funded in part by the U.S. Department of Energy,
 * the National Science Foundation and the State of Tennessee.
 */

/*
 *	lpvmgen.c
 *
 *	Libpvm generic functions.
 *
$Log: lpvmgen.c,v $
 * Revision 1.6  1994/11/07  21:36:33  manchek
 * flush messages on pvm_exit.
 * function prototype for SCO
 *
 * Revision 1.5  1994/10/15  19:12:35  manchek
 * check for system error in TM_SPAWN reply (cc != count)
 *
 * Revision 1.4  1994/06/03  20:38:16  manchek
 * version 3.3.0
 *
 * Revision 1.3  1993/11/30  15:52:42  manchek
 * implemented case autoerr == 2 in lpvmerr()
 *
 * Revision 1.2  1993/09/16  21:37:39  manchek
 * pvm_send() missing a return before lpvmerr()
 *
 * Revision 1.1  1993/08/30  23:26:48  manchek
 * Initial revision
 *
 */


#include <stdio.h>
#ifdef IMA_BSD386
#include <machine/endian.h>
#endif
#ifdef IMA_LINUX
#include <endian.h>
#endif
#include <rpc/types.h>
#include <rpc/xdr.h>
#ifdef	SYSVSTR
#include <string.h>
#define	CINDEX(s,c)	strchr(s,c)
#else
#include <strings.h>
#define	CINDEX(s,c)	index(s,c)
#endif
#include <signal.h>
#include <pvm3.h>
#include "global.h"
#include "pvmalloc.h"
#include "tdpro.h"
#include <pvmsdpro.h>
#include "pvmumbuf.h"
#include "listmac.h"
#include "bfunc.h"
#include "tvdefs.h"
#include <pvmtev.h>
#include "tevmac.h"


/***************
 **  Globals  **
 **           **
 ***************/

char *getenv();

extern int pvm_errno;					/* from lpvm.c */
extern int pvmautoerr;					/* from lpvm.c */
extern int pvmcouttid;					/* from lpvm.c */
extern int pvmcoutcod;					/* from lpvm.c */
extern int pvmctrctid;					/* from lpvm.c */
extern int pvmctrccod;					/* from lpvm.c */
extern int pvmmyptid;					/* from lpvm.c */
extern int pvmmytid;					/* from lpvm.c */
extern int pvmrbufmid;					/* from pack.c */
extern int pvmrescode;					/* from lpvm.c */
extern struct umbuf *pvmrxlist;			/* from lpvm.c */
extern int pvmsbufmid;					/* from pack.c */
extern int pvmschedtid;					/* from lpvm.c */
extern int pvmtidhmask;					/* from lpvm.c */
extern int pvmtoplvl;					/* from lpvm.c */
extern Pvmtmask pvmtrcmask;				/* from lpvm.c */
extern Pvmtmask pvmctrcmask;			/* from lpvm.c */
extern int pvmtrctid;					/* from lpvm.c */


/***************
 **  Private  **
 **           **
 ***************/

static int def_match();

static char rcsid[] = "$Id: lpvmgen.c,v 1.6 1994/11/07 21:36:33 manchek Exp $";
static char pvmtxt[512];				/* scratch for error log */
static int (*recv_match)() = def_match;


/**************************
 **  Internal Functions  **
 **                      **
 **************************/

static int
def_match(mid, tid, code)
	int mid;
	int tid;
	int code;
{
	struct umbuf *up;

	if (!(up = midtobuf(mid)))
		return PvmNoSuchBuf;
	return ((tid == -1 || tid == up->ub_src)
			&& (code == -1 || code == up->ub_cod)) ? 1 : 0;
}


/*	lpvmerr()
*
*	Error has occurred in libpvm function.
*	Action determined by pvmautoerr (set by setopt):
*		0	Do nothing
*		1	Print error message
*		2	Print error message, exit program with error code
*/

int
lpvmerr(f, n)
	char *f;		/* error location */
	int n;			/* error code */
{
	char buf[128];

	pvm_errno = n;
	if (pvmautoerr) {
		buf[0] = 0;
		strncat(buf, f, sizeof(buf)-4);
		strcat(buf, "()");
		pvm_perror(buf);
		fflush(stderr);
		if (pvmautoerr == 2) {
			pvmautoerr = 1;
			pvm_exit();
			exit(n);
		}
	}
	return n;
}


/************************
 **  Libpvm Functions  **
 **                    **
 ************************/

int
pvm_addhosts(names, count, svp)
	char **names;	/* host name vector */
	int count;		/* length of names */
	int *svp;		/* status vector return */
{
	int sbf, rbf;
	int cc;
	int i;
	int *sv;		/* status vector */
	char buf[128];
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_ADDHOSTS0)) {
			pvm_pkint(&count, 1, 1);
			for (i = 0; i < count; i++)
				pvm_pkstr(names[i]);
			TEV_FIN;
		}
	}

	if (count < 1 || count > (pvmtidhmask >> (ffs(pvmtidhmask) - 1))) {
		cc = PvmBadParam;

	} else {
		if (!(cc = BEATASK)) {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			pvm_pkint(&count, 1, 1);
			for (i = 0; i < count; i++)
				pvm_pkstr(names[i]);

			if (pvmschedtid)
				cc = msendrecv(pvmschedtid, SM_ADDHOST);
			else
				cc = msendrecv(TIDPVMD, TM_ADDHOST);
			if (cc > 0) {
				pvm_upkint(&cc, 1, 1);
				if (cc >= 0) {
					if (cc == count) {
						pvm_upkint(&cc, 1, 1);	/* toss narches */
						sv = TALLOC(count, int, "sv1");
						cc = 0;
						for (i = 0; i < count; i++) {
							pvm_upkint(&sv[i], 1, 1);
							pvm_upkstr(buf);	/* toss name, arch, speed */
							pvm_upkstr(buf);
							pvm_upkint((int *)buf, 1, 1);
							if (sv[i] >= 0)
								cc++;
						}
						if (svp)
							BCOPY((char*)sv, (char*)svp, count * sizeof(int));
						PVM_FREE(sv);

					} else {
						sprintf(pvmtxt,
								"pvm_addhosts() sent count %d received count %d\n",
								count, cc);
						pvmlogerror(pvmtxt);
						cc = PvmOutOfRes;
					}
				}
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_ADDHOSTS1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_addhosts", cc);
	return cc;
}


int
pvm_config(nhostp, narchp, hostp)
	int *nhostp;
	int *narchp;
	struct pvmhostinfo **hostp;
{
	int sbf, rbf, cc;
	static int nhost = 0;
	static int narch = 0;
	static struct pvmhostinfo *hlist = 0;
	int i;
	char buf[256];	/* XXX static limit, argh */
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_CONFIG0))
			TEV_FIN;
	}

	if (hlist) {
		while (nhost-- > 0) {
			PVM_FREE(hlist[nhost].hi_name);
			PVM_FREE(hlist[nhost].hi_arch);
		}
		PVM_FREE(hlist);
		hlist = 0;
		nhost = 0;
	}
	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		if (pvmschedtid)
			cc = msendrecv(pvmschedtid, SM_CONFIG);
		else
			cc = msendrecv(TIDPVMD, TM_CONFIG);
		if (cc > 0) {
			pvm_upkint(&nhost, 1, 1);
			pvm_upkint(&narch, 1, 1);
			hlist = TALLOC(nhost, struct pvmhostinfo, "hi");
			for (i = 0; i < nhost; i++) {
				pvm_upkint(&hlist[i].hi_tid, 1, 1);
				pvm_upkstr(buf);
				hlist[i].hi_name = STRALLOC(buf);
				pvm_upkstr(buf);
				hlist[i].hi_arch = STRALLOC(buf);
				pvm_upkint(&hlist[i].hi_speed, 1, 1);
			}
			pvm_freebuf(pvm_setrbuf(rbf));
			if (nhostp)
				*nhostp = nhost;
			if (narchp)
				*narchp = narch;
			if (hostp)
				*hostp = hlist;
			cc = 0;
		}
		pvm_freebuf(pvm_setsbuf(sbf));
		pvm_setrbuf(rbf);
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_CONFIG1)) {
			pvm_pkint(&cc, 1, 1);
			pvm_pkint(&nhost, 1, 1);
			pvm_pkint(&narch, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_config", cc);
	return cc;
}


int
pvm_delete(name, req)
	char *name;		/* class name */
	int req;		/* class index or -1 for all */
{
	int sbf, rbf, cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_DELETE0)) {
			pvm_pkstr(name ? name : "");
			pvm_pkint(&req, 1, 1);
			TEV_FIN;
		}
	}

	if (!name || !*name || req < -1) {
		cc = PvmBadParam;

	} else {
		if (!(cc = BEATASK)) {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			cc = TMDB_DELETE;
			pvm_pkint(&cc, 1, 1);
			pvm_pkstr(name);
			pvm_pkint(&req, 1, 1);
			if ((cc = msendrecv(TIDPVMD, TM_DB)) > 0) {
				pvm_upkint(&cc, 1, 1);
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_DELETE1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0 && cc != PvmNoEntry)
		lpvmerr("pvm_delete", cc);
	return cc;
}


int
pvm_delhosts(names, count, svp)
	char **names;
	int count;
	int *svp;		/* status vector return */
{
	int sbf, rbf;
	int cc;
	int i;
	int *sv;		/* return values */
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_DELHOSTS0)) {
			pvm_pkint(&count, 1, 1);
			for (i = 0; i < count; i++)
				pvm_pkstr(names[i]);
			TEV_FIN;
		}
	}

	if (count < 1 || count > (pvmtidhmask >> (ffs(pvmtidhmask) - 1))) {
		cc = PvmBadParam;

	} else {
		if (!(cc = BEATASK)) {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			pvm_pkint(&count, 1, 1);
			for (i = 0; i < count; i++)
				pvm_pkstr(names[i]);

			if (pvmschedtid)
				cc = msendrecv(pvmschedtid, SM_DELHOST);
			else
				cc = msendrecv(TIDPVMD, TM_DELHOST);
			if (cc > 0) {
				pvm_upkint(&cc, 1, 1);
				if (cc >= 0) {
					if (cc == count) {
						sv = TALLOC(count, int, "sv2");
						pvm_upkint(sv, count, 1);
						cc = 0;
						for (i = count; i-- > 0; )
							if (sv[i] >= 0)
								cc++;
						if (svp)
							BCOPY((char*)sv, (char*)svp, count * sizeof(int));
						PVM_FREE(sv);

					} else {
						sprintf(pvmtxt,
								"pvm_delhosts() sent count %d received count %d\n",
								count, cc);
						pvmlogerror(pvmtxt);
						cc = PvmOutOfRes;
					}
				}
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_DELHOSTS1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_delhosts", cc);
	return cc;
}


int
pvm_exit()
{
	int sbf, rbf;
	int cc = 0;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_EXIT0))
			TEV_FIN;
	}

	if (pvmmytid != -1) {
		while (pvm_nrecv(-1, -1) > 0) ;		/* XXX attempt to flush messages */
		pvmflusho();
		fflush(stderr);
		fflush(stdout);

		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		if ((cc = msendrecv(TIDPVMD, TM_EXIT)) > 0) {
			pvm_freebuf(pvm_setrbuf(rbf));
			cc = 0;

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));

		pvmendtask();
	}

	if (x) {
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_exit", cc);
	return cc;
}


int
pvm_halt()
{
	int cc, sbf, rbf;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_HALT0))
			TEV_FIN;
	}

	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		cc = (msendrecv(TIDPVMD, TM_HALT) < 0) ? 0 : PvmSysErr;
		pvm_freebuf(pvm_setsbuf(sbf));
		pvm_setrbuf(rbf);
	}

	if (x) {
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_halt", cc);
	return cc;
}


int
pvm_insert(name, req, data)
	char *name;		/* class name */
	int req;		/* requested class index or -1 for any */
	int data;
{
	int sbf, rbf, cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_INSERT0)) {
			pvm_pkstr(name ? name : "");
			pvm_pkint(&req, 1, 1);
			pvm_pkint(&data, 1, 1);
			TEV_FIN;
		}
	}

	if (!name || !*name || req < -1) {
		cc = PvmBadParam;

	} else {
		if (!(cc = BEATASK)) {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			cc = TMDB_INSERT;
			pvm_pkint(&cc, 1, 1);
			pvm_pkstr(name);
			pvm_pkint(&req, 1, 1);
			pvm_pkint(&data, 1, 1);
			if ((cc = msendrecv(TIDPVMD, TM_DB)) > 0) {
				pvm_upkint(&cc, 1, 1);
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_INSERT1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0 && cc != PvmDupEntry)
		lpvmerr("pvm_insert", cc);
	return cc;
}


int
pvm_kill(tid)
	int tid;
{
	int cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_KILL0)) {
			pvm_pkint(&tid, 1, 1);
			TEV_FIN;
		}
	}

	cc = pvm_sendsig(tid, SIGTERM);

	if (x) {
		if (TEV_DO_TRACE(TEV_KILL1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}
	if (cc < 0)
		lpvmerr("pvm_kill", cc);
	return cc;
}


int
pvm_lookup(name, req, datap)
	char *name;		/* class name */
	int req;		/* req class index or -1 for any */
	int *datap;		/* data return */
{
	int sbf, rbf, cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_LOOKUP0)) {
			pvm_pkstr(name ? name : "");
			pvm_pkint(&req, 1, 1);
			TEV_FIN;
		}
	}

	if (!name || !*name || req < -1) {
		cc = PvmBadParam;

	} else {
		if (!(cc = BEATASK)) {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			cc = TMDB_LOOKUP;
			pvm_pkint(&cc, 1, 1);
			pvm_pkstr(name);
			pvm_pkint(&req, 1, 1);
			if ((cc = msendrecv(TIDPVMD, TM_DB)) > 0) {
				pvm_upkint(&cc, 1, 1);
				if (cc >= 0 && datap)
					pvm_upkint(datap, 1, 1);
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_LOOKUP1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0 && cc != PvmNoEntry)
		lpvmerr("pvm_lookup", cc);
	return cc;
}


int
pvm_mcast(tids, count, code)
	int *tids;	/* dest tasks */
	int count;	/* number of tids */
	int code;	/* type code */
{
	int cc;			/* for return codes */
	int sbf, rbf;	/* temp messages */
	int mca;		/* multicast address allocated for message */
	int i;
	int x;
	static struct timeval ztv = { 0, 0 };

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_MCAST0)) {
			pvm_pkint(&count, 1, 1);
			pvm_pkint(&code, 1, 1);
			pvm_pkint(tids, count, 1);
			TEV_FIN;
		}
	}

	/* sanity check args and sendable message */

	if (!(cc = BEATASK)) {
		if (pvmsbufmid <= 0)
			cc = PvmNoBuf;
		else
			if (count < 0 || (!pvmrescode && (code & ~0x7fffffff)))
				cc = PvmBadParam;
			else
				if (!pvmrescode)
					for (i = count; i-- > 0; )
						if (!TIDISTASK(tids[i])) {
							cc = PvmBadParam;
							break;
						}

	}
	if (!cc && count > 0) {

		/* allocate multicast address */

		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&count, 1, 1);
		pvm_pkint(tids, count, 1);
		if ((cc = msendrecv(TIDPVMD, TM_MCA)) > 0) {
			cc = pvm_upkint(&mca, 1, 1);
			pvm_freebuf(pvm_setrbuf(rbf));

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));

		/* send message */

		if (cc >= 0)
			if ((cc = mroute(pvmsbufmid, mca, code, &ztv)) >= 0)
				cc = 0;
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_MCAST1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_mcast", cc);
	return cc;
}


int
pvm_mytid()
{
	int cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_MYTID0))
			TEV_FIN;
	}

	if (!(cc = BEATASK))
		cc = pvmmytid;

	if (x) {
		if (TEV_DO_TRACE(TEV_MYTID1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_mytid", cc);
	return cc;
}


int
pvm_mstat(host)
	char *host;
{
	int sbf, rbf, cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_MSTAT0)) {
			pvm_pkstr(host ? host : "");
			TEV_FIN;
		}
	}
	if (!host || !*host) {
		cc = PvmBadParam;

	} else {
		if (!(cc = BEATASK)) {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			pvm_pkstr(host);
			if ((cc = msendrecv(TIDPVMD, TM_MSTAT)) > 0) {
				pvm_upkint(&cc, 1, 1);
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_MSTAT1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0 && cc != PvmNoHost && cc != PvmHostFail)
		lpvmerr("pvm_mstat", cc);
	return cc;
}


int
pvm_notify(what, code, count, vals)
	int what;
	int code;
	int count;
	int *vals;
{
	int sbf;
	int cc;
	int numtid;
	int x;
	static struct timeval ztv = { 0, 0 };

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_NOTIFY0)) {
			pvm_pkint(&what, 1, 1);
			pvm_pkint(&code, 1, 1);
			pvm_pkint(&count, 1, 1);
			if (what != PvmHostAdd)
				pvm_pkint(vals, count, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (!pvmrescode && (code & ~0x7fffffff)) {
			cc = PvmBadParam;

		} else {
			switch (what) {

			case PvmHostDelete:
				if (count < 1)
					cc = PvmBadParam;
				numtid = count;
				break;

			case PvmTaskExit:
				if (count < 1)
					cc = PvmBadParam;
				else
					for (numtid = count; numtid-- > 0; )
						if (!TIDISTASK(vals[numtid])) {
							cc = PvmBadParam;
							break;
						}
				numtid = count;
				break;

			case PvmHostAdd:
				numtid = 0;
				vals = &numtid;
				break;

			default:
				cc = PvmBadParam;
				break;
			}

			if (!cc) {
				sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
				pvm_pkint(&what, 1, 1);
				pvm_pkint(&code, 1, 1);
				pvm_pkint(&count, 1, 1);
				pvm_pkint(vals, numtid, 1);

				if (pvmschedtid)
					cc = mroute(pvmsbufmid, pvmschedtid, SM_NOTIFY, &ztv);
				else
					cc = mroute(pvmsbufmid, TIDPVMD, TM_NOTIFY, &ztv);
				pvm_freebuf(pvm_setsbuf(sbf));
				if (cc > 0)
					cc = 0;
			}
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_NOTIFY1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_notify", cc);
	return cc;
}


int
pvm_nrecv(tid, code)
	int tid;
	int code;
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;
	int alrdy = 0;
	int x;
	static struct timeval ztv = { 0, 0 };

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_NRECV0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&code, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (pvmrbufmid > 0)
			umbuf_free(pvmrbufmid);
		pvmrbufmid = 0;

		for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
			if (up == pvmrxlist && bestcc)
				break;
			while (up == pvmrxlist) {
				if (alrdy) {
					cc = 0;
					goto done;
				}
				up = up->ub_rlink;
				if ((cc = mroute(0, 0, 0, &ztv)) < 0)
					goto done;
				up = up->ub_link;
				alrdy = 1;
			}

			if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
				goto done;
			if (cc == 1) {
				bestup = up;
				break;
			}
			if (cc > bestcc) {
				bestcc = cc;
				bestup = up;
			}
		}

		LISTDELETE(bestup, ub_link, ub_rlink);
		bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
		if (!(cc = pvm_setrbuf(bestup->ub_mid)))
			cc = bestup->ub_mid;
	}

done:
	if (x) {
		if (TEV_DO_TRACE(TEV_NRECV1)) {
			int a[3];

			pvm_pkint(&cc, 1, 1);
			if (cc > 0) {
				pvm_bufinfo(cc, &a[0], &a[1], &a[2]);
				pvm_pkint(a, 3, 1);

			} else {
				a[0] = -1;
				pvm_pkint(a, 1, 1);
			}
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_nrecv", cc);
	return cc;
}


int
pvm_parent()
{
	int cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_PARENT0))
			TEV_FIN;
	}

	if (!(cc = BEATASK))
		cc = pvmmyptid ? pvmmyptid : PvmNoParent;

	if (x) {
		if (TEV_DO_TRACE(TEV_PARENT1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0 && cc != PvmNoParent)
		lpvmerr("pvm_parent", cc);
	return cc;
}


int
pvm_probe(tid, code)
	int tid;
	int code;
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;
	int alrdy = 0;
	int x;
	static struct timeval ztv = { 0, 0 };

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_PROBE0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&code, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
			if (up == pvmrxlist && bestcc)
				break;
			while (up == pvmrxlist) {
				if (alrdy) {
					cc = 0;
					goto done;
				}
				up = up->ub_rlink;
				if ((cc = mroute(0, 0, 0, &ztv)) < 0)
					goto done;
				up = up->ub_link;
				alrdy = 1;
			}

			if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
				goto done;
			if (cc == 1) {
				bestup = up;
				break;
			}
			if (cc > bestcc) {
				bestcc = cc;
				bestup = up;
			}
		}
		bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
		cc = bestup->ub_mid;
	}

done:
	if (x) {
		if (TEV_DO_TRACE(TEV_PROBE1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_probe", cc);
	return cc;
}


int
pvm_pstat(tid)
	int tid;	/* task */
{
	int sbf, rbf;
	int cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_PSTAT0)) {
			pvm_pkint(&tid, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (!TIDISTASK(tid))
			cc = PvmBadParam;

		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			pvm_pkint(&tid, 1, 1);
			if ((cc = msendrecv(TIDPVMD, TM_PSTAT)) > 0) {
				pvm_upkint(&cc, 1, 1);
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_PSTAT1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0 && cc != PvmNoTask)
		lpvmerr("pvm_pstat", cc);
	return cc;
}


int
pvm_recv(tid, code)
	int tid;
	int code;
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_RECV0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&code, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (pvmrbufmid > 0)
			umbuf_free(pvmrbufmid);
		pvmrbufmid = 0;

		for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
			if (up == pvmrxlist && bestcc)
				break;
			while (up == pvmrxlist) {
				up = up->ub_rlink;
				if ((cc = mroute(0, 0, 0, (struct timeval *)0)) < 0)
					goto done;
				up = up->ub_link;
			}

			if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
				goto done;
			if (cc == 1) {
				bestup = up;
				break;
			}
			if (cc > bestcc) {
				bestcc = cc;
				bestup = up;
			}
		}

		LISTDELETE(bestup, ub_link, ub_rlink);
		bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
		if (!(cc = pvm_setrbuf(bestup->ub_mid)))
			cc = bestup->ub_mid;
	}

done:
	if (x) {
		if (TEV_DO_TRACE(TEV_RECV1)) {
			int a[3];

			pvm_pkint(&cc, 1, 1);
			if (cc > 0) {
				pvm_bufinfo(cc, &a[0], &a[1], &a[2]);
				pvm_pkint(a, 3, 1);

			} else {
				a[0] = -1;
				pvm_pkint(a, 1, 1);
			}
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_recv", cc);
	return cc;
}


int (*
pvm_recvf(new))()
#ifdef	IMA_SCO
	int (*new)(int,int,int);
#else
	int (*new)();
#endif
{
	int (*old)() = recv_match;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_RECVF0))
			TEV_FIN;
	}

	recv_match = new ? new : def_match;

	if (x) {
		if (TEV_DO_TRACE(TEV_RECVF1))
			TEV_FIN;
		pvmtoplvl = x;
	}

	return (old == def_match ? 0 : old);
}


int
pvm_send(tid, code)
	int tid;	/* dest task */
	int code;	/* type code */
{
	int cc;
	int x;
	static struct timeval ztv = { 0, 0 };

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_SEND0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&code, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (!pvmrescode && (!TIDISTASK(tid) || (code & ~0x7fffffff)))
			cc = PvmBadParam;
		else
			if (pvmsbufmid <= 0)
				cc = PvmNoBuf;
			else

/* XXX short-ckt to us should go here.  maybe can inc frag chain
   XXX count and make new message, put on pvmrxlist. */
				if ((cc = mroute(pvmsbufmid, tid, code, &ztv)) > 0)
					cc = 0;
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_SEND1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_send", cc);
	return cc;
}


int
pvm_sendsig(tid, signum)
	int tid;
	int signum;
{
	int cc;
	int sbf, rbf;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_SENDSIG0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&signum, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (!TIDISTASK(tid))
			cc = PvmBadParam;

		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);

			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&signum, 1, 1);
			if ((cc = msendrecv(TIDPVMD, TM_SENDSIG)) > 0) {
				pvm_freebuf(pvm_setrbuf(rbf));
				cc = 0;

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_SENDSIG1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_sendsig", cc);
	return cc;
}


/*	bubble()
*
*	Move nonnegative els to head of array, negative ones to end.
*	Returns number of nonnegative els.
*/

static int
bubble(n, a)
	int n;			/* length of a */
	int *a;
{
	int r, w, t;

	for (w = r = 0; r < n; r++) {
		if (a[w] < 0) {
			if (a[r] >= 0) {
				t = a[w];
				a[w] = a[r];
				a[r] = t;
				w++;
			}

		} else {
			w++;
		}
	}
	return w;
}


#if	defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)
static int
pvmgetenvars(ep)
	char ***ep;
{
	return 0;
}

#else	/*defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)*/
static int
pvmgetenvars(ep)
	char ***ep;
{
	char **xpl;			/* vars to export */
	int mxpl;			/* cur length of xpl */
	int nxpl;			/* num vars found */
	char buf[200];
	char *p, *q;
	int n;

	if (p = getenv("PVM_EXPORT")) {
		mxpl = 5;
		xpl = TALLOC(mxpl, char *, "env");
		xpl[0] = p - 11;
		nxpl = 1;
		while (1) {
			while (*p == ':')
				p++;
			if (!*p)
				break;
			n = (q = CINDEX(p, ':')) ? q - p : strlen(p);
			strncpy(buf, p, n);
			buf[n] = 0;
			if (q = getenv(buf)) {
				if (nxpl == mxpl) {
					mxpl += mxpl / 2 + 1;
					xpl = TREALLOC(xpl, mxpl, char *);
				}
				xpl[nxpl++] = q - n - 1;
			}
			p += n;
		}
		*ep = xpl;
		return nxpl;

	} else {
		return 0;
	}
}
#endif	/*defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)*/


int
pvm_spawn(file, argv, flags, where, count, tids)
	char *file;
	char **argv;
	int flags;
	char *where;
	int count;
	int *tids;
{
	int sbf, rbf;	/* temp for current tx, rx msgs */
	int cc;
	int i, n;
	char **ep;
	int *tidlist = 0;
	char buf[TEV_MASK_LENGTH + 20];
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_SPAWN0)) {
			pvm_pkstr(file ? file : "");
			pvm_pkint(&flags, 1, 1);
			pvm_pkstr(where ? where : "");
			pvm_pkint(&count, 1, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (count < 1)
			cc = PvmBadParam;

		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);

			pvm_pkstr(file);
			pvm_pkint(&flags, 1, 1);
			pvm_pkstr(where ? where : "");
			pvm_pkint(&count, 1, 1);
			if (argv)
				for (n = 0; argv[n]; n++);
			else
				n = 0;
			pvm_pkint(&n, 1, 1);
			for (i = 0; i < n; i++)
				pvm_pkstr(argv[i]);

			pvm_pkint(&pvmcouttid, 1, 1);
			pvm_pkint(&pvmcoutcod, 1, 1);
			pvm_pkint(&pvmctrctid, 1, 1);
			pvm_pkint(&pvmctrccod, 1, 1);

			n = pvmgetenvars(&ep) + 1;
			pvm_pkint(&n, 1, 1);
			n--;
			sprintf(buf, "PVMTMASK=%s", pvmctrcmask);
			pvm_pkstr(buf);
			if (n > 0) {
				for (i = 0; i < n; i++)
					pvm_pkstr(ep[i]);
				PVM_FREE(ep);
			}

			if (pvmschedtid)
				cc = msendrecv(pvmschedtid, SM_SPAWN);
			else
				cc = msendrecv(TIDPVMD, TM_SPAWN);
			if (cc > 0) {
				pvm_upkint(&cc, 1, 1);
				if (cc == count) {
					tidlist = tids ? tids : TALLOC(count, int, "xxx");
					pvm_upkint(tidlist, cc, 1);
					cc = bubble(cc, tidlist);
				}
				pvm_freebuf(pvm_setrbuf(rbf));

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_SPAWN1)) {
			pvm_pkint(&cc, 1, 1);
			if (cc > 0)
				pvm_pkint(tidlist, cc, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (tidlist != tids)
		PVM_FREE(tidlist);

	if (cc < 0)
		lpvmerr("pvm_spawn", cc);
	return cc;
}


int
pvm_tasks(where, ntaskp, taskp)
	int where;					/* which host or 0 for all */
	int *ntaskp;
	struct pvmtaskinfo **taskp;
{
	int cc, ec, sbf, rbf, ae;
	static struct pvmtaskinfo *tlist = 0;
	static int ntask = 0;
	int len1 = 5, len2 = 3;
	char buf[1024];
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_TASKS0)) {
			pvm_pkint(&where, 1, 1);
			TEV_FIN;
		}
	}

	if (tlist) {
		while (ntask-- > 0)
			PVM_FREE(tlist[ntask].ti_a_out);
		PVM_FREE(tlist);
		tlist = 0;
		ntask = 0;
	}

	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&where, 1, 1);

		if (pvmschedtid)
			cc = msendrecv(pvmschedtid, SM_TASK);
		else
			cc = msendrecv(TIDPVMD, TM_TASK);
		if (cc > 0) {
			if (!(cc = pvm_upkint(&ec, 1, 1))
			&& (cc = ec) >= 0) {
				tlist = TALLOC(len1, struct pvmtaskinfo, "ti");
				ae = pvm_setopt(PvmAutoErr, 0);
				ntask = 0;
				while (!pvm_upkint(&tlist[ntask].ti_tid, 1, 1)) {
					pvm_upkint(&tlist[ntask].ti_ptid, 1, 1);
					pvm_upkint(&tlist[ntask].ti_host, 1, 1);
					pvm_upkint(&tlist[ntask].ti_flag, 1, 1);
					pvm_upkstr(buf);
					tlist[ntask].ti_a_out = STRALLOC(buf);
					pvm_upkint(&tlist[ntask].ti_pid, 1, 1);
					ntask++;
					if (ntask == len1) {
						len1 += len2;
						len2 = ntask;
						tlist = TREALLOC(tlist, len1, struct pvmtaskinfo);
					}
				}
				pvm_setopt(PvmAutoErr, ae);
				cc = 0;
			}
			pvm_freebuf(pvm_setrbuf(rbf));
			if (ntaskp)
				*ntaskp = ntask;
			if (taskp)
				*taskp = tlist;
		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_TASKS1)) {
			pvm_pkint(&cc, 1, 1);
			pvm_pkint(&ntask, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_tasks", cc);
	return cc;
}


int
pvm_tickle(narg, argp, nresp, resp)
	int narg;
	int *argp;
	int *nresp;
	int *resp;
{
	int cc;
	int sbf, rbf;
	int nres;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_TICKLE0)) {
			pvm_pkint(&narg, 1, 1);
			pvm_pkint(argp, narg, 1);
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		if (narg < 1 || narg > 10)
			cc = PvmBadParam;

		else {

			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			pvm_pkint(&narg, 1, 1);
			pvm_pkint(argp, narg, 1);
			if ((cc = msendrecv(TIDPVMD, TM_TICKLE)) > 0) {
				pvm_upkint(&nres, 1, 1);
				if (nresp)
					*nresp = nres;
				if (resp)
					pvm_upkint(resp, nres, 1);
				pvm_freebuf(pvm_setrbuf(rbf));
				cc = 0;

			} else
				pvm_setrbuf(rbf);
			pvm_freebuf(pvm_setsbuf(sbf));
		}
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_TICKLE1)) {
			pvm_pkint(&cc, 1, 1);
			pvm_pkint(&nres, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_tickle", cc);
	return cc;
}


int
pvm_tidtohost(tid)
	int tid;
{
	return (tid & pvmtidhmask);
}


int
pvm_trecv(tid, code, tmout)
	int tid;				/* source tid to match */
	int code;				/* message tag to match */
	struct timeval *tmout;	/* time to wait for match */
{
	struct umbuf *up;
	struct umbuf *bestup;
	int bestcc = 0;
	int cc;
	struct timeval tin, tnow;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_TRECV0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&code, 1, 1);
			if (tmout) {
				cc = tmout->tv_sec;
				pvm_pkint(&cc, 1, 1);
				cc = tmout->tv_usec;
				pvm_pkint(&cc, 1, 1);

			} else {
				cc = -1;
				pvm_pkint(&cc, 1, 1);
				pvm_pkint(&cc, 1, 1);
			}
			TEV_FIN;
		}
	}

	gettimeofday(&tin, (struct timezone*)0);

	if (!(cc = BEATASK)) {
		if (pvmrbufmid > 0)
			umbuf_free(pvmrbufmid);
		pvmrbufmid = 0;

		for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
			if (up == pvmrxlist && bestcc)
				break;
			while (up == pvmrxlist) {
				up = up->ub_rlink;
				if (tmout) {
					gettimeofday(&tnow, (struct timezone*)0);
					TVXSUBY(&tnow, &tnow, &tin);
					if (TVXLTY(tmout, &tnow)) {
						if (bestcc)
							goto fnd;
						cc = 0;
						goto done;
					}
					TVXSUBY(&tnow, tmout, &tnow);
					if ((cc = mroute(0, 0, 0, &tnow)) < 0)
						goto done;

				} else {
					if ((cc = mroute(0, 0, 0, (struct timeval *)0)) < 0)
						goto done;
				}
				up = up->ub_link;
			}

			if ((cc = recv_match(up->ub_mid, tid, code)) < 0)
				goto done;
			if (cc == 1) {
				bestup = up;
				break;
			}
			if (cc > bestcc) {
				bestcc = cc;
				bestup = up;
			}
		}

fnd:
		LISTDELETE(bestup, ub_link, ub_rlink);
		bestup->ub_flag &= ~(UB_PACK|UB_UPACK);
		if (!(cc = pvm_setrbuf(bestup->ub_mid)))
			cc = bestup->ub_mid;
	}

done:
	if (x) {
		if (TEV_DO_TRACE(TEV_TRECV1)) {
			int a[3];

			pvm_pkint(&cc, 1, 1);
			if (cc > 0) {
				pvm_bufinfo(cc, &a[0], &a[1], &a[2]);
				pvm_pkint(a, 3, 1);

			} else {
				a[0] = -1;
				pvm_pkint(a, 1, 1);
			}
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_trecv", cc);
	return cc;
}


char *
pvm_version()
{
	return PVM_VER;
}


int
pvm_reg_rm(hip)
	struct pvmhostinfo **hip;
{
	int old_sched;
	int cc;
	int sbf;
	int rbf;
	char buf[256];	/* XXX static limit, argh */
	static struct pvmhostinfo *hin = 0;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_REG_RM0)) {
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);

		if (pvmschedtid)
			cc = msendrecv(pvmschedtid, SM_SCHED);
		else
			cc = msendrecv(TIDPVMD, TM_SCHED);
		if (cc > 0) {
			pvm_upkint(&cc, 1, 1);
			if (cc >= 0) {
				if (hin) {
					PVM_FREE(hin->hi_name);
					PVM_FREE(hin->hi_arch);

				} else
					hin = TALLOC(1, struct pvmhostinfo, "hi");
				pvm_upkint(&hin->hi_tid, 1, 1);
				pvm_upkstr(buf);
				hin->hi_name = STRALLOC(buf);
				pvm_upkstr(buf);
				hin->hi_arch = STRALLOC(buf);
				pvm_upkint(&hin->hi_speed, 1, 1);
				if (hip)
					*hip = hin;
			}
			pvm_freebuf(pvm_setrbuf(rbf));

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_REG_RM1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_reg_rm", cc);
	return cc;
}


int
pvm_reg_tasker()
{
	static int imit = 0;	/* i'm the tasker */

	int cc;
	int sbf;
	int rbf;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_REG_TASKER0)) {
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);

		cc = imit ? 0 : 1;
		pvm_pkint(&cc, 1, 1);
		cc = msendrecv(TIDPVMD, TM_TASKER);
		if (cc > 0) {
			pvm_upkint(&cc, 1, 1);
			if (!cc)
				imit = !imit;
			pvm_freebuf(pvm_setrbuf(rbf));
			cc = 0;

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_REG_TASKER1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_reg_tasker", cc);
	return cc;
}


int
pvm_reg_hoster()
{
	int cc;
	int sbf;
	int rbf;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_REG_HOSTER0)) {
			TEV_FIN;
		}
	}

	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);

		cc = msendrecv(TIDPVMD, TM_HOSTER);
		if (cc > 0) {
			pvm_freebuf(pvm_setrbuf(rbf));
			cc = 0;

		} else
			pvm_setrbuf(rbf);
		pvm_freebuf(pvm_setsbuf(sbf));
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_REG_HOSTER1)) {
			pvm_pkint(&cc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (cc < 0)
		lpvmerr("pvm_reg_hoster", cc);
	return cc;
}


/*	pvm_hostsync()
*
*	Get time of day clock from remote host.
*	Returns current time on remote host,
*	difference between local clock and remote host clock.
*
*	Note the delta time is a 2s-compl./1000000-compl. signed timeval.
*	Positive values are normal, negative ones are f.e.:
*		-1 uSec  = -1,999999
*		-1 Sec   = -1,000000
*		-1.1 Sec = -2,999000
*/

int
pvm_hostsync(host, clk, delta)
	int host;				/* pvmd tid of host */
	struct timeval *clk;	/* current time on host */
	struct timeval *delta;	/* time relative to local clock */
{
	int cc;
	int sbf, rbf;
	struct timeval myta, mytb, remt;
	int i[2];

	if (!(cc = BEATASK)) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		rbf = pvm_setrbuf(0);
		pvm_pkint(&host, 1, 1);

		gettimeofday(&myta, (struct timezone *) NULL);
		if ((cc = msendrecv(TIDPVMD, TM_HOSTSYNC)) > 0) {
			gettimeofday(&mytb, (struct timezone *) NULL);

			pvm_upkint(&cc, 1, 1);
			if (cc >= 0) {
				cc = 0;
				pvm_upkint(i, 2, 1);
				remt.tv_sec = i[0];
				remt.tv_usec = i[1];

				if (clk)
					*clk = remt;

				if (delta) {
					TVDIVN(&myta, &myta, 2);
					TVDIVN(&mytb, &mytb, 2);
					TVXADDY(&myta, &myta, &mytb);
					TVXSUBY(&myta, &myta, &remt);
					*delta = myta;
				}
			}
		}
	}

	if (cc < 0)
		lpvmerr("pvm_host_sync", cc);
	return cc;
}


/*	pvm_gettmask()
*
*	Get our (or child) trace mask.
*/

int
pvm_gettmask(who, tmask)
	int who;
	Pvmtmask tmask;
{
	int i;
	char *tm = 0;

	if (who == PvmTaskChild)
		tm = pvmctrcmask;
	else if (who == PvmTaskSelf)
		tm = pvmtrcmask;

	if (!tm)
		return lpvmerr("pvm_gettmask", PvmBadParam);
	BCOPY(tm, tmask, TEV_MASK_LENGTH);
	return PvmOk;
}


/*	pvm_settmask()
*
*	Set our (or child) trace mask.
*/

int
pvm_settmask(who, tmask)
	int who;
	Pvmtmask tmask;
{
	int i;
	char *tm = 0;

	if (who == PvmTaskChild)
		tm = pvmctrcmask;
	else if (who == PvmTaskSelf)
		tm = pvmtrcmask;

	if (!tm)
		return lpvmerr("pvm_settmask", PvmBadParam);

	BCOPY(tmask, tm, TEV_MASK_LENGTH);
	return PvmOk;
}


