Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/cmd/hxfs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <b.h>
#include <thread.h>
#include <fcall.h>
#include <auth.h>
#include <9p.h>
#include <bio.h>

typedef struct Pos Pos;
typedef struct Loc Loc;
typedef struct Badge Badge;
typedef struct Name Name;

enum {
	Qdir,
	Qbadge,

	Ntoks	= 32,
	Interval= 2,	// poll interval, in seconds.
	Nlocs	= 3,	// # of locations kept per badge
	Nbadges	= 16,	// Max # of badges
	Nnames	= 128,	// Max # of names (badges+locations)
	Troam	= 5,	// time (secs) to forget old location
	Told	= 120,	// time (secs) to forget last location
	Tdial	= 15,	// time (secs) to retry a dial for srv.
	Stack	= 16 * 1024,

	Esc	= 27,

};

struct Pos {
	int	badge;
	int	loc;
};

struct Loc {
	int	loc;
	int	time;
};

struct Badge {
	int	id;
	Loc	locs[Nlocs];
};

struct Name {
	int	id;
	char*	name;
};

#define dprint	if(debug)fprint
static int	debug;
static char*	ctladdr;
static Channel*	posc;
static Channel*	fsc;
static Badge	badges[Nbadges];
static Name*	bnames[Nnames];
static int	nbnames;
static Name*	mnames[Nnames];
static int	nmnames;
static File*	slash;
static int	iofd = -1;
static char*	addr;
static char*	vname;

int mainstacksize = 32*1024;

int
fileclass(File* fp)
{
	return ((fp->qid.path&0xff000000)>>24);
}

/* A badge may be seen by several receivers, and
 * may be at several locations. It may be unnoticed
 * for a while, so it takes a while to assume that
 * its last location is no longer valid.
 * We keep the last Nlocs locations for each badge.
 * We forget those older than Told secs.
 *
 * Badges and locations are added into the data
 * structure and never removed. Files for badges keep
 * a pointer into the corresponding badge. Their content
 * appears to be the set of active locations (most recent first)..
 */

static void
addname(int id, char* s)
{
	Name*	n;

	n = emalloc9p(sizeof(Name));
	n->id = id;
	n->name= s;

	dprint(2, "addname: %d %s\n", id, s);
	if (id < 1000){
		if (nbnames >= nelem(bnames))
			sysfatal("too many names");
		bnames[nbnames++] = n;
	} else {
		if (nmnames >= nelem(mnames))
			sysfatal("too many names");
		mnames[nmnames++] = n;
	}
}


static char*
id2name(int id)
{
	int	i;
	Name**	n;
	int	nn;

	if (id < 1000){
		n = bnames;
		nn= nbnames;
	} else {
		n = mnames;
		nn= nmnames;
	}
	for (i = 0; i < nn; i++)
		if (n[i]->id == id)
			return n[i]->name;
	return nil;
}

static void
dumpnames(void)
{
	int	i;

	fprint(2, "badges:\n");
	for (i = 0; i < nbnames; i++)
		fprint(2, "0x%x -> %s\n", bnames[i]->id, bnames[i]->name);
	fprint(2, "monitors:\n");
	for (i = 0; i < nmnames; i++)
		fprint(2, "0x%x -> %s\n", mnames[i]->id, mnames[i]->name);
}

static void
dumpbadges(void)
{
	int	i, j;
	char*	n;

	fprint(2, "locations:\n");
	for (i = 0; i < nelem(badges); i++)
		if (badges[i].id){
			n = id2name(badges[i].id);
			if (n == nil)
				continue;
			fprint(2,"\t %s:\t", n);
			for (j = 0; j < Nlocs; j++){
				if (badges[i].locs[j].time){
					n = id2name(badges[i].locs[j].loc);
					if (n)
						fprint(2, " %s", n);
					else
						fprint(2, " unknown");
				}
			}
			fprint(2,"\n");
		}
	fprint(2,"\n");
}

