/* * Plan B volume file system server. * * Is Plan B safe? * * When used as directed, Plan B is safe for most women. There have been * no serious complications associated with Plan B. Common side effects * include nausea, abdominal pain, fatigue, headache, and menstrual * changes. Women who are pregnant, have undiagnosed vaginal bleeding, * or have an allergy to the product should not use Plan B. Plan B cannot * terminate an established pregnancy * * How effective is Plan B? * * Taken within 72 hours of unprotected intercourse, Plan B can, when * used correctly, reduce the risk of pregnancy by 89 percent after a * single act of unprotected sex. Effectiveness declines as the interval * between intercourse and the start of treatment increases. * * http://www.go2planb.com * * How to use Plan B? * * This replaces the old vold (with the discovery protocol) * and the kernel volume mechanism. * * When supplied a volume spec it provides * a file tree that corresponds to a volume instance, which one, * it depends on the set of volumes known. * * This translates the client RPCs to RPCs for the appropriate fs. * Using the local kernel as a client was beareable but a bit slow. * * 5.48u 4.30s 49.72r mk at /sys/src/9/pc volfs * 5.45u 5.40s 43.68r mk at /sys/src/9/pc planb 3rd ed. */ #include #include #include #include #include #include #include #include #include "names.h" #include "vols.h" typedef struct Work Work; struct Work { Frpc* rpc; // being serviced int nrpcs; // serviced Channel*c; // where to send requests }; int debug, vdebug; /* general debug (xtra if > 1); volume debug */ static int srvfd = -1; static int ctlfd = -1; static Channel* reqc; static Channel* repc; static Channel* tmrc; static char* sname; static char* csname; static int dodiscover; static Work work[Nthreads]; static char* adsaddr; /* When feasible, we use the client's Frpc to issue all * our rpcs to the server. This means that some routines * in volfs.c and in vols.c receive an fop parameter. * (A benefit of this is that we can use the client tags * as our tags for RPCs made to servers. Another benefit * is that we avoid extra allocation of Frpcs.) * Beware that such routines will overwrite whatever * is in the fop to issue appropriate rpcs. However, * tag is guaranteed to remain untouched. */ static QLock rpclck; static Frpc* freerpcs; static int nrpcs; // #rpcs allocated. Frpc* rpcalloc(void) { int x[1]; Frpc* m; Channel*sc; qlock(&rpclck); if (freerpcs == nil){ nrpcs++; m = emalloc(sizeof(Frpc)); setmalloctag(m, getcallerpc(x+2)); memset(m, 0, sizeof(Frpc)); m->sc = chancreate(sizeof(ulong), 0); } else { m = freerpcs; freerpcs = m->next; sc = m->sc; memset(m, 0, sizeof(Frpc)); m->sc = sc; } if (nrpcs > Nrpcs && !(nrpcs%20)) fprint(2, "%s: warning: #%d allocated rpcs\n", argv0, nrpcs); qunlock(&rpclck); return m; } void rpcfree(Frpc* m) { if (m == nil) return; if (m->time == 0xbbbb){ fprint(2, "rpcfree: double free for %p\n", m); abort(); } m->time = 0xbbbb; rpcfree(m->rep); m->rep = nil; qlock(&rpclck); free(m->d); m->d = nil; m->next = freerpcs; freerpcs = m; qunlock(&rpclck); } static void flushtag(int tag) { static int nflushed; int i; for (i = 0; i < nelem(work); i++){ if (work[i].rpc && work[i].rpc->f.tag == tag) break; } if (i == nelem(work)){ dprint(2, "flush: no tag %d (%d flushes)\n", tag, nflushed++); return; } work[i].rpc->flushed = 1; work[i].rpc->time = 0; } static void workthread(void* a) { Work* w = a; Frpc* m; threadsetname("work"); while(m = recvp(w->c)){ w->nrpcs++; assert(m->f.type >= 0 && m->f.type < Tmax); assert(fscalls[m->f.type]); w->rpc = m; fscalls[m->f.type](m); if (m->f.type == Tflush) flushtag(m->f.oldtag); sendp(repc, m); w->rpc = nil; } threadexits(nil); } static void replythread(void *) { Frpc* m; Fid* df; threadsetname("replythread"); df = emalloc(sizeof(Fid)); while(m = recvp(repc)){ if (m->fid && m->fid->debug) df->debug = m->fid->debug; else df->debug = 0; fidfree(m->freefid, m); if (m->flushed){ dprint(2, "flushed reply (type %d, tag %d) ignored\n", m->r.type, m->r.tag); } else { m->fid = df; if (putfrep(srvfd, m) < 0) fprint(2, "%s: reply: %r\n", argv0); } rpcfree(m); } sysfatal("no output process"); } static void discover(char* addr) { static char* raddr = nil; static int fd = -1; char dir[50]; NetConnInfo*ni; if (fd < 0) if (raddr != nil) fd = dial(netmkaddr(raddr, nil, "ads"), 0, 0, 0); else fd = dial(netmkaddr(addr, nil, "ads"), 0, dir, 0); if (fd < 0){ fprint(2, "%s: %s: %r\n", argv0, addr); } else { if (raddr == nil){ ni = getnetconninfo(dir, fd); if (ni != nil){ raddr = estrdup(ni->raddr); free(ni); } } // This print is ignored by old protocol // the new one uses it as a request to dump announces. if (fprint(fd, "PlanB announces\n") < 0){ close(fd); fd = -1; // We'll try reopening next time they call us. } else fdconfig(fd, addr); } } static void timerthread(void*) { ulong now; ulong last; threadsetname("timers"); last = 0; for(;;){ now = recvul(tmrc); fstimer(now); if (dodiscover && adsaddr && now - last > Disctick){ discover(adsaddr); last = time(nil); } } } static void stats(void) { int i; int nused; fprint(2, "%d rpcs allocated %d workers ", nrpcs, nelem(work)); nused = 0; for (i = 0; i < nelem(work); i++){ if (work[i].nrpcs) nused++; } fprint(2, "%d idle\n", nelem(work) - nused); } static void ctlproc(void*) { static Biobuf bcmd; char* ln; char* c; threadsetname("ctlproc"); Binit(&bcmd, ctlfd, OREAD); while(ln = Brdstr(&bcmd, '\n', 1)){ switch(ln[0]){ case 'V': fprint(2, "Debug dump:\n"); dumpvols(); dumpfss(); dumpmvols(); dumpfids(); break; case 'S': fprint(2, "Debug dump:\n"); stats(); fsstats(); break; case 'D': debug = vdebug = 0; for(c = ln+1; *c; c++) switch(*c){ case 'n': break; case 'D': debug = 2; break; case 'd': debug = 1; break; case 'v': vdebug = 1; break; } break; case 'T': c = strchr(ln+1, ' '); if (c) *c = 0; c = strchr(ln+1, '\n'); if (c) *c = 0; if (ln[1] == 'T') tracemvol(ln+2, 2); else tracemvol(ln+1, 1); break; case 'N': tracemvol(nil, 0); break; default: cmdline(ln, "/srv/volctl", 0); } free(ln); } Bterm(&bcmd); exits(nil); } static void dispatchproc(void*) { Frpc* m; int i; int rr; Frpc* fop; threadsetname("dispatcher"); for (i = 0; i < Nthreads; i++) threadcreate(workthread, work+i, Wstack); threadcreate(replythread, nil, Stack); threadcreate(timerthread, nil, Stack); rr = 0; fop = rpcalloc(); fop->f.tag = 1; while(m = recvp(reqc)){ checkvols(fop); for (i = 0; i < Nthreads; i++){ if (work[i].rpc == nil){ sendp(work[i].c, m); break; } } if (i == Nthreads){ dprint(2, "%s: all workers busy\n", argv0); sendp(work[rr].c, m); rr = (rr + 1) % Nthreads; } } threadexits(nil); } static void timerproc(void*) { ulong now; threadsetname("timerproc"); for(;;){ sleep(Tmout*1000); now = time(nil); nbsendul(tmrc, now); // time does not block! } } static void srvinproc(void *) { Frpc* m; threadsetname("srvinproc"); for(;;){ m = rpcalloc(); if (getfcall(srvfd, m) <= 0){ rpcfree(m); fprint(2, "%s: getfcall: %r\n", argv0); break; } sendp(reqc, m); } sysfatal("no input process"); } static char* mkadsaddr(void) { char* r; char* s; s = getenv("fileserver"); if (s == nil) return smprint("tcp!193.147.71.86!11010"); r = smprint("tcp!%s!11010", s); free(s); return r; } /* To prevent deadlocks, bns runs with just this: * /mnt/factotum /net /dev /env and /srv */ static void cleanns(void) { int fd; if (bind("#/", "/", MREPL) < 0) fprint(2, "FAIL> bind /: %r\n"); fd = open("#s/factotum", ORDWR); mount(fd, -1, "/mnt", MREPL|MCREATE, ""); close(fd); if (bind("#I","/net", MAFTER) < 0) fprint(2, "cannot bind /net: %r\n"); if (access("/net/tcp/clone", AEXIST) < 0) fprint(2, "NO TCP\n"); if (bind("#c","/dev", MREPL|MCREATE) < 0) fprint(2, "cannot bind /dev: %r\n"); if (bind("#s","/srv", MREPL|MCREATE) < 0) fprint(2, "cannot bind /srv: %r\n"); if (bind("#r","/dev", MAFTER|MCREATE) < 0) fprint(2, "cannot bind #r: %r\n"); bind("#e","/env", MREPL|MCREATE); } static void mainproc(void* a) { int fd[2]; int cfd[2]; int i; Channel*c = a; threadsetname("mainproc"); cleanns(); config(nil); if (pipe(fd)<0 || pipe(cfd) < 0) sysfatal("pipe: %r"); if (postfd(sname, fd[0]) < 0) sysfatal("can't post srv: %s: %r", sname); csname = smprint("%sctl", sname); if (postfd(csname, cfd[0]) < 0) sysfatal("can't post srv: %s: %r", csname); rfork(RFFDG); close(fd[0]); close(cfd[0]); srvfd = fd[1]; ctlfd = cfd[1]; reqc = chancreate(sizeof(Frpc*), 0); for (i = 0; i < Nthreads; i++) work[i].c = chancreate(sizeof(Frpc*), 0); repc = chancreate(sizeof(Frpc*), 5); tmrc = chancreate(sizeof(ulong), 0); assert(reqc && repc); if (proccreate(ctlproc, nil, Stack) < 0) sysfatal("procrfork: %r"); if (proccreate(timerproc, nil, Stack) < 0) sysfatal("procrfork: %r"); if (proccreate(srvinproc, nil, Stack) < 0) sysfatal("procrfork: %r"); if (proccreate(dispatchproc, nil, Stack) < 0) sysfatal("procrfork: %r"); free(csname); sendul(c, 0); threadexits(nil); } static void usage(void) { fprint(2,"usage: %s [-DHdv] [-l] [-s srv]\n", argv0); exits("usage"); } void threadmain(int argc, char *argv[]) { Channel* sc; int domnt; extern int newnsdebug; sname = "vol"; adsaddr = nil; domnt = 1; ARGBEGIN { case 'H': hdebug++; break; case 'D': case 'd': debug++; break; case 'v': vdebug++; break; case 'l': dodiscover++; break; case 's': domnt = 0; sname = EARGF(usage()); break; default: usage(); } ARGEND; threadsetname("main"); if (debug > 2) mainmem->flags |= POOL_PARANOIA; if (argc > 0) usage(); adsaddr = mkadsaddr(); quotefmtinstall(); fmtinstall('D', dirfmt); fmtinstall('M', dirmodefmt); fmtinstall('F', fcallfmt); fmtinstall('V', Vfmt); fmtinstall('W', Wfmt); fmtinstall('X', fidfmt); fmtinstall('I', eipfmt); fmtinstall('N', namefmt); fmtinstall('K', Kfmt); fmtinstall('k', kfmt); exprinit(); sc = chancreate(sizeof(ulong), 0); procrfork(mainproc, sc, Stack, RFNOTEG|RFCNAMEG|RFENVG); recvul(sc); chanfree(sc); if (debug>1 || vdebug>1) newnsdebug = 1; if (domnt){ newns(getuser(), "/lib/namespace.planb"); print("starting brc\n"); procexecl(nil, "/rc/bin/brc", "brc", nil); print("brc: %r\nstarting rc\n"); procexecl(nil, "/bin/rc", "rc", "-i", nil); } threadexits(nil); }