
/*
 *           PVM 3.2:  Parallel Virtual Machine System 3.2
 *               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 3.2 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.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 <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#ifdef IMA_RS6K
#include <sys/select.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#ifdef	SYSVSTR
#include <string.h>
#else
#include <strings.h>
#endif
#include <errno.h>
#include <signal.h>
#include <pvm3.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"

/* 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 */
};

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

#define	TC_CONREQ	0x80000001		/* connection request */
#define	TC_CONACK	0x80000002		/* connection request ack */
#define	TC_TASKEXIT	0x80000003		/* task exited/doesn't exist */


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 */
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 pvmtrctid = 0;						/* trace dst and code */
int pvmtrccod = 0;
int pvm_useruid = -1;					/* user's unix uid */


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

static char rcsid[] = "$Id: lpvm.c,v 1.4 1993/10/04 20:29:05 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() */


/**************************
 **  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);

	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;
	}
	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
*		(if block is true) at least one message fully received
*	Returns >=0 the number of complete messages downloaded, or
*	negative on error.
*/

int
mroute(mid, dtid, code, block)
	int mid;		/* message (or 0) */
	int dtid;		/* dest */
	int code;		/* type code */
	int block;		/* 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;

	if (up = midtobuf(mid)) {
		up->ub_dst = dtid;
		up->ub_cod = code;
	}
	if (
	pvmrouteopt == PvmRouteDirect
	&& dtid && TIDISTASK(dtid) && dtid != pvmmytid
	&& up
	&& !ttpcb_find(dtid)
	) {

		if ((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0) {
			sad = pvmourinet;
			l = sizeof(sad);
			if (bind(s, (struct sockaddr*)&sad, sizeof(sad)) == -1) {
				pvmlogperror("mroute() bind");
				(void)close(s);

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

				} else {
					pcbp = ttpcb_creat(dtid);
					pcbp->tt_fd = s;
					pcbp->tt_sad = sad;
					pcbp->tt_state = TTCONWAIT;
					sbf = pvm_setsbuf(pvm_mkbuf(PvmDataFoo));
					l = TDPROTOCOL;
					pvm_pkint(&l, 1, 1);
					pvm_pkstr(inadport_hex(&sad));
					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);
					l = dtid;
					pvm_pkint(&l, 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 ? 0 : 1))) < 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);
				}
			}

		} else {
/*
			if (errno == EMFILE) {
			} else {
			}
*/
	/*
	* XXX  for now, it's a bad idea to try closing connections
	* XXX  in order to open new ones.  just route messages through
	* XXX  the pvmd.
	*/
			pvmlogperror("mroute() socket");
		}
	}
	cc = mxfer(mid, block);
	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);
}


/*	mctl()
*
*	Called by mxinput() when a control message (with code bit 31 set)
*	is received.
*/

static int
mctl(up)
	struct umbuf *up;
{
	struct ttpcb *pcbp;
	int rbf;				/* temp rx message storage */
	int sbf = 0;			/* returned message to send */
	char buf[16];			/* to convert sockaddr */
	int ttpro;				/* to check protocol number */
	int ackd;
	int l;
	struct umbuf *up2;
	int i;
	int tid;
	static int linger[2] = { 1, 60 };	/* XXX arbitrary time */

