code: 9ferno

ref: 600bbfe4aaa9ad0f73d8d73eef1b7670e5f7d3a3
dir: /appl/collab/proxy.b/

View raw version
implement Proxy;

include "sys.m";
	sys: Sys;

include "srvmgr.m";
include "proxy.m";

Srvreq, Srvreply: import Srvmgr;

init(root: string, fd: ref Sys->FD, rc: chan of ref Srvreq, user: string)
{
	sys = load Sys Sys->PATH;

	if(fd == nil)
		raise "err: fd for export was nil";

	sys->chdir(root);
	sys->bind("export/services", "export/services", Sys->MCREATE);
	sys->bind("#s", "export/services", Sys->MBEFORE);

	ctlio := sys->file2chan("export/services", "ctl");
	if(ctlio == nil)
		raise "err: export/services file2chan failed -- " + root + "/export/services directory does not exist" ;

	hangup := chan of int;
	spawn export(fd, "export", hangup);
	fd = nil;

	for (;;) alt {
	<- hangup =>
		# closedown all clients
		sys->print("client exit [%s]\n", user);
		rmclients(rc);
		return;
	(offset, count, fid, r) := <- ctlio.read =>
		client := fid2client(fid);
		if (r == nil) {
			if (client != nil)
				rmclient(rc, client);
			continue;
		}
		if (client == nil) {
			rreply(r, (nil, "service not set"));
			continue;
		}
		rreply(r, reads(client.path, offset, count));

	(offset, data, fid, w) := <- ctlio.write =>
		client := fid2client(fid);
		if (w == nil) {
			if (client != nil)
				rmclient(rc, client);
			continue;
		}
		if (client != nil) {
			wreply(w, (0, "service set"));
			continue;
		}
		err := newclient(rc, user, fid, string data);
		if (err != nil)
			wreply(w, (0, err));
		else
			wreply(w, (len data, nil));
	}
	
}

rreply(rc: chan of (array of byte, string), reply: (array of byte, string))
{
	alt {
	rc <-= reply =>;
	* =>;
	}
}

wreply(wc: chan of (int, string), reply: (int, string))
{
	alt {
	wc <-= reply=>;
	* =>;
	}
}

reads(str: string, off, nbytes: int): (array of byte, string)
{
	bstr := array of byte str;
	slen := len bstr;
	if(off < 0 || off >= slen)
		return (nil, nil);
	if(off + nbytes > slen)
		nbytes = slen - off;
	if(nbytes <= 0)
		return (nil, nil);
	return (bstr[off:off+nbytes], nil);
}

export(exportfd: ref Sys->FD, dir: string, done: chan of int)
{
	sys->export(exportfd, dir, Sys->EXPWAIT);
	done <-= 1;
}

Client: adt {
	fid: int;
	path: string;
	sname: string;
	id: string;
};

clients: list of ref Client;
freepaths: list of string;
nextpath := 0;

fid2client(fid: int): ref Client
{
	for(cl := clients; cl != nil; cl = tl cl)
		if ((c := hd cl).fid == fid)
			return c;
	return nil;
}

newclient(rc: chan of ref Srvreq, user: string, fid: int, cmd: string): string
{
sys->print("new Client %s [%s]\n", user, cmd);
	for (i := 0; i < len cmd; i++)
		if (cmd[i] == ' ')
			break;
	if (i == 0 || i == len cmd)
		return "bad command";

	sname := cmd[:i];
	id := cmd[i:];
	reply := chan of Srvreply;
	rc <-= ref Srvreq.Acquire(sname, id, user, reply);
	(err, root, fd) := <- reply;
	if (err != nil)
		return err;

	path := "";
	if (freepaths != nil)
		(path, freepaths) = (hd freepaths, tl freepaths);
	else
		path = string nextpath++;

	sys->mount(fd, nil, "mnt", Sys->MREPL, nil);	# connection to the active service fs
	mkdir("export/services/"+path);
	sys->bind("mnt/"+root, "export/services/"+path, Sys->MREPL|Sys->MCREATE);
	sys->unmount("mnt", nil);
	clients = ref Client(fid, path, sname, id) :: clients;
	return nil;
}

rmclient(rc: chan of ref Srvreq, client: ref Client)
{
sys->print("rmclient [%s %s]\n", client.sname, client.id);
	nl: list of ref Client;
	for(cl := clients; cl != nil; cl = tl cl)
		if((c := hd cl) == client){
			sys->unmount("export/services/" + client.path, nil);
			freepaths = client.path :: freepaths;
			rc <-= ref Srvreq.Release(client.sname, client.id);
		} else
			nl = c :: nl;
	clients = nl;
}

rmclients(rc: chan of ref Srvreq)
{
	for(cl := clients; cl != nil; cl = tl cl){
		c := hd cl;
sys->print("rmclients [%s %s]\n", c.sname, c.id);
		rc <-= ref Srvreq.Release(c.sname, c.id);
	}
	clients = nil;
}

mkdir(path: string)
{
	sys->print("mkdir [%s]\n", path);
	sys->create(path, Sys->OREAD, 8r777 | Sys->DMDIR);
}