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

/*
 *	ddpro.c
 *
 *	Entry points for messages from network.
 *
$Log: ddpro.c,v $
 * Revision 1.1  1993/08/30  23:26:47  manchek
 * Initial revision
 *
 */


#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef	SYSVSTR
#include <string.h>
#else
#include <strings.h>
#endif
#include <errno.h>
#include <stdio.h>

#include "global.h"
#include "fromlib.h"
#include "tdpro.h"
#include "ddpro.h"
#include "protoglarp.h"
#include "pvmalloc.h"
#include "host.h"
#include "mesg.h"
#include "waitc.h"
#include "task.h"
#include "listmac.h"
#if defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)
#include "pvmmimd.h"
#endif
#include "bfunc.h"


extern void pvmbailout();
extern char *inadport_hex();
char **pathsep();

extern int debugmask;				/* from pvmd.c */
extern char **epaths;				/* from pvmd.c */
extern char *debugger;				/* from pvmd.c */
extern struct htab *hosts;			/* from pvmd.c */
extern struct task *locltasks;		/* from task.c */
extern char *myarchname;			/* from pvmd.c */
extern int myhostpart;				/* from pvmd.c */
extern int mytid;					/* from pvmd.c */
extern int myunixpid;				/* from pvmd.c */
extern struct htab *newhosts;		/* from pvmd.c */
extern int netsock;					/* from pvmd.c */
extern int pprime;					/* from pvmd.c */
extern int pprwid;					/* from pvmd.c */
extern int runstate;				/* from pvmd.c */
extern int tidhmask;				/* from pvmd.c */
extern int tidlmask;				/* from pvmd.c */
extern struct waitc *waitlist;		/* from waitc.c */


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

static char rcsid[] = "$Id: ddpro.c,v 1.1 1993/08/30 23:26:47 manchek Exp $";
static char pvmtxt[512];			/* scratch for error log */

int dm_addhost();
int dm_addhostack();
int dm_db();
int dm_dback();
int dm_delhost();
int dm_delhostack();
int dm_halt();
int dm_htcommit();
int dm_htdel();
int dm_htupd();
int dm_htupdack();
int dm_mca();
int dm_notify();
int dm_notifyack();
int dm_null();
int dm_pstat();
int dm_pstatack();
int dm_sendsig();
int dm_slconf();
int dm_spawn();
int dm_spawnack();
int dm_startack();
int dm_task();
int dm_taskack();
int dm_taskout();

int (*netswitch[])() = {
	0,
	dm_addhost, dm_addhostack,
	dm_spawn, dm_spawnack,
	dm_sendsig,
	dm_htupd, dm_htupdack,
	dm_htcommit,
	dm_slconf,
	dm_startack,
	dm_task, dm_taskack,
	dm_delhost, dm_delhostack,
	dm_null,
	dm_taskout,
	dm_pstat, dm_pstatack,
	dm_halt,
	dm_mca,
	dm_notify, dm_notifyack,
	dm_db, dm_dback,
	dm_htdel,
};


char *dm_names[] = {
	"dm_addhost", "dm_addhostack",
	"dm_spawn", "dm_spawnack",
	"dm_sendsig",
	"dm_htupd", "dm_htupdack",
	"dm_htcommit",
	"dm_slconf",
	"dm_startack",
	"dm_task", "dm_taskack",
	"dm_delhost", "dm_delhostack",
	"dm_null",
	"dm_taskout",
	"dm_pstat", "dm_pstatack",
	"dm_halt",
	"dm_mca",
	"dm_notify", "dm_notifyack",
	"dm_db", "dm_dback",
	"dm_htdel",
};

char *
dmname(code)
	int code;
{
	if (code < 1 || code > sizeof(dm_names)/sizeof(dm_names[0]))
		return "unknown";
	return dm_names[code - 1];
}


/*	hostfailentry()
*
*	We've decided a remote pvmd has failed.
*	Wake up any wait context waiting on that host (different action
*	for each kind of wait).
*	Send a HTDEL message to remaining hosts except us.
*/

int
hostfailentry(hp)
	struct hostd *hp;
{
	int hpart = hp->hd_hostpart;
	struct waitc *wp, *wp2;
	struct mesg *mp;

	if (debugmask & PDMHOST) {
		sprintf(pvmtxt, "hostfailentry() host %s\n", hp->hd_name);
		pvmlogerror(pvmtxt);
		hd_dump(hp);
	}

	if (hp == hosts->ht_hosts[hosts->ht_master]) {
		pvmlogerror("hostfailentry() lost master host, we're screwwwed\n");
		pvmbailout(0);
	}

	/*
	* if we're master pvmd, send HT_DEL message to all others
	*/

	if (hp->hd_hostpart && hosts->ht_master == hosts->ht_local) {
		struct hostd *hp2;
		int hh;

		mp = mesg_new(0);
		mp->m_cod = DM_HTDEL;
		pkint(mp, hosts->ht_serial);
		pkint(mp, hp->hd_hostpart);
		for (hh = hosts->ht_last; hh > 0; hh--)
			if (hh != hosts->ht_local
			&& (hp2 = hosts->ht_hosts[hh]) && hp2 != hp) {
				mp->m_ref++;
				mp->m_dst = hp2->hd_hostpart | TIDPVMD;
				sendmessage(mp);
			}
		mesg_unref(mp);
	}