static void
fsread(Req* r)
{
	File* file;
	int	id;
	Badge*	b;
	char	buf[80];
	char*	s;
	int	i;
	char*	n;

	if (r->fid->qid.type&QTAUTH){
		authread(r);
		return;
	}
	file = r->fid->file;
	id = fileclass(file);
	switch(id){
	case Qbadge:
		rlock(file);
		b = file->aux;
		assert(b);
		s = buf;
		for (i = 0; i < Nlocs; i++){
			if (b->locs[i].loc){
				n = id2name(b->locs[i].loc);
				if (n)
					s = seprint(s, buf+80, "%s\n", n);
				else
					s = seprint(s, buf+80, "unknown\n");
			}
		}
		if (s == buf)
			strcpy(buf, "none\n");
		runlock(file);
		readstr(r, buf);
		respond(r, nil);
		break;
	default:
		respond(r, "bug: bad id in fspread");
	}
}

static void
fsattach(Req* r)
{
	if (r->srv->auth != nil && authattach(r) < 0)
		return;
	respond(r, nil);
}

static void
fsopen(Req* r)
{
	int	mode;

	if (r->fid->qid.type&QTAUTH)
		sysfatal("fsopen for an AUTH file. fixme: add respond/return\n");
	mode = (r->ifcall.mode&3);
	if (mode != OREAD)
		respond(r, "permission denied");
	else
		respond(r, nil);
}

static void
fswrite(Req* r)
{
	if (r->fid->qid.type&QTAUTH)
		authwrite(r);
	else
		respond(r, "permission denied");
}

static void
fsclean(Srv* s)
{
	Channel* rc;

	rc = s->aux;
	if (rc)
		chanfree(rc);
}
	
static void
fssend(Req* r)
{
	Channel* rc;

	
	if (r->srv->aux == nil)
		r->srv->aux = chancreate(sizeof(ulong), 0);
	rc = r->srv->aux;
	sendp(fsc, r);
	recvul(rc);
}

static void
fsclunk(Fid* fid)
{

	if (fid->qid.type&QTAUTH)
		authdestroy(fid);
}

static Srv sfs=
{
	.auth	=auth9p,
	.attach	=fsattach,
	.open	=fsopen,
	.write = fswrite,
	.read  = fssend,
	.end	= fsclean,
	.destroyfid = 	fsclunk,
};

static int
loccmp(void* l1, void* l2)
{
	Loc* l1p = l1;
	Loc* l2p = l2;

	return l2p->time - l1p->time;
}

static void
sortlocs(Badge* b)
{
	qsort(b->locs, Nlocs, sizeof(b->locs[0]), loccmp);
}

static void
collectold(void)
{
	int	i, j;
	long	now;
	long	dt;

	now = time(nil);
	for (i = 0; i < nelem(badges); i++)
		if (badges[i].id){
			for (j = 0; j < Nlocs; j++){
				if (badges[i].locs[j].time){
					dt = now - badges[i].locs[j].time;
					if (j > 0 && dt > Troam){
						badges[i].locs[j].time = 0;
						badges[i].locs[j].loc = 0;
					}
					if (j == 0 && dt > Told){
						badges[i].locs[j].time = 0;
						badges[i].locs[j].loc = 0;
					}
				}
			}
			sortlocs(badges+i);
		}
}

static void
updatebadge(Badge* b, int loc)
{
	int	old;
	int	i;
	long	now;

	now = time(nil);
	for (old = i = 0; i < Nlocs; i++){
		if (b->locs[i].loc == loc){
			old = i;
			break;
		}
		if (b->locs[old].time > b->locs[i].time)
			old = i;
	}
	b->locs[old].time = now;
	b->locs[old].loc = loc;
	sortlocs(b);
}

static void
addfile(Badge* b)
{
	File*	f;
	char*	n;

	n = id2name(b->id);
	if (n){
		f = createfile(slash, n, "sys", 0444, b);
		f->qid.path |= ((Qbadge&0xff)<<24);
		closefile(f);
	}
}

static void
updatepos(Pos* p)
{
	int	i;

	for (i = 0; i < nelem(badges); i++){
		if (badges[i].id == p->badge){
			updatebadge(badges+i, p->loc);
			return;
		}
	}
	for (i = 0; i < nelem(badges); i++){
		if (!badges[i].id){
			badges[i].id = p->badge;
			updatebadge(badges+i, p->loc);
			addfile(badges+i);
			return;
		}
	}
	fprint(2, "%s: too many badges; ignoring %x\n", argv0, p->badge);
}

