
/*
 *         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.
 */

/*
 *	lpvm.c
 *
 *	Libpvm core for unix environment.
 *
$Log: lpvm.c,v $
 * Revision 1.13  1994/10/15  19:07:31  manchek
 * don't use fd_sets when select returns -1; will hang reading.
 * cast message tags for comparison as integers
 *
 * Revision 1.12  1994/09/02  15:23:37  manchek
 * fixed segfaults in setopt when task not connected
 *
 * Revision 1.11  1994/07/18  19:20:18  manchek
 * fix to call write() with max 4096 length for RS6K
 *
 * Revision 1.10  1994/06/21  19:36:23  manchek
 * forgot to ifdef tt_spath
 *
 * Revision 1.9  1994/06/21  18:31:31  manchek
 * connect in mksocs() tries several times before giving up.
 * don't setsockopt() at TCP level on Unix task-task sockets
 *
 * Revision 1.8  1994/06/04  21:44:57  manchek
 * added unix domain sockets
 *
 * Revision 1.7  1994/06/03  20:38:15  manchek
 * version 3.3.0
 *
 * Revision 1.6  1993/11/30  23:50:01  manchek
 * set nodelay and snd, rcv buffer sizes on t-t sockets
 *
 * Revision 1.5  1993/11/30  15:51:30  manchek
 * beatask complains if it can't write d-auth file (fs full?)
 *
 * Revision 1.4  1993/10/04  20:29:05  manchek
 * mksocks() and pvm_start_pvmd() now use pvmdsockfile(), not TDSOCKNAME.
 * made pvm_useruid global for pvmcruft.c
 *
 * Revision 1.3  1993/10/04  19:10:22  manchek
 * mctl() conflicts with mctl(2) - declare static for now
 *
 * Revision 1.2  1993/09/16  21:36:20  manchek
 * pvm_start_pvmd() was freeing the return string from pvmgetpvmd()
 *
 * 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>
#include <pvm3.h>
#include <sys/socket.h>
#ifndef NOUNIXDOM
#include <sys/un.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#if defined(IMA_RS6K) || defined(IMA_POWER4)
#include <sys/select.h>
#endif
#include <sys/stat.h>
#include <fcntl.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 <errno.h>
#include <signal.h>
#include "global.h"
#include "tdpro.h"
#include "ddpro.h"
#include "pvmalloc.h"
#include "pvmfrag.h"
#include "pvmumbuf.h"
#include "listmac.h"
#include "tvdefs.h"
#include "bfunc.h"
#include <pvmtev.h>
#include "tevmac.h"

#ifndef	max
#define	max(a,b)	((a)>(b)?(a):(b))
#endif

#ifndef	min
#define	min(a,b)	((a)<(b)?(a):(b))
#endif

/* task debug mask */

#define	TDMPACKET	1		/* packet tracing */
#define	TDMMESSAGE	2		/* message tracing */
#define	TDMSELECT	4		/* select and fd sets */
#define	TDMROUTE	8		/* message route control */

/*
*	task-task or task-pvmd control block
*/

struct ttpcb {
	struct ttpcb *tt_link;			/* dll of peers */
	struct ttpcb *tt_rlink;
	int tt_tid;						/* tid of peer or 0 if master of list */
	int tt_state;
	int tt_fd;						/* fd of connection or -1 */
	struct sockaddr_in tt_sad;		/* address of our socket */
	struct sockaddr_in tt_osad;		/* (authd) address of peer socket */
	struct umbuf *tt_rxfrag;		/* not-assembled incm msg */
	struct frag *tt_rxf;			/* partial incm frag */
#ifndef NOUNIXDOM
	char *tt_spath;					/* socket path */
#endif
};

/* ttpcb states */
#define	TTCONWAIT	1			/* requested, waiting for reply to connect */
#define	TTGRNWAIT	2			/* granted, waiting for connect */
#define	TTOPEN		3			/* connection running */
#define	TTDENY		4			/* don't attempt connection */
#define	TTDEAD		5			/* task dead; pcb should be cleared */

#ifndef	TTSOCKBUF
#define	TTSOCKBUF	0x8000
#endif

#ifdef	NOTMPNAM
#define	TMPNAMFUN(x)	pvmtmpnam(x)
#define	LEN_OF_TMP_NAM	32
char *pvmtmpnam();

#else	/*NOTMPNAM*/
#define	TMPNAMFUN(x)	tmpnam(x)
#ifdef	L_tmpnam
#define	LEN_OF_TMP_NAM	L_tmpnam
#else
#define	LEN_OF_TMP_NAM	64
#endif
#endif	/*NOTMPNAM*/

char *getenv();

extern struct encvec *enctovec();
extern char *inadport_decimal();
extern char *inadport_hex();
char *pvmgetpvmd();
char *pvmdsockfile();


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

extern int errno;					/* from libc */
extern char *sys_errlist[];
extern int sys_nerr;

extern int pvmrbufmid;				/* from pack.c */
extern int pvmsbufmid;				/* from pack.c */

char *pvm_errlist[] = {					/* error messages for -pvm_errno */
		"Error 0",
		"Error 1",
	"Bad parameter",
	"Count mismatch",
		"Error 4",				/* not used */
	"End of buffer",
	"No such host",
	"No such file",
		"Error 8",				/* not used */
		"Error 9",				/* not used */
	"Malloc failed",
		"Error 11",				/* not used */
	"Can't decode message",
		"Error 13",				/* not used */
	"Can't contact local daemon",
	"No current buffer",
	"No such buffer",
	"Null group name",
	"Already in group",
	"No such group",
	"Not in group",
	"No such instance",
	"Host failed",
	"No parent task",
	"Not implemented",
	"Pvmd system error",
	"Version mismatch",
	"Out of resources",
	"Duplicate host",
	"Can't start pvmd",
	"Already in progress",
	"No such task",
	"No such entry",
	"Duplicate entry",
};

int pvmautoerr = 1;						/* whether to auto print err msgs */
int pvmcouttid = 0;						/* child stdout dst and code */
int pvmcoutcod = 0;
int pvmctrctid = 0;						/* child trace dst and code */
int pvmctrccod = 0;
int pvmfrgsiz = UDPMAXLEN;				/* message frag length (to pack) */
int pvmmyptid = -1;						/* parent task id */
int pvmmytid = -1;						/* this task id */
int pvmmyupid = -1;						/* process unix pid */
int pvmudpmtu = UDPMAXLEN;				/* local UDP MTU */
int pvm_errno = 0;						/* last libpvm error code */
int pvmmyndf = 0;						/* host native data enc, init XDR */
int pvm_nerr = sizeof(pvm_errlist)
			/sizeof(pvm_errlist[0]);	/* exported num of errors */
int pvmschedtid = 0;					/* scheduler task */
int pvmrescode = 0;						/* allow use of reserved tids, codes */
struct umbuf *pvmrxlist = 0;			/* not-recvd msg list */
int pvmtidhmask = TIDHOST;				/* mask for host field of tids */
int pvmouttid = 0;						/* stdout dst and code */
int pvmoutcod = 0;
int pvmtoplvl = 1;						/* function called from outside lib */
int pvmtrctid = 0;						/* trace dst and code */
int pvmtrccod = 0;
Pvmtmask pvmtrcmask;					/* trace bitmask */
Pvmtmask pvmctrcmask;					/* child trace bitmask */
int pvm_useruid = -1;					/* user's unix uid */


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

static char rcsid[] = "$Id: lpvm.c,v 1.13 1994/10/15 19:07:31 manchek Exp $";
static int debugmask = 0;				/* which debugging info */
static int mxfersingle = 1;				/* mxfer returns after single frag */
static char pvmtxt[512];				/* scratch for error log */
static struct sockaddr_in pvmourinet;	/* our host ip addr */
static int pvmrouteopt = PvmAllowDirect;/* task-task routing style */
static struct ttpcb *ttlist = 0;		/* dll of connected tasks */
static struct ttpcb *topvmd = 0;		/* default route (to pvmd) */
static int pvmnfds = 0;					/* 1 + highest bit set in fds */
static fd_set pvmrfds;					/* rd fdset for mxfer() */
static int pvmtrcmid = 0;				/* message buffer for trace */
static int pvmtrcsbf = 0;				/* existing other buffer during trace */


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

/*	pvmbailout()
*
*	Called by low-level stuff in f.e. pvmfrag.c.  Don't really want to
*	bail in libpvm.
*/

void
pvmbailout(n)
	int n;
{
	n = n;	/* ayn rand was here */
}


/*	pvmlogerror()
*
*	Log a libpvm error message.  Prepends a string identifying the task.
*/

pvmlogerror(s)
	char *s;
{
	if (pvmmytid == -1)
		fprintf(stderr, "libpvm [pid%d]: %s", pvmmyupid, s);
	else
		fprintf(stderr, "libpvm [t%x]: %s", pvmmytid, s);
}


/*	pvmlogperror()
*
*	Log a libpvm error message.  Prepends a string identifying the
*	task and appends the system error string for _errno.
*/

pvmlogperror(s)
	char *s;
{
	char *em;

	em = ((errno >= 0 && errno < sys_nerr)
		? sys_errlist[errno] : "Unknown Error");
	if (pvmmytid == -1)
		fprintf(stderr, "libpvm [pid%d]: %s: %s\n", pvmmyupid, s, em);
	else
		fprintf(stderr, "libpvm [t%x]: %s: %s\n", pvmmytid, s, em);
}


pvm_fd_add(fd, sets)
	int fd;				/* the fd */
	int sets;			/* which sets */
{
#ifdef	SANITY
	if (fd < 0 || fd >= FD_SETSIZE) {
		sprintf(pvmtxt, "pvm_fd_add() bad fd %d\n", fd);
		pvmlogerror(pvmtxt);
		return 1;
	}
#endif
	if (sets & 1)
		FD_SET(fd, &pvmrfds);
/*
	if (sets & 2)
		FD_SET(fd, &pvmwfds);
	if (sets & 4)
		FD_SET(fd, &pvmefds);
*/

	/* if this is new highest, adjust nfds */

	if (fd >= pvmnfds)
		pvmnfds = fd + 1;
	return 0;
}