	for (wp = waitlist->wa_link; wp != waitlist; wp = wp->wa_link) {
		if ((wp->wa_on & TIDHOST) == hpart) {
			switch (wp->wa_kind) {

			case WT_ADDHOST:	/* the master must have died */
			case WT_DELHOST:	/* the master must have died */

				sprintf(pvmtxt, "hostfailentry() can't deal with wait kind %d\n",
						wp->wa_kind);
				pvmlogerror(pvmtxt);
				break;

			case WT_HTUPD:
				if (wp->wa_peer == wp) {
					int hh;

					mp = mesg_new(0);
					mp->m_cod = DM_HTCOMMIT;

					for (hh = hosts->ht_last; hh > 0; hh--)
						if (hh != hosts->ht_local && (hp = hosts->ht_hosts[hh])) {
							mp->m_ref++;
							mp->m_dst = hp->hd_hostpart | TIDPVMD;
							sendmessage(mp);
						}
					mesg_unref(mp);

					sendmessage(wp->wa_mesg);
					wp->wa_mesg = 0;
				}
				break;

			case WT_SPAWN:
				{
					struct waitc_spawn *wxp;
					int v;

					wxp = (struct waitc_spawn*)wp->wa_spec;

	/* mark tasks assigned to this host as failed */

					for (v = wxp->w_veclen; v-- > 0; )
						if (wxp->w_vec[v] == hp->hd_hostpart)
							wxp->w_vec[v] = PvmHostFail;

					ht_delete(wxp->w_ht, hp);

	/* is this was the last wait, reply to task */

					if (wp->wa_peer == wp)
						assign_tasks(wp);
				}
				break;

			case WT_TASK:

	/* send message if we're the last waiter */

				if (wp->wa_peer == wp) {
					mp = wp->wa_mesg;
					mp->m_ref++;
					sendmessage(mp);
				}
				break;

			case WT_HOSTSTART:
				if (newhosts) {

	/* reply to waiter */

					mp = mesg_new(0);
					mp->m_dst = wp->wa_tid;
					mp->m_cod = DM_ADDHOSTACK;
					mp->m_wid = wp->wa_dep;
					pkint(mp, PvmDSysErr);
					sendmessage(mp);

					ht_free(newhosts);
					newhosts = 0;

				} else {
					pvmlogerror(
						"dm_hostfailentry() HOSTSTART/no new host tbl?\n");
				}
				break;

			case WT_TASKX:
				mp = mesg_new(0);
				mp->m_dst = wp->wa_tid;
				mp->m_cod = wp->wa_dep;
				pkint(mp, wp->wa_on);
				sendmessage(mp);
				break;

			case WT_PSTAT:
			case WT_MSTAT:
				mp = mesg_new(0);
				mp->m_dst = wp->wa_tid;
				if (wp->wa_kind == WT_PSTAT)
					mp->m_cod = TM_PSTAT;
				else
					mp->m_cod = TM_MSTAT;
				pkint(mp, PvmHostFail);
				sendmessage(mp);
				break;

			case WT_HOSTF:
				mp = mesg_new(0);
				mp->m_dst = wp->wa_tid;
				mp->m_cod = wp->wa_dep;
				pkint(mp, wp->wa_on);
				sendmessage(mp);
				break;

			default:
				sprintf(pvmtxt, "hostfailentry() alien wait kind %d\n",
						wp->wa_kind);
				pvmlogerror(pvmtxt);
				break;
			}

			wp2 = wp->wa_rlink;
			wait_delete(wp);
			wp = wp2;
		}
	}
	return 0;
}


int
netentry(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int c = mp->m_cod;

	if (debugmask & PDMMESSAGE) {
		sprintf(pvmtxt,
				"hostentry() from host %s src t%x dst t%x cod %s wid %d\n",
				hp->hd_name, mp->m_src, mp->m_dst, dmname(mp->m_cod),
				mp->m_wid);
		pvmlogerror(pvmtxt);
/*
		pvmhdump(mp->m_frag->fr_link->fr_dat, mp->m_frag->fr_link->fr_len,
				"hostentry() ");
*/
	}

	if (c < 1 || c >= sizeof(netswitch)/sizeof(netswitch[0])) {
		sprintf(pvmtxt, "netentry() message from t%x with bogus code %d\n",
				mp->m_src, c);
		pvmlogerror(pvmtxt);
		goto bail;
	}

	(netswitch[c])(hp, mp);

bail:
	mesg_unref(mp);
	return 0;
}


/*********************************
 **  Pvmd message entry points  **
 **                             **
 *********************************/

/*	hostid_new()
*
*	Get the next available host id (one not in use in hosts or newhosts).
*	Wrap around to lowest number when we reach the highest possible.
*	Return zero if none are available.
*/

int
hostid_new()
{
	static int lasthh = 0;
	int oldhh;
	int maxhostid = tidhmask >> (ffs(tidhmask) - 1);

	/* if first time, find highest hostid in host table */

	if (!lasthh)
		for (lasthh = hosts->ht_last; !hosts->ht_hosts[lasthh]; lasthh--) ;

	if (++lasthh > maxhostid)
		lasthh = 1;
	oldhh = lasthh;

	/* find next free hostid */

	while ((lasthh <= hosts->ht_last && hosts->ht_hosts[lasthh])
	|| (lasthh <= newhosts->ht_last && newhosts->ht_hosts[lasthh])) {
		if (++lasthh > maxhostid)
			lasthh = 1;
		if (lasthh == oldhh)
			return 0;
	}

	return lasthh;
}


/*	dm_addhost()
*
*	(Master pvmd) gets request to add new hosts.
*
*	DM_ADDHOST(wid) {
*		int count
*		string name[count]
*	}
*/

int
dm_addhost(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int count;
	int i;
	struct waitc *wp;
	int pid;
	int hh;
	int maxhostid = (tidhmask >> ffs(tidhmask) - 1);
	char *name;

/* XXX this should queue the request */
	if (newhosts || pprime) {
		struct mesg *mp2;

		pvmlogerror("dm_addhost() already adding new hosts, oops\n");
		mp2 = mesg_new(0);
		mp2->m_dst = mp->m_src;
		mp2->m_cod = DM_ADDHOSTACK;
		mp2->m_wid = mp->m_wid;
		pkint(mp2, PvmAlready);
		sendmessage(mp2);
		return 0;
	}

	/* sanity check count */

	if (upkint(mp, &count)) {
		pvmlogerror("dm_addhost() bad msg format\n");
		return 0;
	}
	if (count < 1 || count > maxhostid) {
		pvmlogerror("dm_addhost() bad count\n");
		return 0;
	}

	/* extract host table from message */

	newhosts = ht_new(1);
	newhosts->ht_serial = hosts->ht_serial + 1;
	newhosts->ht_master = hosts->ht_master;
	newhosts->ht_cons = hosts->ht_cons;

	for (i = 0; i < count; i++) {
		if (upkstralloc(mp, &name)) {
			pvmlogerror("dm_addhost() bad msg format\n");
			ht_free(newhosts);
			newhosts = 0;
			return 0;
		}
	/* XXX this is not right - shouldn't bind dtids to new hosts
	   XXX until they're up and running. */
		if (!(hh = hostid_new())) {
			pvmlogerror("dm_addhost() out of hostids\n");
			PVM_FREE(name);

		} else {
			hp = hd_new(hh);
			hp->hd_name = name;
			ht_insert(newhosts, hp);
			hd_unref(hp);
		}
	}

	/* make wait context for caller */

	wp = wait_new(WT_HOSTSTART);
	wp->wa_tid = mp->m_src;
	wp->wa_dep = mp->m_wid;
	wp->wa_on = 0;

	/* fork off pvmd' to do the startup */

	pprwid = wp->wa_wid;

	if (pid = fork()) {		/* still us */
		if (pid == -1) {
			pvmlogperror("dm_addhost() fork");

	/* nack request if can't fork */
			mp = mesg_new(0);
			mp->m_dst = wp->wa_tid;
			mp->m_cod = DM_ADDHOSTACK;
			mp->m_wid = wp->wa_wid;
			pkint(mp, PvmOutOfRes);
			sendmessage(mp);
			wait_delete(wp);

		} else {
			pprime = pid;
		}

	} else {				/* pvmd' to do the startup */
		beprime();
		start_slaves(newhosts);
	}
	return 0;
}


