shithub: gefs

ref: 1d87f43466480d3f9200260d88ea3d07bcb527a6
dir: /cons.c/

View raw version
#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 *fnam, 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);
		fnam = (flg & Lmut) ? "mutable" : "";
		fprint(fd, "snap %.*s %s\n", s.kv.nk-1, s.kv.k+1, fnam);
	}
	btexit(&s);
}

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;
		case 'l':
			listsnap(fd);
			return;
		default:
			fprint(fd, "usage: snap -[md] 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, "fishy\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
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)
{
	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);
	}
	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]);

}

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"
		"	tree [name]\n"
		"		the contents of the tree associated with a\n"
		"		snapshot. The special name 'snap' shows the\n"
		"		snapshot tree\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=1, .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="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)
			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);
	}
}