pvm_fd_delete(fd, sets)
	int fd;				/* the fd */
	int sets;			/* which sets */
{
#ifdef	SANITY
	if (fd < 0 || fd >= FD_SETSIZE) {
		sprintf(pvmtxt, "pvm_fd_delete() bad fd %d\n", fd);
		pvmlogerror(pvmtxt);
		return 1;
	}
#endif
	if (sets & 1)
		FD_CLR(fd, &pvmrfds);
/*
	if (sets & 2)
		FD_CLR(fd, &pvmwfds);
	if (sets & 4)
		FD_CLR(fd, &pvmefds);
*/

	/* if this was highest, may have to adjust nfds to new highest */

	if (fd + 1 == pvmnfds)
		while (pvmnfds > 0) {
			pvmnfds--;
			if (FD_ISSET(pvmnfds, &pvmrfds)
/*
			|| FD_ISSET(pvmnfds, &pvmefds)
			|| FD_ISSET(pvmnfds, &pvmwfds)
*/
			) {
				pvmnfds++;
				break;
			}
		}
	return 0;
}


print_fdset(pad, n, f)
	char *pad;		/* label at head */
	int n;			/* max fd + 1 */
	fd_set *f;		/* fd set */
{
	char *p = pvmtxt;
	int i;
	char *s = "";

	strcpy(p, pad);
	p += strlen(p);
	for (i = 0; i < n; i++)
		if (FD_ISSET(i, f)) {
			sprintf(p, "%s%d", s, i);
			p += strlen(p);
			s = ",";
		}
	strcat(p, "\n");
	pvmlogerror(pvmtxt);
	return 0;
}


/*	ttpcb_new()
*
*	Create a new, blank ttpcb.
*/

struct ttpcb *
ttpcb_new()
{
	struct ttpcb *pcbp;

	if (pcbp = TALLOC(1, struct ttpcb, "tpcb")) {
		BZERO((char*)pcbp, sizeof(struct ttpcb));
		pcbp->tt_fd = -1;
		pcbp->tt_rxfrag = TALLOC(1, struct umbuf, "umb");
		BZERO((char*)pcbp->tt_rxfrag, sizeof(struct umbuf));
		pcbp->tt_rxfrag->ub_link = pcbp->tt_rxfrag->ub_rlink = pcbp->tt_rxfrag;
	}
	return pcbp;
}


/*	ttpcb_delete()
*
*	Get rid of a ttpcb.  Delete it from any list, close the socket,
*	free any rx frag heap.
*/

void
ttpcb_delete(pcbp)
	struct ttpcb *pcbp;
{
	struct umbuf *up;

	if (pcbp->tt_link) {
		LISTDELETE(pcbp, tt_link, tt_rlink);
	}
	if (pcbp->tt_fd != -1) {
		pvm_fd_delete(pcbp->tt_fd, 3);
		(void)close(pcbp->tt_fd);
	}
	if (up = pcbp->tt_rxfrag) {
		while (up->ub_link != up)
			umbuf_free(up->ub_link->ub_mid);
		PVM_FREE(up);
	}
	if (pcbp->tt_rxf)
		fr_unref(pcbp->tt_rxf);
#ifndef NOUNIXDOM
	if (pcbp->tt_spath)
		(void)unlink(pcbp->tt_spath);
#endif

	PVM_FREE(pcbp);
}


/*	ttpcb_creat()
*
*	Create a new task-task entry.  Allocates a ttpcb, inserts it in
*	ttlist.
*/

struct ttpcb *
ttpcb_creat(tid)
	int tid;		/* peer tid */
{
	struct ttpcb *pcbp, *pcbp2;

	if (pcbp = ttpcb_new()) {
		pcbp->tt_tid = tid;

		for (pcbp2 = ttlist->tt_link; pcbp2 != ttlist; pcbp2 = pcbp2->tt_link)
			if (pcbp2->tt_tid > tid)
				break;
		LISTPUTBEFORE(pcbp2, pcbp, tt_link, tt_rlink);
	}
	return pcbp;
}


/*	ttpcb_find()
*
*	Find a task-task entry by tid in ttlist.
*/

struct ttpcb *
ttpcb_find(tid)
	int tid;		/* peer tid */
{
	struct ttpcb *pcbp;

	for (pcbp = ttlist->tt_link; pcbp != ttlist; pcbp = pcbp->tt_link)
		if (pcbp->tt_tid >= tid)
			break;
	return (pcbp->tt_tid == tid) ? pcbp : (struct ttpcb*)0;
}


/*	ttpcb_dead()
*
*	Mark ttpcb dead, close socket, remove it from fd sets.
*/

void
ttpcb_dead(pcbp)
	struct ttpcb *pcbp;
{
	struct umbuf *up;

	pcbp->tt_state = TTDEAD;
	if (pcbp->tt_fd != -1) {
		pvm_fd_delete(pcbp->tt_fd, 3);
		(void)close(pcbp->tt_fd);
		pcbp->tt_fd = -1;
	}
#ifndef NOUNIXDOM
	if (pcbp->tt_spath) {
		(void)unlink(pcbp->tt_spath);
		pcbp->tt_spath = 0;
	}
#endif
	if (pcbp->tt_rxf) {
		fr_unref(pcbp->tt_rxf);
		pcbp->tt_rxf = 0;
	}
	if (up = pcbp->tt_rxfrag) {
		while (up->ub_link != up)
			umbuf_free(up->ub_link->ub_mid);
	}
}


int
ttpcb_dump(pcbp)
	struct ttpcb *pcbp;
{
	sprintf(pvmtxt, "ttpcb_dump() t%x fd=%d sad=%s",
		pcbp->tt_tid,
		pcbp->tt_fd,
		inadport_decimal(&pcbp->tt_sad));
	sprintf(pvmtxt + strlen(pvmtxt), " osad=%s state=%d\n",
		inadport_decimal(&pcbp->tt_osad), pcbp->tt_state);
	pvmlogerror(pvmtxt);
	return 0;
}


int
ttpcb_dumpall()
{
	struct ttpcb *pcbp;

	pvmlogerror("ttpcb_dumpall()\n");
	ttpcb_dump(topvmd);
	for (pcbp = ttlist->tt_link; pcbp != ttlist; pcbp = pcbp->tt_link)
		ttpcb_dump(pcbp);
	return 0;
}


/*	mroute()
*
*	Route a message to a destination.
*	Returns when
*		outgoing message (if any) fully sent and
*		(timed out (tmout) or
*			at least one message fully received)
*	Returns >=0 the number of complete messages downloaded, or
*	negative on error.
*/

int
mroute(mid, dtid, code, tmout)
	int mid;				/* message (or 0) */
	int dtid;				/* dest */
	int code;				/* type code */
	struct timeval *tmout;	/* get at least one message */
{
	struct ttpcb *pcbp;			/* pcb for foreign task */
	int s;						/* socket */
	int sbf;					/* temp TC_CONREQ message */
	int sbf2;					/* temp TM_NOTIFY message */
	struct umbuf *up;
	struct sockaddr_in sad;
	int l;
	int cc;
	int gotem = 0;
	static struct timeval ztv = { 0, 0 };
	char *addr = 0;
#ifndef NOUNIXDOM
	struct sockaddr_un uns;
	char spath[LEN_OF_TMP_NAM];
#endif

	if (up = midtobuf(mid)) {
		up->ub_dst = dtid;
		up->ub_cod = code;

		if (pvmrouteopt == PvmRouteDirect
		&& dtid && TIDISTASK(dtid) && dtid != pvmmytid
		&& !ttpcb_find(dtid)) {

#ifndef NOUNIXDOM
			if ((pvmmytid & pvmtidhmask) == (dtid & pvmtidhmask)) {
				if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
					pvmlogperror("mroute() socket");

				} else {
					(void)TMPNAMFUN(spath);
					BZERO((char*)&uns, sizeof(uns));
					uns.sun_family = AF_UNIX;
					spath[0] = 0;
					(void)tmpnam(spath);
					strcpy(uns.sun_path, spath);

					if (bind(s, (struct sockaddr*)&uns, sizeof(uns)) == -1) {
						pvmlogperror("mroute() bind");
						(void)close(s);
						s = -1;

					} else {
						pcbp = ttpcb_creat(dtid);
						pcbp->tt_fd = s;
						addr = pcbp->tt_spath = STRALLOC(spath);
						pcbp->tt_state = TTCONWAIT;
					}
				}

			} else {
#endif /*NOUNIXDOM*/

				if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
					pvmlogperror("mroute() socket");

				} else {
					sad = pvmourinet;
					l = sizeof(sad);
					if (bind(s, (struct sockaddr*)&sad, sizeof(sad)) == -1) {
						pvmlogperror("mroute() bind");
						(void)close(s);
						s = -1;

					} else {
						if (getsockname(s, (struct sockaddr*)&sad, &l) == -1) {
							pvmlogperror("mroute() getsockname");
							(void)close(s);
							s = -1;

						} else {
#ifndef NOSOCKOPT
							l = 1;
							if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
									(char*)&l, sizeof(int)) == -1)
								pvmlogperror("mroute() setsockopt");
							l = TTSOCKBUF;
							if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
									(char*)&l, sizeof(l)) == -1
							|| setsockopt(s, SOL_SOCKET, SO_RCVBUF,
									(char*)&l, sizeof(l)) == -1)
								pvmlogperror("mroute() setsockopt SO_SNDBUF");
#endif
							pcbp = ttpcb_creat(dtid);
							pcbp->tt_fd = s;
							pcbp->tt_sad = sad;
							pcbp->tt_state = TTCONWAIT;
							addr = inadport_hex(&sad);
						}
					}
				}
#ifndef NOUNIXDOM
			}