/*	dm_addhostack()
*
*	Reply to DM_ADDHOST operation.
*
*	DM_ADDHOSTACK(wid_rtn) {
*		int count			// or negative for error
*		int status[count]	// status of each host
*	}
*/

int
dm_addhostack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;

	if (!(wp = wait_get(hp, mp, WT_ADDHOST)))
		return 0;
	mp->m_ref++;
	mp->m_dst = wp->wa_tid;
	mp->m_cod = TM_ADDHOST;
	sendmessage(mp);
	wait_delete(wp);
	return 0;
}


/*	dm_spawn()
*
*	We try to fork/exec given number of tasks.  On first error, fill
*	remainder of tid array with error code.  Return tid array to
*	caller.
*
*	DM_SPAWN(wid) {
*		int ptid
*		string file
*		int flags
*		int count
*		int nargs
*		string argv[nargs]
*		int outtid
*		int outcod
*		int trctid
*		int trccod
*		int nenv
*		string env[nenv]
*	}
*/

int
dm_spawn(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int ptid;				/* parent task id */
	char *file = 0;			/* file to exec */
	int flags;
	int count;				/* number of tasks */
	int nargs;				/* exec args */
	char **av = 0;
	int outtid, outcod;		/* stdout dst and code */
	int trctid, trccod;		/* trace dst and code */
	int nenv;				/* num of envars */
	char **env = 0;			/* envars */
	int *tids = 0;			/* list of tids from forkexec */
	struct mesg *mp2;		/* reply message hdl */
	struct task *tp = 0;
	int err = 0;			/* error code from forkexec */
	int i;

	hp = hp;

	/* unpack message */

	if (upkuint(mp, &ptid)
	|| upkstralloc(mp, &file)
	|| upkint(mp, &flags)
	|| upkint(mp, &count)
	|| upkint(mp, &nargs))
		goto bad;

	nargs += 2;
	av = TALLOC(nargs + 1, char*, "argv");
	av++;
	BZERO((char*)av, nargs * sizeof(char*));
	av[0] = file;
	file = 0;
	av[--nargs] = 0;
	for (i = 1; i < nargs; i++)
		if (upkstralloc(mp, &av[i]))
			goto bad;

	if (upkuint(mp, &outtid)
	|| upkuint(mp, &outcod)
	|| upkuint(mp, &trctid)
	|| upkuint(mp, &trccod)
	|| upkuint(mp, &nenv))
		goto bad;
	BZERO((char*)(env = TALLOC((nenv + 1), char*, "env")),
			(nenv + 1) * sizeof(char*));
	for (i = 0; i < nenv; i++)
		if (upkstralloc(mp, &env[i]))
			goto bad;

	if (count < 1)
		goto bad;

	tids = TALLOC(count, int, "tids");

#if defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)

/* XXX only writing the first tid, oops. */
	if (flags & PvmMppFront) {
		/* spawn on service node */
		if (err = forkexec(flags, av[0], av, nenv, env, &tp)) {
			tids[0] = err;
		} else {
			tp->t_ptid = ptid;
			tids[0] = tp->t_tid;
		}
	} else
		mpp_load(flags, av[0], av, count, tids, ptid);

#else /*defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)*/

	for (i = 0; i < count; i++) {
		if (err) {
			tids[i] = err;

		} else {
			if (err = forkexec(flags, av[0], av, nenv, env, &tp)) {
				tids[i] = err;

			} else {
				tp->t_ptid = ptid;
				tp->t_outtid = outtid;
				tp->t_outcod = outcod;
				tp->t_trctid = trctid;
				tp->t_trccod = trccod;
				tids[i] = tp->t_tid;
				if (outtid) {
					mp2 = mesg_new(0);
					mp2->m_cod = outcod;
					mp2->m_dst = outtid;
					pkint(mp2, tp->t_tid);
					pkint(mp2, -2);
					pkint(mp2, ptid);
					sendmessage(mp2);
				}
			}
		}
	}

#endif /*defined(IMA_PGON) || defined(IMA_I860) || defined(IMA_CM5)*/

	mp2 = mesg_new(0);
	pkint(mp2, count);
	for (i = 0; i < count; i++)
		pkint(mp2, tids[i]);
	mp2->m_dst = mp->m_src;
	mp2->m_cod = DM_SPAWNACK;
	mp2->m_wid = mp->m_wid;
	sendmessage(mp2);
	goto cleanup;

bad:
	sprintf(pvmtxt, "dm_spawn() from t%x bad msg format\n", mp->m_src);
	pvmlogerror(pvmtxt);

cleanup:
	if (tids)
		PVM_FREE(tids);
	if (file)
		PVM_FREE(file);
	if (av) {
		for (i = 0; i < nargs; i++)
			if (av[i])
				PVM_FREE(av[i]);
		av--;
		PVM_FREE(av);
	}
	if (env) {
		for (i = 0; i < nenv; i++)
			if (env[i])
				PVM_FREE(env[i]);
		PVM_FREE(env);
	}
	return 0;
}


/*	dm_spawnack()
*
*	Reply to DM_SPAWN op.
*
*	DM_SPAWNACK(wid_rtn) {
*		int count
*		int tids[count]
*	}
*/

int
dm_spawnack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;
	struct waitc_spawn *wxp;
	int rcnt;				/* num of tids+errors returned */
	int tid;
	int v;
	int err = 0;

	if (!(wp = wait_get(hp, mp, WT_SPAWN)))
		return 0;

	wxp = (struct waitc_spawn*)wp->wa_spec;
	upkint(mp, &rcnt);
	v = wxp->w_veclen;

	/*
	* unpack tids and place in result vector where hostpart is now
	*/

