#include #include #include #include #include #include #include <9p.h> #include 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); }