#endif

			if (s >= 0) {
				sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
				l = TDPROTOCOL;
				pvm_pkint(&l, 1, 1);
				pvm_pkstr(addr);
				sbf = pvm_setsbuf(sbf);
				up = midtobuf(sbf);
				up->ub_dst = dtid;
				up->ub_cod = TC_CONREQ;

				sbf2 = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
				l = PvmTaskExit;
				pvm_pkint(&l, 1, 1);
				l = TC_TASKEXIT;
				pvm_pkint(&l, 1, 1);
				l = 1;
				pvm_pkint(&l, 1, 1);
				pvm_pkint(&dtid, 1, 1);
				sbf2 = pvm_setsbuf(sbf2);
				up = midtobuf(sbf2);
				up->ub_dst = TIDPVMD;
				up->ub_cod = TM_NOTIFY;

				if (debugmask & TDMROUTE) {
					sprintf(pvmtxt, "mroute() CONREQ to t%x\n", dtid);
					pvmlogerror(pvmtxt);
				}
/* XXX it would be better if mxfer() took a list of send messages */
				do {
					if ((cc = mxfer(sbf,
							(sbf2 ? &ztv : (struct timeval *)0))) < 0)
						break;		/* XXX hmm */
					gotem += cc;
					if (sbf) {
						pvm_freebuf(sbf);
						if (sbf2) {
							sbf = sbf2;
							sbf2 = 0;
						} else
							sbf = 0;
					}
				} while (pcbp->tt_state == TTCONWAIT);
			}
		}
	}

	cc = mxfer(mid, tmout);
	pcbp = ttlist->tt_link;
	while (pcbp != ttlist)
		if (pcbp->tt_state == TTDEAD) {
			if (debugmask & TDMROUTE) {
				sprintf(pvmtxt, "mroute() freeing pcb t%x\n", pcbp->tt_tid);
				pvmlogerror(pvmtxt);
			}
			pcbp = pcbp->tt_link;
			ttpcb_delete(pcbp->tt_rlink);
		} else
			pcbp = pcbp->tt_link;
	return (cc < 0 ? cc : gotem + cc);
}


/*	pvm_tc_conreq()
*
*	Another task requests a connection with us.  Reply with CONACK
*	message.
*
*	Returns mid of reply.
*
*	TC_CONREQ() {
*		int tdprotocol		// t-d protocol revision number
*		string sockaddr		// address of other socket
*	}
*/

static int
pvm_tc_conreq(up)
	struct umbuf *up;
{
	int sbf = 0;				/* reply message mid */
	int ttpro;					/* protocol revision */
	struct ttpcb *pcbp;			/* pcb for connection */
	int ackd;					/* allow connection (0) */
	char *addr;					/* socket address */
	static int linger[2] = { 1, 60 };   /* XXX arbitrary time */
	int i;
	struct umbuf *up2;
#ifndef NOUNIXDOM
	struct sockaddr_un uns;
	char spath[LEN_OF_TMP_NAM];
#endif
	char buf[256];

	pvm_upkint(&ttpro, 1, 1);
	pvm_upkstr(buf);