/*
	sprintf(pvmtxt, "dm_spawnack() hp %x vec len %d, repl len %d\n",
			hp->hd_hostpart, v, rcnt);
	pvmlogerror(pvmtxt);
*/
	while (rcnt-- > 0) {
		upkint(mp, &tid);
		if (tid < 0)
			err++;
		while (--v >= 0)
			if (wxp->w_vec[v] == hp->hd_hostpart)
				break;
		if (v < 0) {
/* XXX aargh */
			pvmlogerror("dm_spawnack() too many tids for result vector, hosing the request\n");
			wait_delete(wp);
			return 0;
		}
		wxp->w_vec[v] = tid;
	}

	if (err)
		ht_delete(wxp->w_ht, hp);

	/*
	* if everyone has checked in, either restart the failed ones
	* or reply to the caller.
	*/

	if (wp->wa_peer == wp)
		assign_tasks(wp);
	wait_delete(wp);
	return 0;
}


/*	dm_sendsig()
*
*	Request to send signal to local task.
*
*	DM_SENDSIG {
*		int tid
*		int signum
*	}
*/

int
dm_sendsig(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int tid;
	int signum;
	struct task *tp;

	hp = hp;

	if (upkuint(mp, &tid) || upkint(mp, &signum)) {
		pvmlogerror("dm_sendsig() bad msg format\n");
		return 0;
	}
	if (tp = task_find(tid)) {
#if defined(IMA_I860) || defined(IMA_CM5)
		(void)mpp_kill(tp, signum);

#else
		if (tp->t_pid)
			(void)kill(tp->t_pid, signum);

		else {
			sprintf(pvmtxt, "dm_sendsig() signal for t%x scrapped (pid = 0)\n",
					tid);
			pvmlogerror(pvmtxt);
		}
#endif

	} else {
		sprintf(pvmtxt, "dm_sendsig() signal for t%x scrapped\n", tid);
		pvmlogerror(pvmtxt);
	}
	return 0;
}


/*	dm_htupd()
*
*	Host table update phase 1 - (Non-master) pvmd is notified of new
*	host table.
*
*	DM_HTUPD(wid) {
*		int serial
*		int master
*		int console
*		int count
*		{
*			int index
*			string name
*			string arch
*			string hexipaddr
*			int mtu
*			int speed
*		} [count]
*	}
*/

int
dm_htupd(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int count;			/* number of hosts in message */
	int hh;
	char buf[16];		/* for converting sockaddr */
	struct mesg *mp2;

	/* unpack new host table params */

	newhosts = ht_new(1);
	newhosts->ht_local = hosts->ht_local;
	upkint(mp, &newhosts->ht_serial);
	upkint(mp, &newhosts->ht_master);
	upkint(mp, &newhosts->ht_cons);

	/* add current hosts to the table */

	ht_merge(newhosts, hosts);

	/* unpack new hosts and add to table */

	/* XXX if we find a host already in table we should kill it with
	   XXX hostfail and put the new one in its place */

	upkint(mp, &count);
	while (count-- > 0) {
		upkint(mp, &hh);
		hp = hd_new(hh);
		upkstralloc(mp, &hp->hd_name);
		upkstralloc(mp, &hp->hd_arch);
		upkstr(mp, buf, sizeof(buf));
		hex_inadport(buf, &hp->hd_sad);
		upkint(mp, &hp->hd_mtu);
		upkint(mp, &hp->hd_speed);
		ht_insert(newhosts, hp);
		hd_unref(hp);
	}

	if (debugmask & PDMHOST) {
		pvmlogerror("dm_htupd() new host table:\n");
		ht_dump(newhosts);
	}
	runstate = PVMDHTUPD;

	/* reply to sender that we have new host table */

	mp2 = mesg_new(0);
	mp2->m_dst = mp->m_src;
	mp2->m_cod = DM_HTUPDACK;
	mp2->m_wid = mp->m_wid;
	sendmessage(mp2);
	return 0;
}


/*	dm_htupdack()
*
*	Reply to DM_HTUPD op.
*
*	DM_HTUPDACK(wid_rtn) { }
*/

int
dm_htupdack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;

	if (!(wp = wait_get(hp, mp, WT_HTUPD)))
		return 0;

	/* is this is the last host checking in, send ht commit */

	if (wp->wa_peer == wp) {
		int hh;

		mp = mesg_new(0);
		mp->m_cod = DM_HTCOMMIT;

		for (hh = hosts->ht_last; hh > 0; hh--)
			if (hh != hosts->ht_local && (hp = hosts->ht_hosts[hh])) {
				mp->m_ref++;
				mp->m_dst = hp->hd_hostpart | TIDPVMD;
				sendmessage(mp);
			}
		mesg_unref(mp);

		sendmessage(wp->wa_mesg);
		wp->wa_mesg = 0;
	}
	wait_delete(wp);
	return 0;
}


/*	ht_diff()
*
*	Generates a host table containing entries in ht2 but not in ht1.
*/

struct htab *
ht_diff(htp2, htp1)
	struct htab *htp2;		/* more */
	struct htab *htp1;		/* less */
{
	struct htab *htp;
	int hh;
	
	htp = ht_new(1);
	for (hh = htp2->ht_last; hh > 0; hh--)
		if (htp2->ht_hosts[hh] && !htp2->ht_hosts[hh]->hd_err
		&& (hh > htp1->ht_last || !htp1->ht_hosts[hh]))
			ht_insert(htp, htp2->ht_hosts[hh]);
	return htp;
}


/*	gotnewhosts()
*
*	Used to wake up any waitcs of kind WT_HOSTA.
*	Sends a message containing count and list of new d-tids.
*/

gotnewhosts(htp2, htp1)
	struct htab *htp2;		/* new host table */
	struct htab *htp1;		/* old host table */
{
	struct mesg *mp;
	struct htab *htp;
	struct waitc *wp, *wp2;
	int hh;

