shithub: gefs

ref: b604e0abe047f5e64615fb405c1d74376fc1681a
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
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 || !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)
{
	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)
{
	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
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);
	}
}