	if (pcbp = ttpcb_find(up->ub_src)) {
		if (debugmask & TDMROUTE) {
			sprintf(pvmtxt, "pvmmctl() crossed CONREQ from t%x\n",
					up->ub_src);
			pvmlogerror(pvmtxt);
		}
		if (pcbp->tt_state == TTCONWAIT) {
			if (buf[0] == '/') {
#ifdef NOUNIXDOM
				sprintf(pvmtxt,
				"pvmmctl() CONREQ from t%x, Unix domain socket unsupported\n",
						up->ub_src);
				pvmlogerror(pvmtxt);

#else /*NOUNIXDOM*/
				BZERO((char*)&uns, sizeof(uns));
				uns.sun_family = AF_UNIX;
				strcpy(uns.sun_path, buf);
				if (connect(pcbp->tt_fd, (struct sockaddr*)&uns,
						sizeof(uns)) == -1) {
					pvmlogperror("pvmmctl() connect");

				} else {
					pcbp->tt_state = TTOPEN;
				}
#endif /*NOUNIXDOM*/

			} else {
				pcbp->tt_osad.sin_family = AF_INET;
				hex_inadport(buf, &pcbp->tt_osad);
				if (connect(pcbp->tt_fd, (struct sockaddr*)&pcbp->tt_osad,
						sizeof(pcbp->tt_osad)) == -1) {
					pvmlogperror("pvmmctl() connect");

				} else {
					pcbp->tt_state = TTOPEN;
#ifndef NOSOCKOPT
					if (setsockopt(pcbp->tt_fd, SOL_SOCKET, SO_LINGER,
							(char*)linger, sizeof(linger)) == -1)
						pvmlogperror("pvmmctl() setsockopt");
#endif /*NOSOCKOPT*/
				}

			}
			if (pcbp->tt_state == TTOPEN) {
				if ((i = fcntl(pcbp->tt_fd, F_GETFL, 0)) == -1)
					pvmlogperror("pvmmctl() fcntl");
				else {
#ifdef O_NDELAY
					i |= O_NDELAY;
#else
					i |= FNDELAY;
#endif
					(void)fcntl(pcbp->tt_fd, F_SETFL, i);
				}
				pvm_fd_add(pcbp->tt_fd, 1);
			}

		} else {
			sprintf(pvmtxt, "pvmmctl() CONREQ from t%x but state=%d ?\n",
					up->ub_src, pcbp->tt_state);
			pvmlogerror(pvmtxt);
		}

	} else {
		if (debugmask & TDMROUTE) {
			sprintf(pvmtxt, "pvmmctl() CONREQ from t%x\n", up->ub_src);
			pvmlogerror(pvmtxt);
		}
		ackd = 1;
		pcbp = ttpcb_creat(up->ub_src);
		if (pvmrouteopt != PvmDontRoute) {
			if (buf[0] == '/') {
#ifdef NOUNIXDOM
				sprintf(pvmtxt,
				"pvmmctl() CONREQ from t%x, Unix domain socket unsupported\n",
						up->ub_src);
				pvmlogerror(pvmtxt);

#else /*NOUNIXDOM*/
				if ((pcbp->tt_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
					pvmlogperror("pvmmctl() socket");

				} else {
					(void)TMPNAMFUN(spath);
					BZERO((char*)&uns, sizeof(uns));
					uns.sun_family = AF_UNIX;
					spath[0] = 0;
					(void)tmpnam(spath);
					strcpy(uns.sun_path, spath);

					if (bind(pcbp->tt_fd, (struct sockaddr*)&uns, sizeof(uns))
					== -1) {
						pvmlogperror("pvmmctl() bind");

					} else {
						if (listen(pcbp->tt_fd, 1) == -1)
							pvmlogperror("pvmmctl() listen");

						else {
							pcbp->tt_state = TTGRNWAIT;
							pvm_fd_add(pcbp->tt_fd, 1);
							ackd = 0;
							addr = pcbp->tt_spath = STRALLOC(spath);
						}
					}
				}
#endif /*NOUNIXDOM*/

			} else {
				if ((pcbp->tt_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
					pvmlogperror("pvmmctl() socket");

				} else {
					pcbp->tt_sad = pvmourinet;
					i = sizeof(pcbp->tt_sad);
					if (bind(pcbp->tt_fd, (struct sockaddr*)&pcbp->tt_sad, i)
					== -1) {
						pvmlogperror("pvmmctl() bind");

					} else {
						if (getsockname(pcbp->tt_fd,
								(struct sockaddr*)&pcbp->tt_sad, &i) == -1) {
							pvmlogperror("pvmmctl() getsockname");

						} else {
							if (listen(pcbp->tt_fd, 1) == -1)
								pvmlogperror("pvmmctl() listen");

							else {
								hex_inadport(buf, &pcbp->tt_osad);
								pcbp->tt_state = TTGRNWAIT;
								pvm_fd_add(pcbp->tt_fd, 1);
								ackd = 0;
								addr = inadport_hex(&pcbp->tt_sad);
							}
						}
					}
				}
			}
		}

		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
		ttpro = TDPROTOCOL;
		pvm_pkint(&ttpro, 1, 1);
		pvm_pkint(&ackd, 1, 1);
		pvm_pkstr(addr);
		sbf = pvm_setsbuf(sbf);
		up2 = midtobuf(sbf);
		up2->ub_dst = up->ub_src;
		up2->ub_cod = TC_CONACK;

		if (ackd)
			ttpcb_delete(pcbp);
	}
	return sbf;
}


/*	pvm_tc_conack()
*
*	Another task replies to our connection request.
*
*	TC_CONACK() {
*		int tdprotocol		// t-d protocol revision number
*		int ack				// 0 ok, 1 denied
*		string sockaddr		// address of other socket
*	}
*/

static int
pvm_tc_conack(up)
	struct umbuf *up;
{
	int ttpro;					/* protocol revision */
	int ackd;					/* connection allowed (0) */
	struct ttpcb *pcbp;			/* pcb for connection */
	static int linger[2] = { 1, 60 };   /* XXX arbitrary time */
	int i;
#ifndef NOUNIXDOM
	struct sockaddr_un uns;
#endif
	char buf[256];

	pvm_upkint(&ttpro, 1, 1);
	pvm_upkint(&ackd, 1, 1);
	pvm_upkstr(buf);

	if (pcbp = ttpcb_find(up->ub_src)) {
		if (pcbp->tt_state == TTCONWAIT) {
			if (debugmask & TDMROUTE) {
				sprintf(pvmtxt, "pvmmctl() CONACK from t%x\n", up->ub_src);
				pvmlogerror(pvmtxt);
			}
			if (ttpro != TDPROTOCOL) {
				sprintf(pvmtxt,
						"pvmmctl() t-t protocol mismatch with t%x\n",
						pcbp->tt_tid);
				pvmlogerror(pvmtxt);
				ackd = 1;

			} else {
				if (ackd != 0) {
					if (debugmask & TDMROUTE) {
						sprintf(pvmtxt, "pvmmctl() route to t%x denied\n",
								pcbp->tt_tid);
						pvmlogerror(pvmtxt);
					}

				} else {
					if (buf[0] == '/') {
#ifdef NOUNIXDOM
						sprintf(pvmtxt,
						"pvmmctl() CONREQ from t%x, Unix domain socket unsupported\n",
								up->ub_src);
						pvmlogerror(pvmtxt);
						ackd = 1;
#else /*NOUNIXDOM*/
						BZERO((char*)&uns, sizeof(uns));
						uns.sun_family = AF_UNIX;
						strcpy(uns.sun_path, buf);
						if (connect(pcbp->tt_fd, (struct sockaddr*)&uns,
								sizeof(uns)) == -1) {
							pvmlogperror("pvmmctl() connect");
							ackd = 1;

						} else {
							pcbp->tt_state = TTOPEN;
							if ((i = fcntl(pcbp->tt_fd, F_GETFL, 0)) == -1)
								pvmlogperror("pvmmctl() fcntl");
							else {
#ifdef O_NDELAY
								i |= O_NDELAY;
#else
								i |= FNDELAY;
#endif
								(void)fcntl(pcbp->tt_fd, F_SETFL, i);
							}
							pvm_fd_add(pcbp->tt_fd, 1);
						}

#endif /*NOUNIXDOM*/

					} else {
						pcbp->tt_osad.sin_family = AF_INET;
						hex_inadport(buf, &pcbp->tt_osad);
						if (connect(pcbp->tt_fd,
								(struct sockaddr*)&pcbp->tt_osad,
								sizeof(pcbp->tt_osad)) == -1) {
							pvmlogperror("pvmmctl() connect");
							ackd = 1;

						} else {
							pcbp->tt_state = TTOPEN;
#ifndef NOSOCKOPT
							if (setsockopt(pcbp->tt_fd, SOL_SOCKET, SO_LINGER,
									(char*)linger, sizeof(linger)) == -1)
								pvmlogperror("pvmmctl() setsockopt");
#endif /*NOSOCKOPT*/
							if ((i = fcntl(pcbp->tt_fd, F_GETFL, 0)) == -1)
								pvmlogperror("pvmmctl() fcntl");
							else {
#ifdef O_NDELAY
								i |= O_NDELAY;
#else
								i |= FNDELAY;
#endif
								(void)fcntl(pcbp->tt_fd, F_SETFL, i);
							}
							pvm_fd_add(pcbp->tt_fd, 1);
						}
					}
				}
			}

			if (ackd != 0) {
				pcbp->tt_state = TTDENY;
				(void)close(pcbp->tt_fd);
				pcbp->tt_fd = -1;
			}

		} else {
			sprintf(pvmtxt, "pvmmctl() CONACK from t%x but state=%d\n",
					up->ub_src, pcbp->tt_state);
			pvmlogerror(pvmtxt);
		}

	} else {
		sprintf(pvmtxt, "pvmmctl() suprious CONACK from t%x\n",
				up->ub_src);
		pvmlogerror(pvmtxt);
	}
	return 0;
}


/*	pvmmctl()
*
*	Entry points for libpvm control messages.
*
*	Called by mxinput() when a control message
*	(code between TC_FIRST and TC_LAST) is received.
*/

static int
pvmmctl(up)
	struct umbuf *up;
{
	int rbf;				/* temp rx message storage */
	int sbf = 0;			/* return message to send */
	struct ttpcb *pcbp;
	int tid;
	char buf[256];

	rbf = pvm_setrbuf(up->ub_mid);

	switch (up->ub_cod) {

	/*
	* do the messy ones in separate functions
	*/

	case TC_CONREQ:
		sbf = pvm_tc_conreq(up);
		break;

	case TC_CONACK:
		sbf = pvm_tc_conack(up);
		break;

	/*
	* we are notified that another task (to which we have a direct route)
	* has exited
	*/

	case TC_TASKEXIT:
		pvm_upkint(&tid, 1, 1);

		if (debugmask & TDMROUTE) {
			sprintf(pvmtxt, "pvmmctl() TASKEXIT for t%x\n", tid);
			pvmlogerror(pvmtxt);
		}

		/*
		* v3.3: for now, don't close fully open route as we may lose
		* messages.  rely on socket getting EOF
		*/
		if ((pcbp = ttpcb_find(tid))
		&& pcbp->tt_state != TTOPEN)
			ttpcb_dead(pcbp);
		break;

	/*
	* we get output from a child* task (because we called pvm_catchout())
	*
	*	TC_OUTPUT() {
	*		int tid				// of source task
	*		int length			// of output
	*		char output[length]	// characters printed
	*	}
	*/

	case TC_OUTPUT:
		pvmclaimo();
		break;

	/*
	* just like the name
	*/

	case TC_NOOP:
		if (debugmask) {
			sprintf(pvmtxt, "pvmmctl() NOOP from t%x\n", up->ub_src);
			pvmlogerror(pvmtxt);
		}
		break;

	/*
	* set our trace mask on the fly
	*
	*	TC_SETTMASK() {
	*		string mask			// new trace mask
	*	}
	*/

	case TC_SETTMASK:
		pvm_upkstr(buf);
		if (strlen(buf) + 1 == TEV_MASK_LENGTH)
			BCOPY(buf, pvmtrcmask, TEV_MASK_LENGTH);
		else
			pvmlogerror("pvmmctl() bogus trace mask\n");
		break;

	default:
		sprintf(pvmtxt, "pvmmctl() from t%x code=%d ?\n",
				up->ub_src, up->ub_cod);
		pvmlogerror(pvmtxt);
		break;
	}

	pvm_setrbuf(rbf);
	umbuf_free(up->ub_mid);
	return sbf;
}


/*	mxinput()
*
*	Input from a connection.
*	Reassemble packets and messages and add them to pvmrxlist.
*	Returns >= 0 the number of messages added to pvmrxlist,
*	or negative on error.
*/

int
mxinput(pcbp)
	struct ttpcb *pcbp;

{
	int gotem = 0;			/* num msgs added to pvmrxlist */
	struct frag *fp;		/* shadows pcbp->tt_rxf */
	struct frag *fp2;
	int n;					/* bytes received */
	int m;					/* length of fragment */
	struct umbuf *rxup;		/* message containing this frag */
	char *cp;				/* gp */
	int src;
	int ff;

	if (!pcbp->tt_rxf)
		pcbp->tt_rxf = fr_new(pvmfrgsiz);
	fp = pcbp->tt_rxf;

	/* read fragment header separately from body */

	n = (fp->fr_len < TDFRAGHDR) ? 0 : pvmget32(fp->fr_dat + 8);
	n += TDFRAGHDR - fp->fr_len;
	if (debugmask & TDMPACKET) {
		sprintf(pvmtxt, "mxinput() pcb t%x fr_len=%d fr_dat=+%d n=%d\n",
				pcbp->tt_tid, fp->fr_len, fp->fr_dat - fp->fr_buf, n);
		pvmlogerror(pvmtxt);
	}
	n = read(pcbp->tt_fd, fp->fr_dat + fp->fr_len, n);
	if (debugmask & TDMPACKET) {
		sprintf(pvmtxt, "mxinput() read=%d\n", n);
		pvmlogerror(pvmtxt);
	}

	/* deal with errors and closes */

	if (n == -1 && errno != EWOULDBLOCK && errno != EINTR) {
		if (debugmask & TDMPACKET) {
			sprintf(pvmtxt, "mxinput() t%x read", pcbp->tt_tid);
			pvmlogperror(pvmtxt);
		}
		return PvmSysErr;
	}
	if (!n) {
		if (debugmask & TDMPACKET) {
			sprintf(pvmtxt, "mxinput() t%x read EOF\n", pcbp->tt_tid);
			pvmlogerror(pvmtxt);
		}
		return -1;
	}

	if (n < 1)
		return gotem;
	if ((fp->fr_len += n) < TDFRAGHDR)
		return gotem;

	/* realloc buffer if frag won't fit */

	m = TDFRAGHDR + pvmget32(fp->fr_dat + 8);
	if (fp->fr_len == TDFRAGHDR) {
		if (m > fp->fr_max - (fp->fr_dat - fp->fr_buf)) {
			fp2 = fr_new(m);
			BCOPY(fp->fr_dat, fp2->fr_dat, TDFRAGHDR);
			fp2->fr_len = fp->fr_len;
			fr_unref(fp);
			fp = pcbp->tt_rxf = fp2;
			if (debugmask & TDMPACKET) {
				sprintf(pvmtxt, "mxinput() realloc frag max=%d\n", m);
				pvmlogerror(pvmtxt);
			}
		}
	}

	if (fp->fr_len == m) {
		pcbp->tt_rxf = 0;

		cp = fp->fr_dat;
/*
		dst = pvmget32(cp);		XXX might unpack and check
*/
		src = pvmget32(cp + 4);
		ff = pvmget8(cp + 12);
		fp->fr_len -= TDFRAGHDR;
		fp->fr_dat += TDFRAGHDR;

		if (debugmask & TDMPACKET) {
			sprintf(pvmtxt, "mxinput() pkt src t%x len %d ff %d\n",
					src, fp->fr_len, ff);
			pvmlogerror(pvmtxt);
		}

	/*
	* if start of message, make new umbuf, add to frag pile
	*/
		if (ff & FFSOM) {
			cp += TDFRAGHDR;
			fp->fr_len -= TTMSGHDR;
			fp->fr_dat += TTMSGHDR;
			rxup = midtobuf(umbuf_new());
			rxup->ub_cod = pvmget32(cp);
			rxup->ub_enc = pvmget32(cp + 4);
			rxup->ub_wid = pvmget32(cp + 8);
/*
			csum = pvmget32(cp + 12);
*/
			rxup->ub_src = src;
			LISTPUTBEFORE(pcbp->tt_rxfrag, rxup, ub_link, ub_rlink);

		} else {
			/* locate frag's message */

			for (rxup = pcbp->tt_rxfrag->ub_link; rxup != pcbp->tt_rxfrag;
					rxup = rxup->ub_link)
				if (rxup->ub_src == src)
					break;
		}

		if (rxup == pcbp->tt_rxfrag) {	/* uh oh, no message for it */
			pvmlogerror("mxinput() frag with no message\n");
			fr_unref(fp);

		} else {
			LISTPUTBEFORE(rxup->ub_frag, fp, fr_link, fr_rlink);
			rxup->ub_len += fp->fr_len;
	/*
	* if end of message, move to pvmrxlist and count it
	*/
			if (ff & FFEOM) {
				LISTDELETE(rxup, ub_link, ub_rlink);
				rxup->ub_codef = enctovec(rxup->ub_enc);
				LISTPUTBEFORE(pvmrxlist, rxup, ub_link, ub_rlink);
				gotem++;
				if (debugmask & TDMMESSAGE) {
					sprintf(pvmtxt,
							"mxinput() src t%x route t%x cod %d len %d\n",
							rxup->ub_src, pcbp->tt_tid,
							rxup->ub_cod, rxup->ub_len);
					pvmlogerror(pvmtxt);
				}
			}
		}
	}
	return gotem;
}


/*	mxfer()
*
*	Move message frags between task and pvmd or other tasks.
*	Returns when
*		Outgoing message (if any) fully sent and
*		(Timed out (tmout) or
*			At least one message fully received) and
*		No message from pvmd partially received.
*	Returns >=0 the number of complete messages downloaded, or
*	negative on error.
*/

int
mxfer(mid, tmout)
	int mid;					/* message */
	struct timeval *tmout;		/* get at least one message */
{
	struct umbuf *txup;			/* ogm */
	struct ttpcb *txpcbp = 0;	/* route for ogm */
	struct frag *txfp = 0;		/* cur ogm frag or null */
	struct timeval tin;
	struct timeval tnow;
	struct timeval *tvp;
	fd_set rfds, wfds;
	int wantmore = 1;			/* nothing rxd yet and not timed out */
	int gotem = 0;				/* count complete msgs downloaded */
	int ff;
	int n;
	char *txcp = 0;				/* point to remainder of txfp */
	int txtogo = 0;				/* len of remainder of txfp */
	int sbf;					/* message sent by pvmmctl() */
	struct umbuf *up;
	struct sockaddr_in sad;
	int s;
	struct ttpcb *pcbp;
	int tstk[100];				/* XXX aaigh! shouldn't be a stack */
	int tstkp = 0;
	char dummy[TDFRAGHDR+TTMSGHDR];	/* for inplace data */

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

	if (txup = midtobuf(mid)) {
		txfp = txup->ub_frag->fr_link;
		if (!txfp->fr_buf) {
			txfp = fr_new(MAXHDR);
			txfp->fr_dat += MAXHDR;
			LISTPUTBEFORE(txup->ub_frag, txfp, fr_link, fr_rlink);
		}
		if (!(txpcbp = ttpcb_find(txup->ub_dst)) || txpcbp->tt_state != TTOPEN)
			txpcbp = topvmd;
	}

	if (debugmask & TDMMESSAGE) {
		sprintf(pvmtxt, "mxfer() mid %d tmout ", mid);
		if (tmout)
			sprintf(pvmtxt + strlen(pvmtxt), "%d.%06d",
					tmout->tv_sec, tmout->tv_usec);
		else
			strcat(pvmtxt, "-");
		if (txup)
			sprintf(pvmtxt + strlen(pvmtxt), " dst t%x route t%x\n",
					txup->ub_dst, txpcbp->tt_tid);
		strcat(pvmtxt, "\n");
		pvmlogerror(pvmtxt);
	}

/* XXX we go an extra time through select because wantmore gets set
   XXX inside loop.  no big deal.  also, could avoid calling gettimeofday
   XXX if tmout is 0.0 */

	while (txfp || topvmd->tt_rxf || wantmore) {

		if (txfp || topvmd->tt_rxf) {			/* gotta block */
	/*
	* must block if sending any message, or receiving from pvmd
	*/
			tvp = 0;

		} else {
			if (wantmore) {
				if (tmout) {					/* finite time to block */
					gettimeofday(&tnow, (struct timezone*)0);
					TVXSUBY(&tnow, &tnow, &tin);
					if (TVXLTY(tmout, &tnow)) {	/* time's up */
						TVCLEAR(&tnow);
						wantmore = 0;

					} else {
						TVXSUBY(&tnow, tmout, &tnow);
					}
					tvp = &tnow;

				} else {						/* forever to block */
					tvp = 0;
				}

			} else {							/* return asap */
				TVCLEAR(&tnow);
				tvp = &tnow;
			}
		}

		rfds = pvmrfds;
		FD_ZERO(&wfds);
		if (txfp)
			FD_SET(txpcbp->tt_fd, &wfds);

		if (debugmask & TDMSELECT) {
			if (tvp)
				sprintf(pvmtxt, "mxfer() select timeout is %d.%06d\n",
						tvp->tv_sec, tvp->tv_usec);
			else
				sprintf(pvmtxt, "mxfer() select timeout is -\n");
			pvmlogerror(pvmtxt);
			print_fdset("mxfer() rfds=", pvmnfds, &rfds);
			print_fdset("mxfer() wfds=", pvmnfds, &wfds);
		}

		if ((n = select(pvmnfds, &rfds, &wfds, (fd_set*)0, tvp)) == -1
		&& errno != EINTR) {
			pvmlogperror("mxfer() select");
			return PvmSysErr;
		}
		if (debugmask & TDMSELECT) {
			sprintf(pvmtxt, "mxfer() select returns %d\n", n);
			pvmlogerror(pvmtxt);
		}
		if (n == -1)
			continue;

	/*
	* if pvmd conn has data ready, read packets
	*/

		if (FD_ISSET(topvmd->tt_fd, &rfds) && !(mxfersingle && gotem)) {
			up = pvmrxlist->ub_rlink;
			if ((n = mxinput(topvmd)) < 0) {
				if (n == -1)
					pvmlogerror("mxfer() EOF on pvmd sock\n");
				else
					pvmlogerror("mxfer() mxinput bad return on pvmd sock\n");
				return PvmSysErr;
			}

			if (gotem += n)
				wantmore = 0;

	/* pass any control messages to pvmmctl() */

			while ((up = up->ub_link) != pvmrxlist) {
				if (up->ub_cod >= (int)TC_FIRST && up->ub_cod <= (int)TC_LAST) {
					up = up->ub_rlink;
					if ((sbf = pvmmctl(up->ub_link)) > 0) {
						if (txfp)
							tstk[tstkp++] = sbf;
						else {
							txup = midtobuf(sbf);
							txfp = txup->ub_frag->fr_link;
							txfp = txfp->fr_buf ? txfp : 0;
							if (!(txpcbp = ttpcb_find(txup->ub_dst))
							|| txpcbp->tt_state != TTOPEN)
								txpcbp = topvmd;
						}
					}
				}
			}
		}

	/*
	* if task conn has data ready, read packets
	*/

		for (pcbp = ttlist->tt_link; pcbp != ttlist; pcbp = pcbp->tt_link) {
			if (pcbp->tt_state == TTOPEN && FD_ISSET(pcbp->tt_fd, &rfds)) {
				if ((n = mxinput(pcbp)) < 0)
					ttpcb_dead(pcbp);

				else {
					if (gotem += n)
						wantmore = 0;
				}

			} else
				if (pcbp->tt_state == TTGRNWAIT
				&& FD_ISSET(pcbp->tt_fd, &rfds)) {
					n = sizeof(sad);
					s = accept(pcbp->tt_fd, (struct sockaddr*)&sad, &n);
					if (s == -1) {
						pvmlogperror("mxfer() accept");
						ttpcb_dead(pcbp);

					} else {
#ifndef NOSOCKOPT
#ifndef NOUNIXDOM
						if (!pcbp->tt_spath) {	/* not unix sock */
#endif
							n = 1;
							if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
									(char*)&n, sizeof(int)) == -1)
								pvmlogperror("mxfer() setsockopt");
							n = TTSOCKBUF;
							if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
									(char*)&n, sizeof(int)) == -1
							|| setsockopt(s, SOL_SOCKET, SO_RCVBUF,
									(char*)&n, sizeof(n)) == -1)
								pvmlogperror("mxfer() setsockopt SO_SNDBUF");
#ifndef NOUNIXDOM
						}
#endif
#endif /*NOSOCKOPT*/
						if (debugmask & TDMROUTE) {
							sprintf(pvmtxt, "mxfer() accept from t%x\n",
									pcbp->tt_tid);
							pvmlogerror(pvmtxt);
						}
						(void)close(pcbp->tt_fd);
/*
						(void)dup2(s, pcbp->tt_fd);
						(void)close(s);
*/
						pvm_fd_delete(pcbp->tt_fd, 1);
						pcbp->tt_fd = s;
						pcbp->tt_state = TTOPEN;
						pvm_fd_add(s, 1);
					}
				}
		}

	/*
	* if sending and socket ready to write, send packets
	*/

		if (txfp && FD_ISSET(txpcbp->tt_fd, &wfds)) {
			if (!txtogo) {
				if (txfp->fr_u.dab)			/* packed data */
					txcp = txfp->fr_dat;
				else						/* inplace data */
					txcp = dummy + sizeof(dummy);
				txtogo = txfp->fr_len;

	/*
	* if this is first frag, prepend message header
	*/
				ff = 0;
				if (txfp->fr_rlink == txup->ub_frag) {
					txcp -= TTMSGHDR;
					txtogo += TTMSGHDR;
					pvmput32(txcp, txup->ub_cod);
					pvmput32(txcp + 4,
							(txup->ub_enc == 2 ? pvmmyndf : txup->ub_enc));
					pvmput32(txcp + 8, txup->ub_wid);
/*
					pvmput32(txcp + 12, csum);
*/
					ff = FFSOM;
				}
				if (txfp->fr_link == txup->ub_frag)
					ff |= FFEOM;
	/*
	* prepend frag header
	*/
				txcp -= TDFRAGHDR;
				pvmput32(txcp, txup->ub_dst);
				pvmput32(txcp + 4, pvmmytid);
				pvmput32(txcp + 8, txtogo);
				pvmput32(txcp + 12, 0);		/* to keep putrify happy */
				pvmput8(txcp + 12, ff);
				txtogo += TDFRAGHDR;
				if (!txfp->fr_u.dab)			/* inplace data */
					txtogo -= txfp->fr_len;
			}

			if (debugmask & TDMPACKET) {
				sprintf(pvmtxt, "mxfer() dst t%x n=%d\n",
						txup->ub_dst, txtogo);
				pvmlogerror(pvmtxt);
			}
#if defined(IMA_RS6K) || defined(IMA_POWER4)
			n = write(txpcbp->tt_fd, txcp, min(txtogo, 4096));
#else
			n = write(txpcbp->tt_fd, txcp, txtogo);
#endif
			if (debugmask & TDMPACKET) {
				sprintf(pvmtxt,
						"mxfer() wrote %d\n", n);
				pvmlogerror(pvmtxt);
			}
			if (n == -1 && errno != EWOULDBLOCK && errno != EINTR) {
				if (txpcbp == topvmd) {
					pvmlogperror("mxfer() write pvmd sock");
					return PvmSysErr;

				} else {
					pvmlogperror("mxfer() write tt sock");
	/* reset message and route to pvmd */
					ttpcb_dead(txpcbp);
					txpcbp = topvmd;
					txcp = 0;
					txtogo = 0;
					txfp = txup->ub_frag->fr_link;
				}
			}
			if (n > 0) {
				txcp += n;
				if ((txtogo -= n) <= 0) {
					if (txcp == dummy + sizeof(dummy)) {	/* inplace */
						txcp = txfp->fr_dat;
						txtogo = txfp->fr_len;

					} else {								/* packed */
						txcp = 0;
						txfp = txfp->fr_link;
						if (!txfp->fr_buf) {
							if (tstkp > 0) {
								txup = midtobuf(tstk[--tstkp]);
								txfp = txup->ub_frag->fr_link;
								txfp = txfp->fr_buf ? txfp : 0;
								if (!(txpcbp = ttpcb_find(txup->ub_dst))
								|| txpcbp->tt_state != TTOPEN)
									txpcbp = topvmd;
							} else
								txfp = 0;
						}
					}
				}
			}
		}

		if (debugmask & TDMMESSAGE) {
			sprintf(pvmtxt, "mxfer() txfp %d gotem %d tt_rxf %d\n",
					(txfp ? 1 : 0), gotem, (topvmd->tt_rxf ? 1 : 0));
			pvmlogerror(pvmtxt);
		}
	}

	return gotem;
}


