static char *RCSid = "$Header: tm_subr.c,v 1.9 90/03/19 11:25:09 mr-frog Exp $";

/*
 * tm_subr.c
 *
 * routines for connecting to and communicating with
 * the transaction manager.
 *
 * Dave Pare, 1986
 */

#include <errno.h>
#include <signal.h>
#include "misc.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <varargs.h>
#include "nat.h"
#include "tm.h"
#include "user.h"
#include "file.h"
#include "queue.h"
#include "ioqueue.h"
#include "io.h"
#include "bit.h"
#include "io_mask.h"

struct	iop *iop;
int	tm_answer[10];
int	tm_seq = 0;
extern	int (*leprfunc)();

static	int tm_parse();
int	tm_input();

int
tm_connect()
{
	extern	s_char serverport[];
	int	s;
	struct	sockaddr_un un;
	s_char	buf[80];

	strcpy(un.sun_path, serverport);
	un.sun_family = AF_UNIX;
	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return 0;
	}
	if (connect(s, (struct sockaddr *) &un, strlen(un.sun_path)+2) < 0) {
		perror("unix socket connect");
		return 0;
	}
	iop = io_open(s, IO_READ|IO_WRITE|IO_NBLOCK, 2048, tm_input, 0);
	(void) sprintf(buf, "%d %d %d %d\n", C_IDENT, tm_seq++, getpid(), cnum);
	io_puts(iop, buf);
	return 1;
}

tm_close()
{
	s_char	buf[80];

	(void) sprintf(buf,"%d\n",C_CLOSE);
	io_puts(iop,buf);
}

tm_lock(file)
	int	file;
{
	return tm_waitlock(C_LOCK, file, "Waiting on lock...\n");
}

tm_unlock(file)
	int	file;
{
	return tm_waitlock(C_UNLOCK, file, "Waiting to unlock...\n");
}

/*
 * XXX waitlock sets a global and waits for
 * the tm_input routine to clear them.  Ugh.
 */
static int tm_waitingfor;
static int tm_result;

int
tm_waitlock(command, file, waitstr)
	int	command;
	int	file;
	s_char	*waitstr;
{
	s_char	buf[80];
	int	n;
	struct	timeval tv;

	sprintf(buf, "%d %d 0 %d\n", command, tm_seq, file);
	io_puts(iop, buf);
	io_flush();
	tm_waitingfor = tm_seq;
	tm_result = 0;
	while (tm_result == 0) {
		tv.tv_sec = 2;
		tv.tv_usec = 0;
		n = io_select(&tv);
		if (n == 0) {
			do {
				if (leprfunc)
					leprfunc(waitstr);
				io_flush();
				tv.tv_sec = 5;
				tv.tv_usec = 0;
			} while (io_select(&tv) == 0);
		}
	}
	tm_seq++;
	return (tm_result >= 0);
}

/*ARGSUSED*/
tm_input(iop, op, assoc)
	iop_t	iop;
	int	op;
	s_char	*assoc;
{
	extern	int aborted;
	s_char	buf[80];
	int	seq;
	int	offs;

	if (io_error(iop) || io_eof(iop)) {
		tm_result = -1;
		return;
	}
	while (io_gets(iop, buf, sizeof(buf)) >= 0) {
		if (tm_parse(buf) == 0)
			continue;
		seq = tm_answer[1];
		/* tm_answer[2] (which) is ignored now */
		offs = tm_answer[3];
		switch (tm_answer[0]) {
		case TM_MOD:
			if (offs < 0) {
				ef_close(EF_NATION);
				ef_open(EF_NATION, O_RDONLY, EFF_MEM);
			} else
				natmod(buf, tm_answer);
			break;
		case TM_OK:
			if (tm_waitingfor >= 0 && seq == tm_waitingfor)
				tm_result = 1;
			break;
		case TM_ABORT:
			leprfunc("Abort sent by server\n");
			aborted = 1;
			break;
		case TM_ERROR:
			if (tm_waitingfor >= 0 && seq == tm_waitingfor)
				tm_result = -1;
			break;
		case TM_PANIC:
		default:
			/* don't worry about seq #'s on panics */
			logerror("tm_update: panic; buf %s", buf);
			tm_exit(1);
			break;
		}
	}
}