	switch (up->ub_cod) {

	case TC_CONREQ:
		rbf = pvm_setrbuf(up->ub_mid);
		pvm_upkint(&ttpro, 1, 1);
		pvm_upkstr(buf);
		pvm_setrbuf(rbf);

		if (pcbp = ttpcb_find(up->ub_src)) {
			if (debugmask & TDMROUTE) {
				sprintf(pvmtxt, "mctl() crossed CONREQ from t%x\n",
						up->ub_src);
				pvmlogerror(pvmtxt);
			}
			if (pcbp->tt_state == TTCONWAIT) {
				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("mctl() connect");

				} else {
					pcbp->tt_state = TTOPEN;
#ifndef NOSOCKOPT
#if 0
					i = 1;
					if (setsockopt(pcbp->tt_fd, IPPROTO_TCP, TCP_NODELAY,
							(char*)&i, sizeof(int)) == -1)
						pvmlogperror("mctl() setsockopt");
#endif /*0*/
					if (setsockopt(pcbp->tt_fd, SOL_SOCKET, SO_LINGER,
							(char*)linger, sizeof(linger)) == -1)
						pvmlogperror("mctl() setsockopt");
#endif /*NOSOCKOPT*/
					if ((i = fcntl(pcbp->tt_fd, F_GETFL, 0)) == -1)
						pvmlogperror("mctl() 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, "mctl() CONREQ from t%x but state=%d ?\n",
						up->ub_src, pcbp->tt_state);
				pvmlogerror(pvmtxt);
			}

		} else {
			if (debugmask & TDMROUTE) {
				sprintf(pvmtxt, "mctl() CONREQ from t%x\n", up->ub_src);
				pvmlogerror(pvmtxt);
			}
			ackd = 1;
			pcbp = ttpcb_creat(up->ub_src);
			if (pvmrouteopt != PvmDontRoute) {
				if ((pcbp->tt_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
					pvmlogperror("mctl() socket");

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

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

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

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

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

			if (ackd)
				ttpcb_delete(pcbp);
		}
		break;

	case TC_CONACK:
		rbf = pvm_setrbuf(up->ub_mid);
		pvm_upkint(&ttpro, 1, 1);
		pvm_upkint(&ackd, 1, 1);
		pvm_upkstr(buf);
		pvm_setrbuf(rbf);

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

				} else {
					if (ackd == 0) {
						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("mctl() connect");
							ackd = 1;

						} else {
							pcbp->tt_state = TTOPEN;
#ifndef NOSOCKOPT
#if 0
							i = 1;
							if (setsockopt(pcbp->tt_fd, IPPROTO_TCP,
									TCP_NODELAY, (char*)&i, sizeof(int))
							== -1)
								pvmlogperror("mctl() setsockopt");
#endif /*0*/
							if (setsockopt(pcbp->tt_fd, SOL_SOCKET, SO_LINGER,
									(char*)linger, sizeof(linger)) == -1)
								pvmlogperror("mctl() setsockopt");
#endif /*NOSOCKOPT*/
							if ((i = fcntl(pcbp->tt_fd, F_GETFL, 0)) == -1)
								pvmlogperror("mctl() 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 {
						if (debugmask & TDMROUTE) {
							sprintf(pvmtxt, "mctl() route to t%x denied\n",
									pcbp->tt_tid);
							pvmlogerror(pvmtxt);
						}
					}
				}

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

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

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

	case TC_TASKEXIT:
		rbf = pvm_setrbuf(up->ub_mid);
		pvm_upkint(&tid, 1, 1);
		pvm_setrbuf(rbf);

		if (debugmask & TDMROUTE) {
			sprintf(pvmtxt, "mctl() TASKEXIT for t%x\n", tid);
			pvmlogerror(pvmtxt);
		}
		if (pcbp = ttpcb_find(tid))
			ttpcb_dead(pcbp);
		break;

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

	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_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
*		(if block is true) at least one message fully received
*	Returns >=0 the number of complete messages downloaded, or
*	negative on error.
*/

int
mxfer(mid, block)
	int mid;		/* message */
	int block;		/* 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 tout;
	struct timeval *tvp;
	fd_set rfds, wfds;
	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 mctl() */
	struct umbuf *up;
	struct sockaddr_in sad;
	int s;
	struct ttpcb *pcbp;
int tstk[100];				/* XXX aaigh! */
int tstkp = 0;

/* XXX doesn't want to be a stack.  make it a dll or something */

	if (txup = midtobuf(mid)) {
		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 (debugmask & TDMMESSAGE) {
			sprintf(pvmtxt, "mxfer() mid %d block %d dst t%x route t%x\n",
					mid, block, txup->ub_dst, txpcbp->tt_tid);
			pvmlogerror(pvmtxt);
		}

	} else {
		if (debugmask & TDMMESSAGE) {
			sprintf(pvmtxt, "mxfer() mid %d block %d\n", mid, block);
			pvmlogerror(pvmtxt);
		}
	}

	do {
		TVCLEAR(&tout);
		tvp = (txfp || (block && !gotem) || topvmd->tt_rxf) ? 0 : &tout;
/* XXX should topvmd->tt_rxf be U{ttlist->rxf} ? */

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

		if (debugmask & TDMSELECT) {
			sprintf(pvmtxt, "mxfer() select timeout is %s\n",
					(tvp ? "0" : "Inf"));
			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 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;
			}

			gotem += n;

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

			while ((up = up->ub_link) != pvmrxlist) {
				if (up->ub_cod & 0x80000000) {
					up = up->ub_rlink;
					if ((sbf = mctl(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
					gotem += n;

			} 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 {
						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) {
				txcp = txfp->fr_dat;
				txtogo = txfp->fr_len;
	/*
	* if this is first frag, prepend t-t 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);
					ff = FFSOM;
				}
				if (txfp->fr_link == txup->ub_frag)
					ff |= FFEOM;
	/*
	* prepend t-d header
	*/
				txcp -= TDFRAGHDR;
				pvmput32(txcp, txup->ub_dst);
				pvmput32(txcp + 4, pvmmytid);
				pvmput32(txcp + 8, txtogo);
				pvmput8(txcp + 12, ff);
				txtogo += TDFRAGHDR;
				if (debugmask & TDMPACKET) {
					sprintf(pvmtxt, "mxfer() dst t%x n=%d\n",
							txup->ub_dst, txtogo);
					pvmlogerror(pvmtxt);
				}
			}

			n = write(txpcbp->tt_fd, txcp, txtogo);
			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 */
/*
					abort();
*/
					ttpcb_dead(txpcbp);
					txpcbp = topvmd;
					txcp = 0;
					txtogo = 0;
					txfp = txup->ub_frag->fr_link;
				}
			}
			if (n > 0) {
				if ((txtogo -= n) > 0) {
					txcp += n;

				} else {
					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);
		}
	} while (txfp || (block && !gotem) || topvmd->tt_rxf);
/* XXX should topvmd->tt_rxf be U{ttlist->tt_rxf} ? */

	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, 1)) < 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, 1)) < 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;

	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) {
				if (--try < 1) {
					pvmlogerror("mksocs() read addr file: wrong length read\n");
					goto bail;
				}
				sleep(1);	/* XXX hmm */
			}
		} while (n != 13);
		(void)close(d);
		buf[n] = 0;
		p = buf;
	}

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

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

	hex_inadport(p, &topvmd->tt_osad);
	topvmd->tt_osad.sin_family = AF_INET;
	n = sizeof(topvmd->tt_osad);

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

	if (connect(topvmd->tt_fd, (struct sockaddr*)&topvmd->tt_osad, n) == -1) {
		pvmlogperror("mksocs() connect");
		goto bail;
	}

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

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