/*	msendrecv()
*
*	Single op to send a system message (usually to our pvmd) and get
*	the reply.
*	Returns message handle or negative if error.
*/

int
msendrecv(other, code)
	int other;				/* dst, src tid */
	int code;				/* message code */
{
	int cc;
	struct umbuf *up;

	if (pvmsbufmid <= 0)
		return PvmNoBuf;

	/* send code to other */
	if (debugmask & TDMMESSAGE) {
		sprintf(pvmtxt, "msendrecv() to t%x code %d\n", other, code);
		pvmlogerror(pvmtxt);
	}
	if ((cc = mroute(pvmsbufmid, other, code, (struct timeval *)0)) < 0)
		return cc;

	/* recv code from other */
	for (up = pvmrxlist->ub_link; 1; up = up->ub_link) {
		if (up == pvmrxlist) {
			up = up->ub_rlink;
			if ((cc = mroute(0, 0, 0, (struct timeval *)0)) < 0)
				return cc;
			up = up->ub_link;
		}

		if (debugmask & TDMMESSAGE) {
			sprintf(pvmtxt, "msendrecv() from t%x code %d\n",
					up->ub_src, up->ub_cod);
			pvmlogerror(pvmtxt);
		}
		if (up->ub_src == other && up->ub_cod == code)
			break;
	}
	LISTDELETE(up, ub_link, ub_rlink);
	if (pvmrbufmid > 0)
		umbuf_free(pvmrbufmid);
	pvmrbufmid = 0;
	if (cc = pvm_setrbuf(up->ub_mid))
		return cc;
	return up->ub_mid;
}


