ref: c36ed06ba0fd376ca0e346d0639d8cbf18c1cb45
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)
{
Fmsg *m;
Amsg *a;
m = mallocz(sizeof(Fmsg), 1);
a = mallocz(sizeof(Amsg), 1);
if(m == nil || a == nil){
fprint(fd, "alloc sync msg: %r\n");
free(m);
free(a);
return;
}
a->op = AOsync;
a->halt = halt;
a->fd = fd;
m->a = a;
chsend(fs->wrchan, m);
}
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)
{
Fmsg *m;
Amsg *a;
m = mallocz(sizeof(Fmsg), 1);
a = mallocz(sizeof(Amsg), 1);
if(m == nil || a == nil){
fprint(fd, "alloc sync msg: %r\n");
goto Error;
}
if(strcmp(ap[0], ap[1]) == 0){
fprint(fd, "not a new snap: %s\n", ap[1]);
goto Error;
}
if(strcmp(ap[0], "-d") == 0){
strecpy(a->old, a->old+sizeof(a->old), ap[1]);
a->new[0] = 0;
}else{
strecpy(a->old, a->old+sizeof(a->old), ap[0]);
strecpy(a->new, a->new+sizeof(a->new), ap[1]);
}
a->op = AOsnap;
a->fd = fd;
m->a = a;
sendsync(fd, 0);
chsend(fs->wrchan, m);
return;
Error:
free(m);
free(a);
return;
}
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;
Tree *t;
if((t = opensnap("adm")) == nil){
fprint(fd, "load users: missing 'adm'\n");
return;
}
e = loadusers(fd, t);
if(e != nil)
fprint(fd, "load users: %s\n", e);
else
fprint(fd, "refreshed users\n");
closesnap(t);
}
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];
lock(a);
size += a->size;
used += a->used;
unlock(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){
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(t, &s, &kv)) != nil){
fprint(fd, "scan failed: %s\n", e);
btexit(t, &s);
goto Out;
}
if(s.done)
break;
fprint(fd, "%P\n", &kv);
}
btexit(t, &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=2, .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);
}
}