	mp = 0;
	for (wp = waitlist->wa_link; wp != waitlist; wp = wp2) {
		wp2 = wp->wa_link;
		if (wp->wa_kind == WT_HOSTA) {
			if (!mp) {
/* XXX shouldn't diff host tables and pack message several times, oops */
				mp = mesg_new(0);
				htp = ht_diff(htp2, htp1);
				pkint(mp, htp->ht_cnt);
				for (hh = htp->ht_last; hh > 0; hh--)
					if (htp->ht_hosts[hh])
						pkint(mp, htp->ht_hosts[hh]->hd_hostpart);
				ht_free(htp);
			}
			mp->m_ref++;
			mp->m_dst = wp->wa_tid;
			mp->m_cod = wp->wa_dep;
			sendmessage(mp);
			wait_delete(wp);
		}
	}
	if (mp)
		mesg_unref(mp);
	return 0;
}


/*	dm_htcommit()
*
*	Host table update phase 2 - commit to new host table.
*
*	DM_HTCOMMIT { }
*/

int
dm_htcommit(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct htab *htp;

	if (hp != hosts->ht_hosts[hosts->ht_master]) {
		sprintf(pvmtxt, "dm_htcommit() from t%x (not master)?\n",
				mp->m_src);
		pvmlogerror(pvmtxt);
	}

	if (newhosts) {
		htp = hosts;
		hosts = newhosts;
		newhosts = 0;
		if (debugmask & PDMHOST) {
			sprintf(pvmtxt,
			"dm_htcommit() committing from host table serial %d to %d\n",
					htp->ht_serial, hosts->ht_serial);
			pvmlogerror(pvmtxt);
		}

		gotnewhosts(hosts, htp);
		ht_free(htp);
		runstate = PVMDNORMAL;

	} else {
		pvmlogerror("dm_htcommit() no new host table pending?\n");
	}
	return 0;
}


/*	dm_slconf()
*
*	Pvmd gets config info (from master).
*	One of these should arrive before a pvmd is fully up, even
*	before the host table update, to set private params.
*	More may be used later to set random knobs.
*
*	DM_SLCONF {
*		{
*			int fieldtype		// from ddpro.h
*			string value
*		} []					// implied
*	}
*/

int
dm_slconf(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int t;				/* field type */
	char *s;

	mp = mp;
	hp = hp;

	while (!upkint(mp, &t) && !upkstralloc(mp, &s)) {
		switch (t) {

		case DM_SLCONF_EP:
			epaths = pathsep(s, ':', myarchname, '%');
			if (debugmask & PDMSTARTUP) {
				sprintf(pvmtxt, "dm_slconf() ep<%s>\n", s);
				pvmlogerror(pvmtxt);
			}
			break;

		case DM_SLCONF_BP:
			debugger = s;
			if (debugmask & PDMSTARTUP) {
				sprintf(pvmtxt, "dm_slconf() bp<%s>\n", s);
				pvmlogerror(pvmtxt);
			}
			break;

		default:
			if (strlen(s) > sizeof(pvmtxt)-50)
				s[sizeof(pvmtxt)-50] = 0;
			sprintf(pvmtxt, "dm_slconf() ? type %d val <%s>\n", t, s);
			pvmlogerror(pvmtxt);
			PVM_FREE(s);
			break;
		}
	}
	return 0;
}


/*	dm_startack()
*
*	This is how the new host table info gets from pvmd' back to
*	pvmd[master].
*
*	DM_STARTACK(wid_rtn) {
*		int count
*		{
*			int index
*			int errcode			// zero if ok
*			{					// if errcode == 0
*				string arch
*				string hexipaddr
*				int mtu
*				{					// to be fwd in DM_SLCONF message
*					int fieldtype	// from ddpro.h
*					string value
*				} []				// implied
*				int 0				// marks end of options
*			}
*		} [count]
*	}
*/

int
dm_startack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;		/* wait context on pvmd' */
	int count;				/* num of new hosts */
	int happy;				/* num of happy new hosts */
	int hh;
	char buf[16];			/* for converting sockaddr */
	struct waitc *wp2;		/* 'seed' waitc for peer wait on htupdack */
	struct waitc *wp3;
	struct mesg *mp2;
	int t;
	char *s;

	finack_to_host(hp);

	if (!(wp = wait_get(hp, mp, WT_HOSTSTART)))
		return 0;

	if (!newhosts) {
		pvmlogerror("dm_startack() no new host table?\n");
		return 0;
	}

	/*
	* unpack host table info (from pvmd'), put stats or err codes in newhosts.
	* forward options to new slaves with DM_SLCONF message.
	* if one or more started ok, do host-table update.
	*/

	upkint(mp, &count);
	happy = 0;
	while (count-- > 0) {
		upkint(mp, &hh);
		hp = newhosts->ht_hosts[hh];
		upkint(mp, &hp->hd_err);
		if (!hp->hd_err) {
			upkstralloc(mp, &hp->hd_arch);
			upkstr(mp, buf, sizeof(buf));
			hex_inadport(buf, &hp->hd_sad);
			upkint(mp, &hp->hd_mtu);

			if (!happy++)
				runstate = PVMDHTUPD;

			mp2 = mesg_new(0);
			mp2->m_cod = DM_SLCONF;
			while (!upkint(mp, &t) && t) {
				pkint(mp2, t);
				upkstralloc(mp, &s);
				pkstr(mp2, s);
				PVM_FREE(s);
			}
			mp2->m_dst = hp->hd_hostpart | TIDPVMD;
			sendmessage(mp2);
		}
	}

	if (debugmask & PDMSTARTUP) {
		pvmlogerror("dm_startack() new host table from pvmd':\n");
		ht_dump(newhosts);
	}

	wp2 = wait_new(WT_HTUPD);

	/*
	* compose reply to waiter.  attach it to a wait context to go
	* when the host table update is completed.
	*/

	mp = mesg_new(0);
	mp->m_dst = wp->wa_tid;
	mp->m_cod = DM_ADDHOSTACK;
	mp->m_wid = wp->wa_dep;
	pkint(mp, newhosts->ht_cnt);