static void
parsepos(char* s)
{
	char	mname[] = "0xXXXX";
	char	bname[] = "0xXXXX";
	char*	p;
	Pos	pos;

	p = strchr(s, ' ');
	if (p == nil || p - s != 4){
		dprint(2, "bad monitor name: %s\n", s);
		return;
	}
	*p++ = 0;
	strcpy(mname + 2, s);
	pos.loc = strtol(mname, nil, 0);
	while(p && *p){
		s = p;
		p = strchr(s, ' ');
		if (p == nil)
			break;
		*p = 0;
		if (p - s < 6){
			dprint(2, "bad badge name: %s\n", s);
			break;
		}
		p++;
		strncpy(bname + 2, s, 4);
		pos.badge = strtol(bname, nil, 0);
		dprint(2, "badge %d at %d\n", pos.badge, pos.loc);
		send(posc, &pos);
	}
}

static int
hxcmd(int fd, char cmd, char ans)
{
	char	buf[1];
	int	i, n;

	i = 0;
	do {
		if (write(fd, &cmd, 1) != 1)
			return 0;
		if (ans == 0)
			break;
		n = read(fd, buf, sizeof(buf));
		if (n <= 0)
			return 0;
		if (buf[0] != ans){
			if(0)dprint(2, "%s: cmd %c: bad answer '%c' 0x%x\n",
				argv0, cmd, buf[0], buf[0]);
			continue;
		}
	} while (buf[0] != ans && i++ < 100);
	if (i == 100){
		werrstr("HX5C not responding");
		return 0;
	}
	return 1;
}

static void
hxinit(int fd)
{
	hxcmd(fd, Esc, 0);
}

static uchar*
PUTS(uchar* p, int s, int* sum)
{
	p[0] = ((s >> 8) & 0xff);
	*sum = ((*sum + p[0]) & 0xff);
	p[1] = (s & 0xff);
	*sum = ((*sum + p[1]) & 0xff);
	return p + 2;
}

static uchar*
PUTB(uchar* p, int s, int* sum)
{
	p[0] = (s & 0xff);
	*sum = ((*sum + p[0]) % 256);
	return p + 1;
}

static void
dumpbuf(uchar* buf, int n)
{
	int	i;

	for (i = 0; i < n; i++)
		print("%c ", buf[i]);
	print("\n");
}

static int
hxconfig(int fd)
{
	uchar	buf[4096];
	uchar*	p;
	int	sum;
	int	i;
	char	cmd;

	cmd = '$';
	hxcmd(fd, cmd, '!');
	sum = 0;
	p = buf;
	p = PUTS(p, 2*nmnames + 1, &sum);	// size
	p = PUTB(p, Interval, &sum);
	for (i = 0; i < nmnames; i++)
		p = PUTS(p, mnames[i]->id, &sum);
	p = PUTB(p, sum, &sum);

	dprint(2, "wrote: ");
	if (debug)
		dumpbuf(buf, p-buf);
	dprint(2, "\n\n");
	if (write(fd, buf, p - buf) != p - buf)
		sysfatal("write: %r\n");
	read(fd, buf, 1);
	if (buf[0] != '+')
		return 0;
	return 1;
}

static void
consume(char* buf)
{
	char*	toks[Ntoks];
	char	delims[2];
	int	ntoks;
	int	i;
	int	l;

	delims[0] = 0xd;
	delims[1] = 0;
	ntoks = gettokens(buf, toks, nelem(toks), delims);
	for (i = 0; i < ntoks; i++)
		if (toks[i] && *toks[i]){
			l = strlen(toks[i]);
			if (toks[i][l-1] != '#' || l < 5)
				continue;
			if(0)dprint(2, "got '%s'\n", toks[i]);
			toks[i][l-1] = 0;
			parsepos(toks[i]);
		}
}

static void
hxio(void* )
{
	uchar	buf[1024];
	int	n;
	int	nr;

	threadsetname("hxio");
	n = 0;
	for(;;){
		nr = read(iofd, buf + n, sizeof(buf) - n);
		if (nr <= 0)
			sysfatal("read: %r\n");
		if (0 && debug)
			dumpbuf(buf, nr);
		n += nr;
		if (n >= sizeof(buf)-1 || buf[n-1] == 0x0d){
			buf[n] = 0;
			consume((char*)buf);
			n = 0;
		}
	}
	threadexits(nil);
}

