#include #include #include #include #include enum { Nclients = 256, }; typedef struct Cli Cli; struct Cli { ulong epoch; int fd; char* ad; char* addr; int adfd; }; int reportall = 1; ulong epoch; char* adsdir = "/lib/ndb/vol"; char* cvols; QLock clilk; Cli* cli[Nclients]; #define dprint if(debug)fprint #define Dprint if(debug>1)fprint int debug; Cli* newclient(int fd) { int i; qlock(&clilk); for (i = 0; i < nelem(cli); i++) if (cli[i] == nil) break; if (i == nelem(cli)){ qunlock(&clilk); fprint(2, "adsrv: fixme: cli table exhausted\n"); return nil; } cli[i] = malloc(sizeof(Cli)); assert(cli[i]); cli[i]->addr = nil; cli[i]->epoch = 0; cli[i]->fd = fd; cli[i]->adfd= -1; cli[i]->ad = nil; qunlock(&clilk); return cli[i]; } void delclient(Cli* c) { int i; close(c->fd); close(c->adfd); // ORCLOSE qlock(&clilk); for (i = 0; i < nelem(cli); i++) if (cli[i] == c){ if (cli[i]->ad != nil) dprint(2, "gone: %s\n", cli[i]->ad); free(cli[i]->ad); free(cli[i]); free(cli[i]->addr); cli[i] = nil; break; } qunlock(&clilk); } void writeannounces(int fd, Cli* c) { int i; qlock(&clilk); for (i = 0; i < nelem(cli); i++){ if (cli[i] != nil && cli[i]->ad != nil) if (reportall || cli[i]->epoch > c->epoch) fprint(fd, "%s\n", cli[i]->ad); } if (cvols != nil) fprint(fd, "%s", cvols); c->epoch = epoch; write(fd, "\n", 1); qunlock(&clilk); } char* readannounce(Biobuf* bin, Cli* c) { char* a; long l; char* p; char* s; char* fname; char* path; if (c->ad != nil && c->adfd != -1) return nil; fname = nil; a = Brdline(bin, '\n'); l = Blinelen(bin); if (l < 1) return nil; a[--l] = 0; if (l > 0 && a[l-1] == '\r') a[--l] = 0; if (strncmp(a, "tcp!", 4)) goto fail; s = strchr(a, '\t'); if (s == nil) goto fail; *s++ = 0; fname = strdup(a); p = strchr(fname, '!'); if (p == nil) goto fail; p++; p = strchr(p, '!'); if (p == nil) goto fail; p++; path = smprint("%s/%s", adsdir, fname); qlock(&clilk); c->epoch = ++epoch; free(c->ad); c->ad = smprint("tcp!%s!%s\t%s", c->addr, p, s); if (c->adfd != -1) close(c->adfd); c->adfd = create(path, OWRITE|ORCLOSE, 0666); if (c->adfd == -1 || fprint(c->adfd, "%s\n", c->ad) < 0){ fprint(2, "%s: %s: %r\n", argv0, s); free(c->ad); c->ad = nil; close(c->adfd); c->adfd = -1; } // leave it open. qunlock(&clilk); free(path); free(fname); return c->ad; fail: fprint(2, "bad announce: %s\n", a); free(fname); return nil; } void clientproc(void* a) { Cli* c = a; Biobuf bin; char* ln; long l; Binit(&bin, c->fd, OREAD); while(ln = Brdline(&bin, '\n')){ l = Blinelen(&bin); if (l < 2) continue; ln[--l] = 0; if (ln[l-1] == '\r') ln[--l] = 0; if (!strcmp(ln, "PlanB announces")){ Dprint(2, "write announces to %d\n", c->fd); writeannounces(c->fd, c); } else if (!strcmp(ln, "PlanB announce:")){ if (readannounce(&bin, c)) dprint(2, "announce from %d: %s\n", c->fd, c->ad); } else Dprint(2, "unknown messsage: %s\n", ln); } Dprint(2, "gone client %d: %r\n", c->fd); Bterm(&bin); delclient(c); threadexits(nil); } static char* getremotesys(char *ndir) { char buf[128], *serv, *sys; int fd, n; snprint(buf, sizeof buf, "%s/remote", ndir); sys = nil; fd = open(buf, OREAD); if(fd >= 0){ n = read(fd, buf, sizeof(buf)-1); if(n>0){ buf[n-1] = 0; serv = strchr(buf, '!'); if(serv) *serv = 0; sys = strdup(buf); } close(fd); } if(sys == nil) sys = strdup("unknown"); return sys; } void server(char* addr) { char adir[40], ldir[40]; int cfd, fd; Cli* c; cfd = announce(addr, adir); if (cfd < 0) sysfatal("%s: announce: %r", argv0); for (;;){ cfd = listen(adir, ldir); if (cfd < 0) sysfatal("%s: listen: %r", argv0); fd = accept(cfd, ldir); if(fd < 0){ fprint(2, "%s: accept: %r\n", argv0); continue; } fprint(cfd, "keepalive 2000\n"); close(cfd); c = newclient(fd); c->addr = getremotesys(ldir); Dprint(2, "new client %d %s\n", fd, c->addr); proccreate(clientproc, c, 16*1024); } sysfatal("server died"); } void usage(void) { fprint(2, "usage: %s [-d] [-n addr] [-c volcfg] [dir]\n", argv0); sysfatal("usage"); } void threadmain(int argc, char *argv[]) { char* addr; char* cfg; addr= "tcp!*!11010"; cfg = "/lib/ndb/vol/vols"; ARGBEGIN{ case 'c': cfg = EARGF(usage()); break; case 'n': addr = EARGF(usage()); break; case 'd': debug++; break; default: usage(); }ARGEND; switch(argc){ case 0: break; case 1: adsdir = argv[0]; break; default: usage(); } if (cfg != nil && access(cfg, AREAD) == 0) cvols = readfstr(cfg); dprint(2, "%s %s %s\n", argv0, adsdir, addr); server(addr); threadexits(nil); }