/* XXX danger, the dtids may be out of order w/r/t the names sent
   XXX if the hostid wraps */
	for (hh = 1; hh <= newhosts->ht_last; hh++)
		if (hp = newhosts->ht_hosts[hh])
			pkint(mp, (hp->hd_err ? hp->hd_err : hp->hd_hostpart));
	wp2->wa_mesg = mp;
	wait_delete(wp);
	wp = 0;

	/* if at least one working new host */

	if (happy) {
		
		/*
		* create host table update message containing all current hosts
		* plus happy new ones.
		*/

		mp = mesg_new(0);
		mp->m_cod = DM_HTUPD;
		pkint(mp, newhosts->ht_serial);
		pkint(mp, hosts->ht_master);
		pkint(mp, hosts->ht_cons);
		pkint(mp, hosts->ht_cnt + happy);
		for (hh = hosts->ht_last; hh > 0; hh--)
			if ((hp = hosts->ht_hosts[hh])) {
				pkint(mp, hh);
				pkstr(mp, hp->hd_name);
				pkstr(mp, hp->hd_arch);
				pkstr(mp, inadport_hex(&hp->hd_sad));
				pkint(mp, hp->hd_mtu);
				pkint(mp, hp->hd_speed);
			}
		for (hh = newhosts->ht_last; hh > 0; hh--)
			if ((hp = newhosts->ht_hosts[hh]) && !hp->hd_err) {
				pkint(mp, hh);
				pkstr(mp, hp->hd_name);
				pkstr(mp, hp->hd_arch);
				pkstr(mp, inadport_hex(&hp->hd_sad));
				pkint(mp, hp->hd_mtu);
				pkint(mp, hp->hd_speed);
			}

		/*
		* send to each new host
		*/

		for (hh = newhosts->ht_last; hh > 0; hh--)
			if ((hp = newhosts->ht_hosts[hh]) && !hp->hd_err) {
				mp->m_ref++;
				mp->m_dst = hp->hd_hostpart | TIDPVMD;
				wp3 = wait_new(WT_HTUPD);
				wp2->wa_mesg->m_ref++;
				wp3->wa_mesg = wp2->wa_mesg;
				wp3->wa_on = hp->hd_hostpart;
				LISTPUTBEFORE(wp2, wp3, wa_peer, wa_rpeer);
				mp->m_wid = wp3->wa_wid;
				sendmessage(mp);
			}
		mesg_unref(mp);

		/*
		* create host table update message containing happy new ones.
		*/

		mp = mesg_new(0);
		mp->m_cod = DM_HTUPD;
		pkint(mp, newhosts->ht_serial);
		pkint(mp, newhosts->ht_master);
		pkint(mp, newhosts->ht_cons);
		pkint(mp, happy);
		for (hh = newhosts->ht_last; hh > 0; hh--)
			if ((hp = newhosts->ht_hosts[hh]) && !hp->hd_err) {
				pkint(mp, hh);
				pkstr(mp, hp->hd_name);
				pkstr(mp, hp->hd_arch);
				pkstr(mp, inadport_hex(&hp->hd_sad));
				pkint(mp, hp->hd_mtu);
				pkint(mp, hp->hd_speed);
			}

		/*
		* send to each old host
		*/

		for (hh = hosts->ht_last; hh > 0; hh--)
			if (hh != hosts->ht_local && (hp = hosts->ht_hosts[hh])) {
				mp->m_ref++;
				mp->m_dst = hp->hd_hostpart | TIDPVMD;
				wp3 = wait_new(WT_HTUPD);
				wp2->wa_mesg->m_ref++;
				wp3->wa_mesg = wp2->wa_mesg;
				wp3->wa_on = hp->hd_hostpart;
				LISTPUTBEFORE(wp2, wp3, wa_peer, wa_rpeer);
				mp->m_wid = wp3->wa_wid;
				sendmessage(mp);
			}
		mesg_unref(mp);

		/*
		* update our host table
		*/

	/* XXX returning to normal state right here is a hack, we should
	   XXX wait for all the ht upd acks to come back but we need the
	   XXX regular message service, hostfail entry, etc. */

		gotnewhosts(newhosts, hosts);
		for (hh = newhosts->ht_last; hh > 0; hh--)
			if ((hp = newhosts->ht_hosts[hh]) && !hp->hd_err)
				ht_insert(hosts, hp);
		hosts->ht_serial = newhosts->ht_serial;

		if (debugmask & PDMHOST) {
			pvmlogerror("dm_startack() committing to new host table:\n");
			ht_dump(hosts);
		}
		runstate = PVMDNORMAL;
	}

	if (wp2->wa_peer == wp2) {
		sendmessage(wp2->wa_mesg);
		wp2->wa_mesg = 0;
	}
	wait_delete(wp2);

	ht_free(newhosts);
	newhosts = 0;
	return 0;
}


/*	dm_task()
*
*	(Remote or local) pvmd requests information about local task(s).
*
*	DM_TASK(wid) {
*		int where			// 0 for all, hostpart, or full tid
*	}
*/

int
dm_task(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct task *tp;
	struct mesg *mp2;
	int where;

	hp = hp;

	if (upkuint(mp, &where)) {
		pvmlogerror("dm_task() bad msg format\n");
		return 0;
	}

	/* pack list of local tasks and reply to waiter */

	mp2 = mesg_new(0);
	mp2->m_dst = mp->m_src;
	mp2->m_cod = DM_TASKACK;
	mp2->m_wid = mp->m_wid;
	if (where & tidlmask) {
		if (tp = task_find(where)) {
			pkint(mp2, tp->t_tid);
			pkint(mp2, tp->t_ptid);
			pkint(mp2, myhostpart);
			pkint(mp2, tp->t_flag);
			pkstr(mp2, tp->t_a_out ? tp->t_a_out : "");
		}

	} else {
		for (tp = locltasks->t_link; tp != locltasks; tp = tp->t_link) {
			pkint(mp2, tp->t_tid);
			pkint(mp2, tp->t_ptid);
			pkint(mp2, myhostpart);
			pkint(mp2, tp->t_flag);
			pkstr(mp2, (tp->t_a_out ? tp->t_a_out : ""));
		}
	}
	sendmessage(mp2);
	return 0;
}


/*	dm_taskack()
*
*	Reply to DM_TASK op.
*
*	DM_TASKACK(wid_rtn) {
*		{
*			int tid
*			int ptid
*			int hostpart
*			int flag
*			string a_out
*		} []				// implied
*	}
*/