static
int
tm_parse(buf)
	s_char	*buf;
{
	s_char	*bp;
	int	i;

	bp = buf;
	for (i=0; i<10 && *bp; i++) {
		tm_answer[i] = atoip(&bp);
	}
	return i;
}

/*
 * nat_value takes a variable 5th param. The following stuff is an attempt
 * to get around machine dependencies. I think varargs handles most cases
 * of this sort of thing - its just a bit of a pain to set up!
 * JPO 5/88
 */
/*VARARGS*/
nat_value(va_alist)
	va_dcl
{
	va_list	ap;
	int	off;
	natid	coun;
	int	n;
	int	how;
	s_char	val[80];
	s_char	buf[128];
	struct	fixnat *fp;
	struct	boundstr *bnd;

	va_start (ap);
	off = va_arg(ap, int);
	coun = va_arg(ap, unsigned);	/* assumed 'natid' is an unsigned */
	n = va_arg(ap, int);
	how = va_arg(ap, int);
	if ((fp = natfield(off)) == 0) {
		logerror("nat_value: offset %d bad", off);
		return;
	}
	switch (fp->type) {
	case NF_CHAR:
		sprintf(val, "%d", va_arg(ap, int));
		break;
	case NF_UCHAR:
		sprintf(val, "%d", va_arg(ap, unsigned));
		break;
	case NF_SHORT:
		sprintf(val, "%d", va_arg(ap, int));
		break;
	case NF_LONG:
		sprintf(val, "%d", va_arg(ap, long));
		break;
	case NF_FLOAT:
		sprintf(val, "%f", va_arg(ap, double));
		break;
	case NF_DOUBLE:
		sprintf(val, "%f", va_arg(ap, double));
		break;
	case NF_STRING:
		strcpy(val, va_arg(ap, s_char *));
		break;
	case NF_BOUND:	/* pointer to struct -- portability */
		bnd = va_arg(ap, struct boundstr *);
		sprintf(val, "%d %d %d %d", bnd->b_xl,
			bnd->b_xh, bnd->b_yl, bnd->b_yh);
		break;
	default:
		logerror("bad case value %d", fp->type);
		break;
	}
	sprintf(buf, "%d -1 %d %d %d %d %d \"%s\"\n", C_NATMOD,
		EF_NATION, coun, off, n, how, val);
	io_puts(iop, buf);
	va_end(ap);
}

int
setrel(us, them, relate)
	natid	us;
	natid	them;
	int	relate;
{
	struct	natstr *np;
	int	ind;

	if ((np = getnatp(us)) == 0)
		return 0;
	putrel(np, them, relate);
#ifdef SLOW_WAR
	NAT_SETARY(nat_relate[0], us, them, np->nat_relate[them]);
#else
	ind = them / 8;
	NAT_SETARY(nat_relate[0], us, ind, np->nat_relate[ind]);
#endif /* SLOW_WAR */
	return 1;
}

int
setrej(us, them, how, what)
	natid	us;
	natid	them;
	int	how;
	int	what;
{
	struct	natstr *np;
	int	ind;

	if ((np = getnatp(us)) == 0)
		return 0;
	putreject(np, them, how, what);
	ind = them / 4;
	NAT_SETARY(nat_rejects[0], us, ind, np->nat_rejects[ind]);
	return 1;
}

#ifdef	DEMANDUPDATE
tm_updatecheck()
{
	s_char buf[80];
	(void) sprintf(buf, "%d %d \n", C_WANTUPDATE, tm_seq++);
	io_puts(iop, buf);
}
#endif	DEMANDUPDATE
