ref: 912fdf6efa65da2afce022a84f1a59e7c529f528
dir: /cons.c/
#include <u.h> #include <libc.h> #include <fcall.h> #include <avl.h> #include "dat.h" #include "fns.h" typedef struct Cmd Cmd; struct Cmd { char *name; char *sub; int minarg; int maxarg; void (*fn)(int, char**, int); }; static void setdbg(int fd, char **ap, int na) { debug = (na == 1) ? atoi(ap[0]) : !debug; fprint(fd, "debug → %d\n", debug); } static void sendsync(int fd, int halt) { Amsg *a; a = mallocz(sizeof(Amsg), 1); if(a == nil){ fprint(fd, "alloc sync msg: %r\n"); free(a); return; } a->op = AOsync; a->halt = halt; a->fd = fd; chsend(fs->admchan, a); } static void syncfs(int fd, char **, int) { sendsync(fd, 0); fprint(fd, "synced\n"); } static void haltfs(int fd, char **, int) { sendsync(fd, 1); fprint(fd, "gefs: ending...\n"); } static void snapfs(int fd, char **ap, int na) { Amsg *a; if((a = mallocz(sizeof(Amsg), 1)) == nil){ fprint(fd, "alloc sync msg: %r\n"); return; } a->op = AOsnap; a->fd = fd; if(ap[0][0] == '-'){ switch(ap[0][1]){ case 'm': a->mutable++; break; case 'd': a->delete++; break; default: fprint(fd, "usage: snap -[md] old [new]\n"); free(a); return; } na--; ap++; } if(a->delete && na != 1 || na != 2){ fprint(fd, "usage: snap -[md] old [new]\n"); free(a); return; } if(na >= 1) strecpy(a->old, a->old+sizeof(a->old), ap[0]); if(na >= 2) strecpy(a->new, a->new+sizeof(a->new), ap[1]); sendsync(fd, 0); chsend(fs->admchan, a); } static void fsckfs(int fd, char**, int) { if(checkfs(fd)) fprint(fd, "ok\n"); else fprint(fd, "fishy\n"); } static void refreshusers(int fd, char **, int) { char *e; Mount *mnt; if((mnt = getmount("adm")) == nil){ fprint(fd, "load users: missing 'adm'\n"); return; } e = loadusers(fd, mnt->root); if(e != nil) fprint(fd, "load users: %s\n", e); else fprint(fd, "refreshed users\n"); clunkmount(mnt); } static void showusers(int fd, char**, int) { User *u, *v; int i, j; char *sep; rlock(&fs->userlk); for(i = 0; i < fs->nusers; i++){ u = &fs->users[i]; fprint(fd, "%d:%s:", u->id, u->name); if((v = uid2user(u->lead)) == nil) fprint(fd, "???:"); else fprint(fd, "%s:", v->name); sep = ""; for(j = 0; j < u->nmemb; j++){ if((v = uid2user(u->memb[j])) == nil) fprint(fd, "%s???", sep); else fprint(fd, "%s%s", sep, v->name); sep = ","; } fprint(fd, "\n"); } runlock(&fs->userlk); } static void stats(int fd, char**, int) { Stats *s; s = &fs->stats; fprint(fd, "stats:\n"); fprint(fd, " cache hits: %lld\n", s->cachehit); fprint(fd, " cache lookups: %lld\n", s->cachelook); fprint(fd, " cache ratio: %f\n", (double)s->cachehit/(double)s->cachelook); } static void showdf(int fd, char**, int) { vlong size, used; double pct; Arena *a; int i; size = 0; used = 0; for(i = 0; i < fs->narena; i++){ a = &fs->arenas[i]; qlock(a); size += a->size; used += a->used; qunlock(a); } pct = 100.0*(double)used/(double)size; fprint(fd, "used: %lld / %lld (%.2f%%)\n", used, size, pct); } static void showent(int fd, char **ap, int na) { char *e, *p, *name, kbuf[Keymax], kvbuf[Kvmax]; Tree *t; Kvp kv; Key k; vlong pqid; Scan s; if((t = opensnap("main", nil)) == nil){ fprint(fd, "could not open main snap\n"); return; } pqid = strtoll(ap[0], nil, 16); name = na == 2 ? ap[1] : nil; if((p = packdkey(kbuf, sizeof(kbuf), pqid, name)) == nil){ fprint(fd, "could not pack key\n"); goto Out; } k.k = kbuf; k.nk = p - kbuf; if(name != nil){ if((e = btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf))) != nil){ fprint(fd, "lookup failed: %s\n", e); goto Out; } fprint(fd, "%P\n", &kv); }else{ btnewscan(&s, k.k, k.nk); if((e = btenter(t, &s)) != nil){ fprint(fd, "scan failed: %s\n", e); goto Out; } while(1){ if((e = btnext(&s, &kv)) != nil){ fprint(fd, "scan failed: %s\n", e); btexit(&s); goto Out; } if(s.done) break; fprint(fd, "%P\n", &kv); } btexit(&s); } Out: closesnap(t); } static void permflip(int fd, char **ap, int) { if(strcmp(ap[0], "on") == 0) permissive = 1; else if(strcmp(ap[0], "off") == 0) permissive = 0; else fprint(2, "unknown permissive %s\n", ap[0]); fprint(fd, "permissive: %d → %d\n", !permissive, permissive); } static void help(int fd, char**, int) { char *msg = "help\n" " show this help" "check\n" " run a consistency check on the file system\n" "df\n" " show disk usage stats\n" "halt\n" " stop all writers, sync, and go read-only\n" "permissive [on|off]\n" " switch to/from permissive mode\n" "snap (-d old | old new)\n" " delete, create or update a new snapshot based off old\n" "sync\n" " flush all p[ending writes to disk\n" "users\n" " reload user table from /adm/users in the main snap\n" "show\n" " show debug debug information, the following dumps\n" " are supported:\n" " cache\n" " the contents of the in-memory cache\n" " ent pqid [name]\n" " the contents of a directory entry\n" " tree [name]\n" " the contents of the tree associated with a\n" " snapshot. The special name 'snap' shows the\n" " snapshot tree\n" " snap\n" " the summary of the existing snapshots\n" " fid\n" " the summary of open fids\n" " users\n" " the known user file\n"; fprint(fd, "%s", msg); } Cmd cmdtab[] = { /* admin */ {.name="check", .sub=nil, .minarg=0, .maxarg=0, .fn=fsckfs}, {.name="df", .sub=nil, .minarg=0, .maxarg=0, .fn=showdf}, {.name="halt", .sub=nil, .minarg=0, .maxarg=0, .fn=haltfs}, {.name="help", .sub=nil, .minarg=0, .maxarg=0, .fn=help}, {.name="permissive", .sub=nil, .minarg=1, .maxarg=1, .fn=permflip}, {.name="snap", .sub=nil, .minarg=2, .maxarg=3, .fn=snapfs}, {.name="stats", .sub=nil, .minarg=0, .maxarg=0, .fn=stats}, {.name="sync", .sub=nil, .minarg=0, .maxarg=0, .fn=syncfs}, {.name="users", .sub=nil, .minarg=0, .maxarg=1, .fn=refreshusers}, /* debugging */ {.name="show", .sub="cache", .minarg=0, .maxarg=0, .fn=showcache}, {.name="show", .sub="dlist", .minarg=0, .maxarg=0, .fn=showdlist}, {.name="show", .sub="ent", .minarg=1, .maxarg=2, .fn=showent}, {.name="show", .sub="fid", .minarg=0, .maxarg=0, .fn=showfid}, {.name="show", .sub="free", .minarg=0, .maxarg=0, .fn=showfree}, {.name="show", .sub="snap", .minarg=0, .maxarg=1, .fn=showsnap}, {.name="show", .sub="tree", .minarg=0, .maxarg=1, .fn=showtree}, {.name="show", .sub="users", .minarg=0, .maxarg=0, .fn=showusers}, {.name="debug", .sub=nil, .minarg=0, .maxarg=1, .fn=setdbg}, {.name=nil, .sub=nil}, }; void runcons(int tid, void *pfd) { char buf[256], *f[4], **ap; int i, n, nf, na, fd; Cmd *c; fd = (uintptr)pfd; while(1){ fprint(fd, "gefs# "); if((n = read(fd, buf, sizeof(buf)-1)) == -1) break; epochstart(tid); buf[n] = 0; nf = tokenize(buf, f, nelem(f)); if(nf == 0 || strlen(f[0]) == 0) continue; for(c = cmdtab; c->name != nil; c++){ ap = f; na = nf; if(strcmp(c->name, *ap) != 0) continue; ap++; na--; if(c->sub != nil){ if(na == 0 || strcmp(c->sub, *ap) != 0) continue; ap++; na--; } if(na < c->minarg || na > c->maxarg) continue; c->fn(fd, ap, na); break; } if(c->name == nil){ fprint(fd, "unknown command '%s", f[0]); for(i = 1; i < nf; i++) fprint(fd, " %s", f[i]); fprint(fd, "'\n"); } epochend(tid); } }