int
dm_taskack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;
	struct mesg *mp2;
	int i;
	char *p;

	if (!(wp = wait_get(hp, mp, WT_TASK)))
		return 0;

	/* append data to waiting message */

	mp2 = wp->wa_mesg;
	while (!upkint(mp, &i)) {
		pkint(mp2, i);			/* tid */
		upkint(mp, &i);			/* ptid */
		pkint(mp2, i);
		upkint(mp, &i);			/* host */
		pkint(mp2, i);
		upkint(mp, &i);			/* flag */
		pkint(mp2, i);
		upkstralloc(mp, &p);	/* a.out name */
		pkstr(mp2, p);
		PVM_FREE(p);
	}

	/* send message if we're the last waiter */

	if (wp->wa_peer == wp) {
		mp2->m_ref++;
		sendmessage(mp2);

	}
	wait_delete(wp);
	return 0;
}


/*	dm_delhost()
*
*	(Master pvmd) gets request to delete hosts.
*
*	DM_DELHOST(wid) {
*		int count
*		string names[count]
*	}
*/

int
dm_delhost(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int count;
	char buf[128];
	struct mesg *mp2;		/* DELHOSTACK message */
	struct mesg *mp3;		/* HTDEL message */
	struct htab *ht_del;	/* hosts to delete */
	struct htab *ht_save;	/* remaining hosts */
	int hh;

/* XXX danger, this doesn't check if already doing a host add/delete */

	/* sanity check count */

	if (upkint(mp, &count)) {
		pvmlogerror("dm_delhost() bad msg format\n");
		return 0;
	}
	if (count < 1 || count > (tidhmask >> (ffs(tidhmask) - 1))) {
		pvmlogerror("dm_delhost() bad count\n");
		return 0;
	}

	/*
	* read host names from message, generate delete and save sets
	* and a DELHOSTACK reply message with result code for each host.
	* set SHUTDOWN flag for each host in delete set.
	*/

	ht_del = ht_new(1);
	ht_save = ht_new(1);
	ht_merge(ht_save, hosts);

	mp2 = mesg_new(0);
	mp2->m_cod = DM_DELHOSTACK;
	mp2->m_wid = mp->m_wid;
	mp2->m_dst = mp->m_src;

	mp3 = mesg_new(0);
	mp3->m_cod = DM_HTDEL;
	pkint(mp3, hosts->ht_serial);

	pkint(mp2, count);
	while (count-- > 0) {
		upkstr(mp, buf, sizeof(buf));
		if (hp = nametohost(hosts, buf)) {
			if (tidtohost(ht_del, hp->hd_hostpart)) {
				pkint(mp2, PvmDupHost);

			} else {
				if (hp->hd_hostpart == myhostpart)
					pkint(mp2, PvmBadParam);

				else {
					ht_insert(ht_del, hp);
					ht_delete(ht_save, hp);
					pkint(mp3, hp->hd_hostpart);
					fin_to_host(hp);
					pkint(mp2, 0);
				}
			}

		} else
			pkint(mp2, PvmNoHost);
	}

	/*
	* send HTDEL message to all hosts in ht_save set except us
	*/

	for (hh = ht_save->ht_last; hh > 0; hh--)
		if (hh != hosts->ht_local && (hp = ht_save->ht_hosts[hh])) {
			mp3->m_ref++;
			mp3->m_dst = hp->hd_hostpart | TIDPVMD;
			sendmessage(mp3);
		}
	mesg_unref(mp3);

	/* reply to host that requested DELHOST operation */

	sendmessage(mp2);

	ht_free(ht_del);
	ht_free(ht_save);
	return 0;
}


/*	dm_delhostack()
*
*	Reply to DM_DELHOST operation.
*
*	DM_DELHOSTACK(wid_rtn) {
*		int count			// or negative for error
*		int status[count]	// status of each host
*	}
*/

int
dm_delhostack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;

	if (!(wp = wait_get(hp, mp, WT_DELHOST)))
		return 0;

	mp->m_ref++;
	mp->m_dst = wp->wa_tid;
	mp->m_cod = TM_DELHOST;
	sendmessage(mp);
	wait_delete(wp);
	return 0;
}


/*	dm_null()
*
*	No-op message.
*
*	DM_NULL { }
*/

int
dm_null(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	hp = hp;
	mp = mp;
/*
	sprintf(pvmtxt, "dm_null() from %s\n", hp->hd_name);
	pvmlogerror(pvmtxt);
*/
	return 0;
}


/*	dm_taskout()
*
*	Stdout data from a task.
*
*	DM_TASKOUT {
*		int tid
*		int length
*		char data[length]
*	}
*/

int
dm_taskout(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int tid;
	int l;
	char *p, *q, c;
	char buf2[32];
	char buf[4100];		 /* a bit bigger than in pvmd.c`loclstout() */

	hp = hp;

	if (upkuint(mp, &tid) || upkint(mp, &l) || l < 1)
		return 0;

	/* unpack data, leaving room at head of buffer */

	p = buf + 32;
	if (l > sizeof(buf) - (p - buf) - 2)
		l = sizeof(buf) - (p - buf) - 2;
	byteupk(mp, p, l, 1, 1);

	/* ends with "\n\0" */
	if (p[l - 1] != '\n')
		p[l++] = '\n';
	p[l] = 0;

	sprintf(buf2, "[t%x] ", tid);
	l = strlen(buf2);
	while (*p) {
		for (q = p; *q++ != '\n'; ) ;
		c = *q;
		*q = 0;
		BCOPY(buf2, p - l, l);
		pvmlogerror(p - l);
		*q = c;
		p = q;
	}
	return 0;
}


/*	dm_pstat()
*
*	(Remote or local) pvmd requests status of a local task.
*
*	DM_PSTAT(wid) {
*		int tid
*	}
*/

int
dm_pstat(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int tid;
	struct mesg *mp2;

	hp = hp;
	upkuint(mp, &tid);
	if (tid == (myhostpart | TIDPVMD) || task_find(tid))
		tid = 0;
	else
		tid = PvmNoTask;
	mp2 = mesg_new(0);
	mp2->m_dst = mp->m_src;
	mp2->m_cod = DM_PSTATACK;
	mp2->m_wid = mp->m_wid;
	pkint(mp2, tid);
	sendmessage(mp2);
	return 0;
}


/*	dm_pstatack()
*
*	Reply to DM_PSTAT op.
*
*	DM_PSTATACK(wid_rtn) {
*		int status
*	}
*/

int
dm_pstatack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;

	if (!(wp = wait_get(hp, mp, 0)))
		return 0;

	if (wp->wa_kind == WT_PSTAT)
		mp->m_cod = TM_PSTAT;
	else
		mp->m_cod = TM_MSTAT;
	mp->m_ref++;
	mp->m_dst = wp->wa_tid;
	sendmessage(mp);
	wait_delete(wp);
	return 0;
}