/*	mksocs()
*
*	Make socket for talking to pvmd.  Connect to address advertized
*	in pvmd socket-name file.
*
*	Returns 0 if okay, else error code.
*/

int
mksocs()
{
	char buf[128];
	int d;
	int n;
	int try;
	char *p;
#ifndef NOUNIXDOM
	struct sockaddr_un uns;
#endif

	if (topvmd)
		return 0;

	/*
	* get addr of pvmd, make socket to talk.
	* first try envar PVMSOCK, then try sockaddr file.
	*/

	if (!(p = getenv("PVMSOCK"))) {
		if (!(p = pvmdsockfile())) {
			pvmlogerror("mksocs() pvmdsockfile() failed\n");
			goto bail;
		}
		if ((d = open(p, O_RDONLY, 0)) == -1) {
			pvmlogperror(p);
			goto bail;
		}

		try = 3;
		do {
			if ((n = read(d, buf, sizeof(buf))) == -1) {
				pvmlogperror("mksocs() read addr file");
				goto bail;
			}
/*
			if (n != 13) XXX danger
*/
			if (n == 0) {
				if (--try < 1) {
					pvmlogerror("mksocs() read addr file: wrong length read\n");
					goto bail;
				}
				sleep(1);	/* XXX hmm */
			}
		} while (n == 0);
/*
		while (n != 13); XXX
*/
		(void)close(d);
		buf[n] = 0;
		p = buf;
	}

	FD_ZERO(&pvmrfds);
/*
	FD_ZERO(&pvmwfds);
*/
	pvmnfds = 0;

	topvmd = ttpcb_new();
	topvmd->tt_tid = TIDPVMD;

	if (p[0] == '/') {
#ifdef NOUNIXDOM
		pvmlogerror("mksocs() no support for Unix domain socket\n");
		goto bail;

#else /*NOUNIXDOM*/
		if ((topvmd->tt_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
			pvmlogperror("mksocs() socket");
			goto bail;
		}

	/*
	* XXX these 2 loops are a hack -
	* XXX it keeps too many tasks from overflowing the pvmd listen q.
	* XXX maybe the pvmd should make a socket pair for the task instead.
	*/
		try = 5;
		while (1) {
			BZERO((char*)&uns, sizeof(uns));
			uns.sun_family = AF_UNIX;
			strcpy(uns.sun_path, p);
			n = sizeof(uns);
			if (connect(topvmd->tt_fd, (struct sockaddr*)&uns, n) == -1) {
				if (--try <= 0) {
					pvmlogperror("mksocs() connect");
					goto bail;
				}
				sleep(1);	/* XXX hmm again */

			} else
				break;
		}
#endif /*NOUNIXDOM*/

	} else {
		if ((topvmd->tt_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
			pvmlogperror("mksocs() socket");
			goto bail;
		}

		try = 5;
		while (1) {	/* goto is at the root of all good programming style */
			hex_inadport(p, &topvmd->tt_osad);
			topvmd->tt_osad.sin_family = AF_INET;
			n = sizeof(topvmd->tt_osad);

			if (connect(topvmd->tt_fd, (struct sockaddr*)&topvmd->tt_osad, n)
			== -1) {
				if (--try <= 0) {
					pvmlogperror("mksocs() connect");
					goto bail;
				}
				sleep(1);	/* XXX hmm again */

			} else
				break;
		}

#ifndef NOSOCKOPT
		d = 1;
		if (setsockopt(topvmd->tt_fd, IPPROTO_TCP, TCP_NODELAY, (char*)&d, sizeof(int))
		== -1) {
			pvmlogperror("mksocs() setsockopt");
			goto bail;
		}
#endif
		d = sizeof(topvmd->tt_sad);
		if (getsockname(topvmd->tt_fd, (struct sockaddr*)&topvmd->tt_sad, &d)
		== -1) {
			pvmlogperror("mksocs() getsockname");
			goto bail;
		}
	}

	topvmd->tt_state = TTOPEN;
	pvm_fd_add(topvmd->tt_fd, 1);

	return 0;

bail:
	if (topvmd)
		ttpcb_delete(topvmd);
	topvmd = 0;
	return PvmSysErr;
}


/*	unmksocs()
*
*	Close socket to local pvmd.
*/

int
unmksocs()
{
	if (!topvmd)
		return 1;
	while (ttlist->tt_link != ttlist)
		ttpcb_delete(ttlist->tt_link);
	ttpcb_delete(topvmd);
	topvmd = 0;
	return 0;
}


/*	pvmbeatask()
*
*	Initialize libpvm, config process as a task.
*	This is called as the first step of each libpvm function so no
*	explicit initialization is required.
*
*	Makes socket to talk to local pvmd, connects to pvmd, trades
*	config information and authentication.
*
*	Returns 0 if okay, else error code.
*/

int
pvmbeatask()
{
	int sbf = 0, rbf = 0;			/* saved rx and tx message handles */
	int prver;						/* protocol version */
	static int altpid = -1;			/* pid assigned by pvmd if diff from real */
	int cc;
	char authfn[LEN_OF_TMP_NAM];	/* auth file name */
	int authfd = -1;				/* auth fd to validate pvmd ident */
	int i;
	char buf[16];					/* for converting sockaddr */
	char *p;
	int x;

/*
	pvm_setopt(PvmDebugMask, -1);
*/
	if (pvmmytid != -1)
		return 0;

	if (x = pvmtoplvl)
		pvmtoplvl = 0;

	authfn[0] = 0;

	pvmmyupid = getpid();

	/*
	* get expected pid from environment in case we were started by
	* the pvmd and someone forked again
	*/

	if (altpid == -1) {
		char *p;

		if (p = getenv("PVMEPID"))
			altpid = atoi(p);
		else
			altpid = 0;
	}

/*
	if ((pvm_useruid = getuid()) == -1)
*/
	if ((pvm_useruid = geteuid()) == -1) {
		pvmlogerror("can't getuid()\n");
		cc = PvmSysErr;
		goto bail2;
	}

	if (cc = mksocs())		/* make socket to talk to pvmd */
		goto bail2;

	/*
	*	initialize received-message list
	*/

	pvmrxlist = TALLOC(1, struct umbuf, "umb");
	BZERO((char*)pvmrxlist, sizeof(struct umbuf));
	pvmrxlist->ub_link = pvmrxlist->ub_rlink = pvmrxlist;

	ttlist = TALLOC(1, struct ttpcb, "tpcb");
	BZERO((char*)ttlist, sizeof(struct ttpcb));
	ttlist->tt_link = ttlist->tt_rlink = ttlist;

	/*
	*	create empty t-auth file that pvmd must write and we'll check later
	*/

	(void)TMPNAMFUN(authfn);
	if ((authfd = open(authfn, O_RDONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
		pvmlogperror(authfn);
		pvmlogerror("pvmbeatask() can't creat t-auth file\n");
		cc = PvmSysErr;
		goto bail2;
	}

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

	/*
	*	send first connect message to pvmd
	*/

	mxfersingle = 1;
	prver = TDPROTOCOL;
	pvm_pkint(&prver, 1, 1);
	pvm_pkstr(authfn);
	if ((cc = msendrecv(TIDPVMD, TM_CONNECT)) <= 0)
		goto bail;
	pvm_upkint(&prver, 1, 1);
	if (prver != TDPROTOCOL) {
		sprintf(pvmtxt, "pvmbeatask() t-d protocol mismatch (%d/%d)\n",
			TDPROTOCOL, prver);
		pvmlogerror(pvmtxt);
		cc = PvmSysErr;
		goto bail;
	}
	pvm_upkint(&cc, 1, 1);
	if (!cc) {
		pvmlogerror("pvmbeatask() pvmd refuses connection\n");
		goto bail;
	}

	/*
	*	check our t-auth file; write in pvmd d-auth file
	*/

	if ((cc = read(authfd, (char*)&cc, 1)) == -1) {
		pvmlogperror("pvmbeatask() read authfile");
		cc = PvmSysErr;
		goto bail;
	}
	if (cc != 1) {
		pvmlogerror("pvmbeatask() pvmd didn't validate itself\n");
		cc = PvmSysErr;
		goto bail;
	}
	(void)close(authfd);
	(void)unlink(authfn);

	pvm_upkstr(authfn);
	if ((authfd = open(authfn, O_WRONLY, 0)) == -1) {
		pvmlogperror(authfn);
		pvmlogerror("pvmbeatask() failed to open d-auth file\n");
		authfn[0] = 0;
		cc = PvmSysErr;
		goto bail;
	}
	cc = write(authfd, authfn, 1);
	(void)close(authfd);
	if (cc != 1) {
		if (cc == -1)
			pvmlogperror(authfn);
		pvmlogerror("pvmbeatask() can't write d-auth file\n");
		authfn[0] = 0;
		cc = PvmSysErr;
		goto bail;
	}
	authfd = -1;
	authfn[0] = 0;

	/*
	*	send second connect message to pvmd
	*/

	pvm_initsend(PvmDataFoo);
	pvm_pkint(&pvmmyupid, 1, 1);
	pvm_pkint(&altpid, 1, 1);
	if ((cc = msendrecv(TIDPVMD, TM_CONN2)) <= 0)
		goto bail;
	pvm_upkint(&cc, 1, 1);
	if (!cc) {
		pvmlogerror("pvmbeatask() pvmd refuses connection\n");
		goto bail;
	}
	pvm_upkint(&pvmmytid, 1, 1);
	pvm_upkint(&pvmmyptid, 1, 1);

	pvm_upkint(&pvmouttid, 1, 1);
	pvm_upkint(&pvmoutcod, 1, 1);
	pvmcouttid = pvmouttid;
	pvmcoutcod = pvmoutcod;
	pvm_upkint(&pvmtrctid, 1, 1);
	pvm_upkint(&pvmtrccod, 1, 1);
	pvmctrctid = pvmtrctid;
	pvmctrccod = pvmtrccod;

	/* get trace mask from envar or zero it */

	if ((p = getenv("PVMTMASK")) && strlen(p) + 1 == TEV_MASK_LENGTH)
		BCOPY(p, pvmtrcmask, TEV_MASK_LENGTH);
	else
		TEV_INIT_MASK(pvmtrcmask);

	BCOPY(pvmtrcmask, pvmctrcmask, TEV_MASK_LENGTH);

	pvm_upkint(&pvmudpmtu, 1, 1);
	pvmfrgsiz = pvmudpmtu;

	pvm_upkint(&pvmmyndf, 1, 1);

	pvm_upkstr(buf);
	hex_inadport(buf, &pvmourinet);
	pvmourinet.sin_family = AF_INET;
	pvmourinet.sin_port = 0;

	pvm_upkint(&pvmschedtid, 1, 1);

	pvm_freebuf(pvm_setrbuf(rbf));
	pvm_freebuf(pvm_setsbuf(sbf));
	mxfersingle = 0;

	pvmtoplvl = x;
	return 0;

bail:
	if (pvm_getrbuf() > 0)
		pvm_freebuf(pvm_getrbuf());
	if (pvm_getsbuf() > 0)
		pvm_freebuf(pvm_getsbuf());
	pvm_setrbuf(rbf);
	pvm_setsbuf(sbf);

	if (authfd != -1)
		(void)close(authfd);
	if (authfn[0])
		(void)unlink(authfn);
	unmksocs();

bail2:
	pvmtoplvl = x;
	return cc;
}


int
pvmendtask()
{
	if (pvmmytid != -1) {
		unmksocs();
		pvmmytid = -1;
		pvmtrctid = 0;
	}

	return 0;
}


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


int
pvm_getopt(what)
	int what;
{
	int rc = 0;
	int err = 0;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (pvmmytid != -1 && TEV_DO_TRACE(TEV_GETOPT0)) {
			pvm_pkint(&what, 1, 1);
			TEV_FIN;
		}
	}

	switch (what) {
	case PvmRoute:
		rc = pvmrouteopt;
		break;

	case PvmDebugMask:
		rc = debugmask;
		break;

	case PvmAutoErr:
		rc = pvmautoerr;
		break;

	case PvmOutputTid:
		rc = pvmcouttid;
		break;

	case PvmOutputCode:
		rc = pvmcoutcod;
		break;

	case PvmTraceTid:
		rc = pvmctrctid;
		break;

	case PvmTraceCode:
		rc = pvmctrccod;
		break;

	case PvmFragSize:
		rc = pvmfrgsiz;
		break;

	case PvmResvTids:
		rc = pvmrescode;
		break;

	case PvmSelfOutputTid:
		rc = pvmouttid;
		break;

	case PvmSelfOutputCode:
		rc = pvmoutcod;
		break;

	case PvmSelfTraceTid:
		rc = pvmtrctid;
		break;

	case PvmSelfTraceCode:
		rc = pvmtrccod;
		break;

	default:
		err = 1;
		break;
	}

	if (x) {
		if (pvmmytid != -1 && TEV_DO_TRACE(TEV_GETOPT1)) {
			pvm_pkint(&rc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (err)
		return lpvmerr("pvm_getopt", PvmBadParam);
	return rc;
}


int
pvm_setopt(what, val)
	int what;
	int val;
{
	int rc = 0;
	int err = 0;
	int sbf, rbf;
	int x;
	char buf[16];

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (pvmmytid != -1 && TEV_DO_TRACE(TEV_SETOPT0)) {
			pvm_pkint(&what, 1, 1);
			pvm_pkint(&val, 1, 1);
			TEV_FIN;
		}
	}

	switch (what) {
	case PvmRoute:
		switch (val) {
		case PvmDontRoute:
		case PvmAllowDirect:
		case PvmRouteDirect:
			rc = pvmrouteopt;
			pvmrouteopt = val;
			break;

		default:
			rc = PvmBadParam;
			err = 1;
			break;
		}
		break;

	case PvmDebugMask:
		rc = debugmask;
		debugmask = val;
		break;

	case PvmAutoErr:
		rc = pvmautoerr;
		pvmautoerr = val;
		break;

	case PvmOutputTid:
		if (val && val != pvmmytid
		&& (val != pvmouttid || pvmcoutcod != pvmoutcod)) {
			rc = PvmBadParam;
			err = 1;

		} else {
			rc = pvmcouttid;
			pvmcouttid = val;
		}
		break;

	case PvmOutputCode:
		if (pvmcouttid && pvmcouttid != pvmmytid && val != pvmoutcod) {
			rc = PvmBadParam;
			err = 1;

		} else {
			rc = pvmcoutcod;
			pvmcoutcod = val;
		}
		break;

	case PvmTraceTid:
		if (val && val != pvmmytid
		&& (val != pvmtrctid || pvmctrccod != pvmtrccod)) {
			rc = PvmBadParam;
			err = 1;

		} else {
			rc = pvmctrctid;
			pvmctrctid = val;
		}
		break;

	case PvmTraceCode:
		if (pvmctrctid && pvmctrctid != pvmmytid && val != pvmtrccod) {
			rc = PvmBadParam;
			err = 1;

		} else {
			rc = pvmctrccod;
			pvmctrccod = val;
		}
		break;

	case PvmFragSize:
		if (val < TDFRAGHDR + TTMSGHDR + 4 || val > 1048576) {
			rc = PvmBadParam;
			err = 1;

		} else {
			rc = pvmfrgsiz;
			pvmfrgsiz = val;
		}
		break;

	case PvmResvTids:
		rc = pvmrescode;
		pvmrescode = val;
		break;

	case PvmSelfOutputTid:
		if (rc = BEATASK)
			err = 1;
		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			what = TS_OUTTID;
			pvm_pkint(&what, 1, 1);
			sprintf(buf, "%x", 0xffffffff & val);
			pvm_pkstr(buf);
			if ((rc = msendrecv(TIDPVMD, TM_SETOPT)) > 0) {
				pvm_freebuf(pvm_setrbuf(rbf));
				rc = pvmouttid;
				pvmouttid = val;
				pvmcouttid = pvmouttid;
				pvmcoutcod = pvmoutcod;

			} else {
				pvm_setrbuf(rbf);
				err = 1;
			}
			pvm_freebuf(pvm_setsbuf(sbf));
		}
		break;

	case PvmSelfOutputCode:
		if (rc = BEATASK)
			err = 1;
		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			what = TS_OUTCOD;
			pvm_pkint(&what, 1, 1);
			sprintf(buf, "%x", 0xffffffff & val);
			pvm_pkstr(buf);
			if ((rc = msendrecv(TIDPVMD, TM_SETOPT)) > 0) {
				pvm_freebuf(pvm_setrbuf(rbf));
				rc = pvmoutcod;
				pvmoutcod = val;
				pvmcouttid = pvmouttid;
				pvmcoutcod = pvmoutcod;

			} else {
				pvm_setrbuf(rbf);
				err = 1;
			}
			pvm_freebuf(pvm_setsbuf(sbf));
		}
		break;

	case PvmSelfTraceTid:
		if (rc = BEATASK)
			err = 1;
		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			what = TS_TRCTID;
			pvm_pkint(&what, 1, 1);
			sprintf(buf, "%x", 0xffffffff & val);
			pvm_pkstr(buf);
			if ((rc = msendrecv(TIDPVMD, TM_SETOPT)) > 0) {
				pvm_freebuf(pvm_setrbuf(rbf));
				rc = pvmtrctid;
				pvmtrctid = val;
				pvmctrctid = pvmtrctid;
				pvmctrccod = pvmtrccod;

			} else {
				pvm_setrbuf(rbf);
				err = 1;
			}
			pvm_freebuf(pvm_setsbuf(sbf));
		}
		break;

	case PvmSelfTraceCode:
		if (rc = BEATASK)
			err = 1;
		else {
			sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
			rbf = pvm_setrbuf(0);
			what = TS_TRCCOD;
			pvm_pkint(&what, 1, 1);
			sprintf(buf, "%x", 0xffffffff & val);
			pvm_pkstr(buf);
			if ((rc = msendrecv(TIDPVMD, TM_SETOPT)) > 0) {
				pvm_freebuf(pvm_setrbuf(rbf));
				rc = pvmtrccod;
				pvmtrccod = val;
				pvmctrctid = pvmtrctid;
				pvmctrccod = pvmtrccod;

			} else {
				pvm_setrbuf(rbf);
				err = 1;
			}
			pvm_freebuf(pvm_setsbuf(sbf));
		}
		break;

	default:
		rc = PvmBadParam;
		err = 1;
		break;
	}

	if (x) {
		if (pvmmytid != -1 && TEV_DO_TRACE(TEV_SETOPT1)) {
			pvm_pkint(&rc, 1, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	if (err)
		return lpvmerr("pvm_setopt", rc);
	return rc;
}


int
pvm_perror(s)
	char *s;
{
	if (pvmmytid == -1)
		fprintf(stderr, "libpvm [pid%d]: ", pvmmyupid);
	else
		fprintf(stderr, "libpvm [t%x]: ", pvmmytid);
	fprintf(stderr, "%s: %s\n",
		(s ? s : "(null)"),
		(pvm_errno <= 0 && pvm_errno > -pvm_nerr
				? pvm_errlist[-pvm_errno] : "Unknown Error"));
	return 0;
}


int
pvm_getfds(fds)		/* XXX this function kinda sucks */
	int **fds;			/* fd list return */
{
	static int *fdlist = 0;
	static int fdlen = 0;
	int cc;
	int nfds;
	struct ttpcb *pcbp;
	int x;

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

	if (!(cc = BEATASK)) {
		nfds = 1;
		for (pcbp = ttlist->tt_link; pcbp != ttlist; pcbp = pcbp->tt_link)
			if (pcbp->tt_state == TTOPEN || pcbp->tt_state == TTGRNWAIT)
				nfds++;

		if (fdlen < nfds) {
			fdlen = (nfds * 3) / 2 + 1;
			if (fdlist)
				fdlist = TREALLOC(fdlist, fdlen, int);
			else
				fdlist = TALLOC(fdlen, int, "fdlist");
		}

		fdlist[0] = topvmd->tt_fd;
		nfds = 1;
		for (pcbp = ttlist->tt_link; pcbp != ttlist; pcbp = pcbp->tt_link)
			if (pcbp->tt_state == TTOPEN || pcbp->tt_state == TTGRNWAIT)
				fdlist[nfds++] = pcbp->tt_fd;
		*fds = fdlist;
		cc = nfds;
	}

	if (x) {
		if (TEV_DO_TRACE(TEV_GETFDS1)) {
			pvm_pkint(&cc, 1, 1);
			if (cc > 0)
				pvm_pkint((int *)fdlist, nfds, 1);
			TEV_FIN;
		}
		pvmtoplvl = x;
	}

	return (cc < 0 ? lpvmerr("pvm_getfds", cc) : cc);
}


int
pvm_start_pvmd(argc, argv, block)
	int argc;		/* number of args to pass to pvmd (>= 0) */
	char **argv;	/* args for pvmd or null */
	int block;		/* if true, don't return until add hosts are started */
{
	char *sfn;
	struct stat sb;
	int cc;
	char *fn;			/* file to exec */
	char **av;
	int x;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (pvmmytid != -1 && TEV_DO_TRACE(TEV_START_PVMD0)) {
			pvm_pkint(&argc, 1, 1);
			pvm_pkint(&block, 1, 1);
			for (cc = 0; cc < argc; cc++)
				pvm_pkstr(argv[cc]);
			TEV_FIN;
		}
	}

	if (argc < 0 || !argv)
		argc = 0;

	if ((pvm_useruid = getuid()) == -1) {
		pvmlogerror("can't getuid()\n");
		cc = PvmSysErr;
		goto bail;
	}

	if (!(sfn = pvmdsockfile())) {
		pvmlogerror("pvm_start_pvmd() pvmdsockfile() failed\n");
		cc = PvmSysErr;
		goto bail;
	}

	if (stat(sfn, &sb) != -1) {
		cc = PvmDupHost;
		goto bail;
	}

	fn = pvmgetpvmd();

	av = TALLOC(argc + 2, char *, "argv");
	if (argc > 0)
		BCOPY((char *)&argv[0], (char *)&av[1], argc * sizeof(char*));
	av[0] = fn;
	av[argc + 1] = 0;

	if (!fork()) {
		if (!fork()) {
			for (cc = getdtablesize(); cc-- > 0; )
				(void)close(cc);
			(void)open("/dev/null", O_RDONLY, 0);
			(void)open("/dev/null", O_WRONLY, 0);
			(void)dup2(1, 2);
			execvp(av[0], av);
		}
		_exit(0);
	}
	(void)wait(0);

	PVM_FREE(av);

	for (cc = 8; cc > 0 && stat(sfn, &sb) == -1; cc--)
		sleep(1);

	if (cc <= 0) {
		cc = PvmCantStart;
		goto bail;
	}
	if (cc = BEATASK)
		goto bail;

	if (block) {
		struct pvmhostinfo *hip;
		int t = 1;

		pvm_config((int*)0, (int*)0, &hip);
		while ((cc = pvm_addhosts(&hip[0].hi_name, 1, (int*)0)) == PvmAlready) {
			sleep(t);
			if (t < 8)
				t *= 2;
		}
		if (cc != PvmDupHost)
			goto bail;
		cc = BEATASK;
	}

bail:

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

	return (cc < 0 ? lpvmerr("pvm_start_pvmd", cc) : 0);
}


/*	tev_begin()
*
*	Start a trace event.  Initialize a message, write time and event kind.
*/

int
tev_begin(kind)
	int kind;
{
	struct timeval now;

	gettimeofday(&now, (struct timezone *)0);
	pvmtrcmid = pvm_mkbuf(PvmDataFoo);
	pvmtrcsbf = pvm_setsbuf(pvmtrcmid);
	pvm_pkint((int *)&now.tv_sec, 1, 1);
	pvm_pkint((int *)&now.tv_usec, 1, 1);
	pvm_pkint(&pvmmytid, 1, 1);
	pvm_pkint(&kind, 1, 1);
/*
	fprintf(stderr, "tev_begin() mid %d\n", pvmtrcmid);
*/
	return 1;
}


/*	tev_fin()
*
*	End a trace event.  Send message.
*/

int
tev_fin()
{
	int routetmp;
	static struct timeval ztv = { 0, 0 };

/*
	fprintf(stderr, "tev_fin() mid %d\n", pvmtrcmid);
*/
	if (pvmmytid != pvmtrctid) {
		if ((routetmp = pvmrouteopt) == PvmRouteDirect)
			pvmrouteopt = PvmAllowDirect;
		mroute(pvmtrcmid,  pvmtrctid, pvmtrccod, &ztv);
		pvmrouteopt = routetmp;
	}
	pvm_setsbuf(pvmtrcsbf);
	pvmtrcsbf = 0;
	pvm_freebuf(pvmtrcmid);
	pvmtrcmid = 0;
	return 0;
}


/*	tev_do_trace()
*
*	Export TEV_DO_TRACE() so user code doesn't have to import
*	masks and trace tid, etc.
*/

int
tev_do_trace(kind)
	int kind;
{
	return TEV_DO_TRACE(kind);
}


int
pvm_precv(tid, tag, cp, len, dt, rtid, rtag, rlen)
	int tid;
	int tag;
	void *cp;
	int len;
	int dt;
	int *rtid;
	int *rtag;
	int *rlen;
{
	int rbf;
	int cc = 0;
	int l;
	int x;
	long ad;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_PRECV0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&tag, 1, 1);
			ad = (long)cp;
			pvm_pklong(&ad, 1, 1);
			pvm_pkint(&len, 1, 1);
			pvm_pkint(&dt, 1, 1);
			TEV_FIN;
		}
	}

	switch (dt) {

	case PVM_BYTE:
		len *= sizeof(char);
		break;

	case PVM_SHORT:
	case PVM_USHORT:
		len *= sizeof(short);
		break;

	case PVM_INT:
	case PVM_UINT:
		len *= sizeof(int);
		break;

	case PVM_LONG:
	case PVM_ULONG:
		len *= sizeof(long);
		break;

	case PVM_FLOAT:
		len *= sizeof(float);
		break;

	case PVM_CPLX:
		len *= sizeof(float) * 2;
		break;

	case PVM_DOUBLE:
		len *= sizeof(double);
		break;

	case PVM_DCPLX:
		len *= sizeof(double) * 2;
		break;

	default:
		cc = PvmBadParam;
		break;
	}

	if (!cc) {
		rbf = pvm_setrbuf(0);
		cc = pvm_recv(tid, tag);
		if (cc > 0) {
			pvm_bufinfo(cc, &l, rtag, rtid);
			if (rlen)
				*rlen = l;
			if (l < len)
				len = l;
			pvm_upkbyte((char *)cp, len, 1);
			pvm_freebuf(cc);
			cc = 0;
		}
		pvm_setrbuf(rbf);
	}

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

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


int
pvm_psend(tid, tag, cp, len, dt)
	int tid;
	int tag;
	void *cp;
	int len;
	int dt;
{
	int sbf;
	int cc = 0;
	int x;
	long ad;

	if (x = pvmtoplvl) {
		pvmtoplvl = 0;
		if (TEV_DO_TRACE(TEV_PSEND0)) {
			pvm_pkint(&tid, 1, 1);
			pvm_pkint(&tag, 1, 1);
			ad = (long)cp;
			pvm_pklong(&ad, 1, 1);
			pvm_pkint(&len, 1, 1);
			pvm_pkint(&dt, 1, 1);
			TEV_FIN;
		}
	}

	switch (dt) {

	case PVM_BYTE:
		len *= sizeof(char);
		break;

	case PVM_SHORT:
	case PVM_USHORT:
		len *= sizeof(short);
		break;

	case PVM_INT:
	case PVM_UINT:
		len *= sizeof(int);
		break;

	case PVM_LONG:
	case PVM_ULONG:
		len *= sizeof(long);
		break;

	case PVM_FLOAT:
		len *= sizeof(float);
		break;

	case PVM_CPLX:
		len *= sizeof(float) * 2;
		break;

	case PVM_DOUBLE:
		len *= sizeof(double);
		break;

	case PVM_DCPLX:
		len *= sizeof(double) * 2;
		break;

	default:
		cc = PvmBadParam;
		break;
	}

	if (!cc) {
		sbf = pvm_setsbuf(pvm_mkbuf(PvmDataInPlace));
		pvm_pkbyte((char *)cp, len, 1);
		if ((cc = pvm_send(tid, tag)) > 0)
			cc = 0;
		pvm_freebuf(pvm_setsbuf(sbf));
	}

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

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