static int
hxopen(char* file)
{
	int	fd;
	char*	cfname;
	int	cfd;

	fd = open(file, ORDWR);
	if (fd < 0)
		fprint(2, "cant' open %s: %r\n", file);
	cfname = smprint("%sctl", file);
	assert(cfname);
	cfd = open(cfname, OWRITE);
	if (cfd >= 0){
		fprint(cfd, "b19200 l8 pn s1\n");
		close(cfd);
	}
	free(cfname);
	return fd;
}

static void
io(void*)
{
	Channel*rc;
	Pos	p;
	Req*	r;
	Alt	a[] = {
		{ posc, &p, CHANRCV },
		{ fsc,  &r, CHANRCV },
		{ 0, 0, CHANEND }};

	threadsetname("hxio");
	for(;;){
		switch(alt(a)){
		case 0:
			updatepos(&p);
			if (debug)
				dumpbadges();
			collectold();
			break;
		case 1:
			rc = r->srv->aux;
			fsread(r);
			sendul(rc, 0);
			break;
		default:
			sysfatal("io: recv");
		}
	}
	threadexits(nil);
}

/*
 * The file contains words of the form:
 *	id=name
 * Usually, one per line.
 * No comments or any other fancy stuff.
 */
static void
config(char* fname)
{
	char*	cfg;
	char*	toks[Nnames];
	int	ntoks;
	char*	s;
	int	i;

	cfg = readfstr(fname);
	if (!cfg)
		sysfatal("can't read config");
	ntoks = tokenize(cfg, toks, nelem(toks));
	for (i = 0; i < ntoks; i++){
		s=strchr(toks[i], '=');
		if (s){
			*s++ = 0;
			addname(strtol(toks[i], nil, 10), s);
		}
	}
	// We leak cfg because addname takes the given string
	// and does not make a dup. This is more simple.
}

static void
announceproc(void*)
{
	int	afd = -1;

	for(;;){
		afd = announcevol(afd, addr, vname, nil);
		sleep(10 * 1000);
	}
}


void
usage(void)
{
	fprint(2, "usage: %s [-Ad] [-s srv] [-m mnt] [-n addr] [-c cfg] [-V vol] [iofile]\n", argv0);
	exits("usage");
}

void
threadmain(int argc, char **argv)
{
	char*	mnt;
	char*	srv;
	char*	cfg;
	int	i;

	srv = nil;
	mnt = nil;
	addr = nil;
	cfg = "/sys/lib/hxconf";
	ARGBEGIN{
	case 'A':
		sfs.auth = nil;
		break;
	case 'd':
		if (debug++)
			chatty9p++;
		break;
	case 's':
		srv = EARGF(usage());
		break;
	case 'm':
		mnt = EARGF(usage());
		break;
	case 'n':
		addr = EARGF(usage());
		break;
	case 'c':
		cfg =  EARGF(usage());
		break;
	case 'V':
		vname = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;

	switch(argc){
	case 0:
		ctladdr = "/dev/eia0";
		break;
	case 1:
		ctladdr = argv[0];
		break;
	default:
		usage();
	}
	config(cfg);
	if (debug)
		dumpnames();
	else
		rfork(RFNOTEG);

	iofd = hxopen(ctladdr);
	if (iofd < 0)
		sysfatal("hxopen");
	for (i = 0; i < 3; i++){
		hxinit(iofd);
		if (hxconfig(iofd))
			break;
		sleep(1000);
	}
	if (i == 3)
		sysfatal("can't config hx");

	if (getenv("local"))
		sfs.auth = nil;
	if (srv == nil && mnt == nil && addr == nil)
		addr="tcp!*!11008";
	sfs.tree =  alloctree(nil, nil, DMDIR|0555, nil);
	slash = sfs.tree->root;
	incref(slash);
	posc = chancreate(sizeof(Pos), 5);
	fsc = chancreate(sizeof(Req*), 5);
	proccreate(io, nil, Stack);
	proccreate(hxio, nil, Stack);
	if (addr != nil)
		threadlistensrv(&sfs, addr);
	if (srv != nil || mnt != nil)
		threadpostmountsrv(&sfs, srv, mnt, MREPL|MCREATE);
	if (addr != nil && vname != nil)
		proccreate(announceproc, 0, 8*1024);
	threadexits(nil);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.