/*	dm_halt()
*
*	Task has requested whole machine to halt.
*
*	DM_HALT { }
*/

int
dm_halt(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int hh;

	sprintf(pvmtxt, "dm_halt() from (%s), halting...\n",
			hp->hd_name);
	pvmlogerror(pvmtxt);
	for (hh = hosts->ht_last; hh > 0; hh--) {
		if (hh == hosts->ht_local || !(hp = hosts->ht_hosts[hh]))
			continue;
		finack_to_host(hp);
	}
	runstate = PVMDHALTING;
	return 0;
}


/*	dm_mca()
*
*	Remote pvmd defines a new (single-use) multicast address, to be used
*	by a subsequent message.
*
*	DM_MCA {
*		int gtid			// multicast tid
*		int count			// number of addresses
*		int tids[count]		// addresses
*	}
*/

int
dm_mca(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct mca *mcap;
	int i;

	/* unpack struct mca from message */

	mcap = mca_new();
	upkuint(mp, &mcap->mc_tid);
	upkint(mp, &mcap->mc_ndst);
	mcap->mc_dsts = TALLOC(mcap->mc_ndst, int, "mcad");
	for (i = 0; i < mcap->mc_ndst; i++)
		upkuint(mp, &mcap->mc_dsts[i]);

	/* put on list of mcas of src host */

	LISTPUTBEFORE(hp->hd_mcas, mcap, mc_link, mc_rlink);

	if (debugmask & PDMMESSAGE) {
		sprintf(pvmtxt, "dm_mca() mca %x from %s\n", mcap->mc_tid, hp->hd_name);
		pvmlogerror(pvmtxt);
	}
	return 0;
}


/*	dm_notify()
*
*	Remote pvmd requests to be notified on an event.
*
*	DM_NOTIFY(wid) {
*		int what			// event type XXX currenty PvmTaskExit
*		int tid				// address
*	}
*/

int
dm_notify(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int what, tid;
	struct waitc *wp;
	struct mesg *mp2;

	hp = hp;

	upkint(mp, &what);	/* XXX currently PvmTaskExit */
	upkuint(mp, &tid);

	if (task_find(tid)) {
		wp = wait_new(WT_TASKX);
		wp->wa_on = tid;
		wp->wa_tid = mp->m_src;
		wp->wa_dep = mp->m_wid;

	} else {
		mp2 = mesg_new(0);
		mp2->m_dst = mp->m_src;
		mp2->m_cod = DM_NOTIFYACK;
		mp2->m_wid = mp->m_wid;
		sendmessage(mp2);
	}
	return 0;
}


/*	dm_notifyack()
*
*	Reply to DM_NOTIFY op.
*
*	DM_NOTIFYACK(wid_rtn) { }
*/

int
dm_notifyack(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;

	hp = hp;

	if (!(wp = wait_get((struct hostd*)0, mp, 0)))
		return 0;

	mp = mesg_new(0);
	mp->m_dst = wp->wa_tid;
	mp->m_cod = wp->wa_dep;
	pkint(mp, wp->wa_on);
	sendmessage(mp);

	wait_delete(wp);
	return 0;
}


/*	dm_db()
*
*	(Remote or local) pvmd requests a database op.
*
*	DM_DB(wid) {
*		int opcode		// insert, delete, lookup
*		string name
*		int index
*		int data
*	}
*/

int
dm_db(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int opcode;			/* op requested */
	char *name = 0;		/* class name */
	int req;			/* index requested */
	int data;			/* data to put or get */
	struct mesg *mp2;

	hp = hp;

	if (upkint(mp, &opcode) || upkstralloc(mp, &name) || upkint(mp, &req))
		goto badformat;

	switch (opcode) {

	case TMDB_INSERT:
		if (upkint(mp, &data))
			goto badformat;
		if ((req = nmd_insert(name, req, data)) == -1)
			req = PvmDupEntry;
		break;

	case TMDB_DELETE:
		if ((req = nmd_delete(name, req)) == -1)
			req = PvmNoEntry;
		break;

	case TMDB_LOOKUP:
		if ((req = nmd_lookup(name, req, &data)) == -1)
			req = PvmNoEntry;
		break;

	default:
		goto badformat;
		break;
	}

	mp2 = mesg_new(0);
	mp2->m_dst = mp->m_src;
	mp2->m_cod = DM_DBACK;
	mp2->m_wid = mp->m_wid;
	pkint(mp2, req);
	pkint(mp2, data);
	sendmessage(mp2);
	PVM_FREE(name);
	return 0;

badformat:
	pvmlogerror("dm_db() bad msg format\n");

	if (name)
		PVM_FREE(name);
	return 0;
}


/*	dm_dback()
*
*	Reply to DM_DB op.
*
*	DM_DBACK(wid_rtn) {
*		int index		// and status
*		int data		// if 'lookup'
*	}
*/

int
dm_dback(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	struct waitc *wp;

	if (!(wp = wait_get(hp, mp, WT_DB)))
		return 0;

	mp->m_ref++;
	mp->m_dst = wp->wa_tid;
	mp->m_cod = TM_DB;
	sendmessage(mp);
	wait_delete(wp);
	return 0;
}


int
dm_htdel(hp, mp)
	struct hostd *hp;
	struct mesg *mp;
{
	int serial;
	int tid;

	if (hp != hosts->ht_hosts[hosts->ht_master]) {
		sprintf(pvmtxt, "dm_htdel() from t%x (not master)?\n",
				mp->m_src);
		pvmlogerror(pvmtxt);
		return 0;
	}
	if (upkint(mp, &serial)) {
		pvmlogerror("dm_htdel() bad format\n");
		return 0;
	}
	if (serial != hosts->ht_serial) {
		sprintf(pvmtxt, "dm_htdel() for serial %d, current is %d?\n",
				serial, hosts->ht_serial);
		pvmlogerror(pvmtxt);
		return 0;
	}
	while (!upkuint(mp, &tid)) {
		if (hp = tidtohost(hosts, tid)) {
			if (debugmask & PDMHOST) {
				sprintf(pvmtxt, "dm_htdel() host %s\n", hp->hd_name);
				pvmlogerror(pvmtxt);
			}
			hostfailentry(hp);
			ht_delete(hosts, hp);
		}
	}
	return 0;
}