#else	/*NOTMPNAM*/
#ifdef	L_tmpnam
#define	LEN_OF_TMP_NAM	L_tmpnam
#else
#define	LEN_OF_TMP_NAM	64
#endif

#endif	/*NOTMPNAM*/

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 */
	char buf[16];					/* for converting sockaddr */

/*
	pvm_setopt(PvmDebugMask, -1);
*/
	if (pvmmytid != -1)
		return 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) {
		pvmlogerror("can't getuid()\n");
		return PvmSysErr;
	}

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

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

#ifdef NOTMPNAM
	(void)pvmtmpnam(authfn);
#else
	(void)tmpnam(authfn);
#endif
	if ((authfd = open(authfn, O_RDONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
		pvmlogperror(authfn);
		pvmlogerror("pvmbeatask() can't creat t-auth file\n");
		return PvmSysErr;
	}

	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;
	}
	(void)write(authfd, authfn, 1);
	(void)close(authfd);
	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;

	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_freebuf(pvm_setrbuf(rbf));
	pvm_freebuf(pvm_setsbuf(sbf));
	mxfersingle = 0;

	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();

	return cc;
}


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

	return 0;
}


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


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

	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;

	default:
		err = 1;
		break;
	}
	if (err)
		return lpvmerr("pvm_getopt", PvmBadParam);
	return rc;
}


int
pvm_setopt(what, val)
	int what;
	int val;
{
	int rc = 0;
	int err = 0;

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

		default:
			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))
			err = 1;
		else {
			rc = pvmcouttid;
			pvmcouttid = val;
		}
		break;

	case PvmOutputCode:
		if (pvmcouttid && pvmcouttid != pvmmytid && val != pvmoutcod)
			err = 1;
		else {
			rc = pvmcoutcod;
			pvmcoutcod = val;
		}
		break;

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

	case PvmTraceCode:
		if (pvmctrctid && pvmctrctid != pvmmytid && val != pvmtrccod)
			err = 1;
		else {
			rc = pvmctrccod;
			pvmctrccod = val;
		}
		break;

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

	default:
		err = 1;
		break;
	}
	if (err)
		return lpvmerr("pvm_setopt", PvmBadParam);
	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;

	if (cc = pvmbeatask())
		return lpvmerr("pvm_getfds", cc);

	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;
	return nfds;
}


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;

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

	if ((pvm_useruid = getuid()) == -1) {
		pvmlogerror("can't getuid()\n");
		return lpvmerr("pvm_start_pvmd", PvmSysErr);
	}

	if (!(sfn = pvmdsockfile())) {
		pvmlogerror("pvm_start_pvmd() pvmdsockfile() failed\n");
		return lpvmerr("pvm_start_pvmd", PvmSysErr);
	}

	if (stat(sfn, &sb) != -1)
		return lpvmerr("pvm_start_pvmd", PvmDupHost);

	fn = pvmgetpvmd();

	av = TALLOC(argc + 2, char *, "argv");
	if (argc > 0)
		BCOPY(&argv[0], &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 = pvm_mytid()) < 0)
		goto bail;

	if (block) {
		struct hostinfo *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 = pvm_mytid();
	}

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


/*
* Obsolete Functions
*/

int
pvm_advise(what)
	int what;
{
	return pvm_setopt(PvmRoute, what);
}


int
pvm_setdebug(mask)
	int mask;
{
	return pvm_setopt(PvmDebugMask, mask);
}


