shithub: dddb

ref: aee91c4ed0277e343a62cd124ee7175f54ffefb3
dir: /appl/cmd/ctlfs.b/

View raw version
include "dial.m";
	dial: Dial;

include "security.m";
	auth: Auth;

include "styx.m";
	styx: Styx;
	Tmsg, Rmsg: import Styx;

include "styxservers.m";
	styxservers: Styxservers;
	Styxserver, Fid, Navigator,
	Navop, Enotfound, Enotdir: import styxservers;

# FS file index
Qroot, Qctl, Qstats, Qmax: con iota;
tab := array[] of {
	(Qroot, ".", Sys->DMDIR|8r555),
	(Qctl, "ctl", 8r222),
	(Qstats, "stats", 8r111),
};

# create ctlfs and the appropriate listeners
init_ctlfs(cfg: Config, keyfile: string, algs: list of string)
{
	dial = load Dial Dial->PATH;
	auth = load Auth Auth->PATH;
	styx = load Styx Styx->PATH;

	if(dial == nil)
		error("ctlfs: dial module not found");
	if(auth == nil)
		error("ctlfs: auth module not found");
	if(styx == nil)
		error("ctlfs: styx module not found");

	auth->init();

	styx->init();
	styxservers->init(styx);
	styxservers->traceset(chatty);

	# authinfo init
	if(debug)
		sys->fprint(stderr, "ctlfs: reading authinfo");
	authinfo: ref Keyring->Authinfo;
	if (doauth) {
		if (keyfile == nil)
			keyfile = "/usr/" + user() + "/keyring/default";
		authinfo = keyring->readauthinfo(keyfile);
		if (authinfo == nil)
			error(sys->sprint("ctlfs: cannot read %s: %r", keyfile));
	}

	# announcing
	if(debug)
		sys->fprint(stderr, "ctlfs: announcing dddbctl");
	addr := dial->netmkaddr(cfg.addr, "tcp", "dddbctl");
	c := dial->announce(addr);
	if(c == nil)
		error(sys->sprint("ctlfs: cannot listen on %s\n", addr));

	# bootstrapping
	if(debug)
		sys->fprint
	sys->unmount(nil, "/mnt/keys");

	navch := chan of ref Navop;
	spawn ctlfs_navigator(navch);

	nav := Navigator.new(navch);
	(tc, srv) := Styxserver.new(fildes(0), nav, big Qroot);

	# listener entrypoint
	listener(c, authinfo, algs);
}

# dddbctl listener loop
ctlfs_listener(c: ref Dial->Connection, authinfo: ref Keyring->Authinfo, algs: list of string)
{
	for (;;) {
		nc := dial->listen(c);
		if (nc == nil)
			error(sys->sprint("listen failed: %r"));
		if (debug)
			sys->fprint(stderr, "ctlfs: got connection from %s\n",
						readfile(nc.dir + "/remote"));
		dfd := dial->accept(nc);
		if (dfd != nil) {
			if(nc.cfd != nil)
				sys->fprint(nc.cfd, "keepalive");
			hostname: string;
			if(passhostnames){
				hostname = readfile(nc.dir + "/remote");
				if(hostname != nil)
					hostname = hostname[0:len hostname - 1];
			}

			spawn ctlfs_authenticator(dfd, authinfo, algs, hostname);
		}
	}
}

# authenticate a connection and set the user id.
ctlfs_authenticator(dfd: ref Sys->FD, authinfo: ref Keyring->Authinfo,
		algs: list of string, hostname: string)
{
	# authenticate and change user id appropriately
	(fd, err) := auth->server(algs, authinfo, dfd, 1);
	if (fd == nil) {
		if (debug)
			sys->fprint(stderr(), "ctlfs: authentication failed: %s\n", err);
		return;
	}
	if (debug)
		sys->fprint(stderr(), "ctlfs: client authenticated as %s\n", err);

	spawn exportproc(sync, mfd, err, hostname, fd);
}

ctlfs_loop()
{
	# Primary server loop
	loop:
	while((tmsg := <-tc) != nil) {
		# Switch on operations being performed on a given Fid
		pick msg := tmsg {
		Open =>
			srv.default(msg);
		Read =>
			fid := srv.getfid(msg.fid);

			if(fid.qtype & Sys->QTDIR) {
				# This is a directory read
				srv.default(msg);
				continue loop;
			}

			case int fid.path {
			Qlog =>
				# A read on our log file, tell them what they've already said ?
				s := "";

				for(l := log; l != nil; l = tl l)
					s = hd l + s;

				srv.reply(styxservers->readstr(msg, s));

			* =>
				srv.default(msg);
			}

		Write =>
			fid := srv.getfid(msg.fid);

			case int fid.path {
			Qctl =>
				# Don't care about offset
				cmd := string msg.data;

				reply: ref Rmsg = ref Rmsg.Write(msg.tag, len msg.data);

				case cmd {
				* =>
					# Ignore empty writes
					if(cmd != nil)
						log = cmd :: log;
					else
						reply = ref Rmsg.Error(msg.tag, "empty write!");
				}
				srv.reply(reply);
				
			* =>
				srv.default(msg);
			}

		* =>
			srv.default(msg);
		}
	}

	exit;
}

# Navigator function for moving around under /
ctlfs_navigator(c: chan of ref Navop) {
	loop: 
	for(;;) {
		navop := <-c;
		pick op := navop {
		Stat =>
			op.reply <-= (dir(int op.path), nil);
			
		Walk =>
			if(op.name == "..") {
				op.reply <-= (dir(Qroot), nil);
				continue loop;
			}

			case int op.path&16rff {

			Qroot =>
				for(i := 1; i < Qmax; i++)
					if(tab[i].t1 == op.name) {
						op.reply <-= (dir(i), nil);
						continue loop;
					}

				op.reply <-= (nil, Enotfound);
			* =>
				op.reply <-= (nil, Enotdir);
			}
			
		Readdir =>
			for(i := 0; i < op.count && i + op.offset < (len tab) - 1; i++)
				op.reply <-= (dir(Qroot+1+i+op.offset), nil);

			op.reply <-= (nil, nil);
		}
	}
}