ref: 09112b83f8191e1567c06f27ba797fd690024396
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
listsnap(int fd)
{
char pfx[Snapsz];
Scan s;
uint flg;
int sz;
pfx[0] = Klabel;
sz = 1;
btnewscan(&s, pfx, sz);
btenter(&fs->snap, &s);
while(1){
if(!btnext(&s, &s.kv))
break;
flg = UNPACK32(s.kv.v+1+8);
fprint(fd, "snap %.*s", s.kv.nk-1, s.kv.k+1);
if(flg & Lmut)
fprint(fd, " mutable");
if(flg & Lauto)
fprint(fd, " auto");
if(flg & Lnoauto)
fprint(fd, " auto=off");
fprint(fd, "\n");
}
btexit(&s);
}
static void
snapfs(int fd, char **ap, int na)
{
Amsg *a;
int i;
if((a = mallocz(sizeof(Amsg), 1)) == nil){
fprint(fd, "alloc sync msg: %r\n");
return;
}
a->op = AOsnap;
a->fd = fd;
while(ap[0][0] == '-'){
for(i = 1; ap[0][i]; i++){
switch(ap[0][i]){
case 'S': a->flag |= Lnoauto; break;
case 'm': a->flag |= Lmut; break;
case 'd': a->delete++; break;
case 'l':
listsnap(fd);
free(a);
return;
default:
fprint(fd, "usage: snap -[Smdl] [old [new]]\n");
free(a);
return;
}
}
na--;
ap++;
}
if(a->delete && na != 1 || !a->delete && 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, "broken\n");
}
static void
refreshusers(int fd, char **, int)
{
Mount *mnt;
if((mnt = getmount("adm")) == nil){
fprint(fd, "load users: missing 'adm'\n");
return;
}
if(waserror()){
fprint(fd, "load users: %s\n", errmsg());
clunkmount(mnt);
return;
}
loadusers(fd, mnt->root);
fprint(fd, "refreshed users\n");
clunkmount(mnt);
}
static void
showbstate(int fd, char**, int)
{
char *p, fbuf[8];
Blk *b;
for(b = blkbuf; b != blkbuf+fs->cmax; b++){
p = fbuf;
if(b->flag & Bdirty) *p++ = 'd';
if(b->flag & Bfinal) *p++ = 'f';
if(b->flag & Bfreed) *p++ = 'F';
if(b->flag & Bcached) *p++ = 'c';
if(b->flag & Bqueued) *p++ = 'q';
if(b->flag & Blimbo) *p++ = 'L';
*p = 0;
fprint(fd, "blk %#p type %d flag %s bp %B ref %ld alloc %#p queued %#p, hold %#p drop %#p cached %#p\n",
b, b->type, fbuf, b->bp, b->ref, b->alloced, b->queued, b->lasthold, b->lastdrop, b->cached);
}
}
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
showdf(int fd, char**, int)
{
char *units[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", nil};
vlong size, used;
double hsize, hused;
double pct;
Arena *a;
int i, us, uu;
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);
fprint(fd, "arena %d: %llx/%llx (%.2f)\n", i, a->used, a->size, (double)a->used/(double)a->size);
}
hsize = size;
hused = used;
for(us = 0; us < nelem(units)-1 && hsize >= 500 ; us++)
hsize /= 1024;
for(uu = 0; uu < nelem(units)-1 && hused >= 500 ; uu++)
hused /= 1024;
pct = 100.0*(double)used/(double)size;
fprint(fd, "fill:\t%.2f%%\n", pct);
fprint(fd, "used:\t%lld (%.2f %s)\n", used, hused, units[uu]);
fprint(fd, "avail:\t%lld (%.2f %s)\n", size, hsize, units[us]);
}
void
showfid(int fd, char**, int)
{
int i;
Fid *f;
Conn *c;
for(c = fs->conns; c != nil; c = c->next){
fprint(fd, "fids:\n");
for(i = 0; i < Nfidtab; i++){
lock(&c->fidtablk[i]);
for(f = c->fidtab[i]; f != nil; f = f->next){
rlock(f->dent);
fprint(fd, "\tfid[%d] from %#zx: %d [refs=%ld, k=%K, qid=%Q]\n",
i, getmalloctag(f), f->fid, f->dent->ref, &f->dent->Key, f->dent->qid);
runlock(f->dent);
}
unlock(&c->fidtablk[i]);
}
}
}
void
showtree(int fd, char **ap, int na)
{
char *name;
Tree *t;
Blk *b;
int h;
name = "main";
memset(&t, 0, sizeof(t));
if(na == 1)
name = ap[0];
if(strcmp(name, "snap") == 0)
t = &fs->snap;
else if((t = opensnap(name, nil)) == nil){
fprint(fd, "open %s: %r\n", name);
return;
}
b = getroot(t, &h);
fprint(fd, "=== [%s] %B @%d\n", name, t->bp, t->ht);
showblk(fd, b, "contents", 1);
dropblk(b);
if(t != &fs->snap)
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
unreserve(int fd, char **ap, int)
{
if(strcmp(ap[0], "on") == 0)
usereserve = 0;
else if(strcmp(ap[0], "off") == 0)
usereserve = 1;
else
fprint(2, "unknown reserve %s\n", ap[0]);
fprint(fd, "reserve: %d → %d\n", !permissive, permissive);
}
static void
help(int fd, char**, int)
{
char *msg =
"help -- show this help\n"
"check -- check for consistency\n"
"df -- show disk usage\n"
"halt -- stop all writers, sync, and go read-only\n"
"permit [on|off] -- switch to/from permissive mode\n"
"reserve [on|off] -- enable block reserves\n"
"snap -[Smdl] [old [new]] -- manage snapshots\n"
"sync --flush all pending writes to disk\n"
"users -- reload user table from adm snapshot\n"
"show -- debug dumps\n"
" tree [name]\n"
" fid\n"
" users\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="permit", .sub=nil, .minarg=1, .maxarg=1, .fn=permflip},
{.name="snap", .sub=nil, .minarg=1, .maxarg=3, .fn=snapfs},
{.name="sync", .sub=nil, .minarg=0, .maxarg=0, .fn=syncfs},
{.name="reserve", .sub=nil, .minarg=0, .maxarg=1, .fn=unreserve},
{.name="users", .sub=nil, .minarg=0, .maxarg=1, .fn=refreshusers},
/* debugging */
{.name="show", .sub="fid", .minarg=0, .maxarg=0, .fn=showfid},
{.name="show", .sub="tree", .minarg=0, .maxarg=1, .fn=showtree},
{.name="show", .sub="users", .minarg=0, .maxarg=0, .fn=showusers},
{.name="show", .sub="bstate", .minarg=0, .maxarg=0, .fn=showbstate},
{.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)
goto Next;
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");
}
Next:
epochend(tid);
}
}