Charles.Forsyth20060303a828770d19 years agoHistory
676 lines · 13.0 KB · Brainfuck
RawDownload
implement Cs; ## Connection server translates net!machine!service into# /net/tcp/clone 135.104.9.53!564# include "sys.m";	sys:	Sys; include "draw.m"; include "srv.m";	srv: Srv; include "bufio.m";include "attrdb.m";	attrdb: Attrdb;	Attr, Db, Dbentry, Tuples: import attrdb; include "ip.m";	ip: IP;include "ipattr.m";	ipattr: IPattr; include "arg.m"; Cs: module{	init:	fn(nil: ref Draw->Context, nil: list of string);}; # signature of dial-on-demand moduleCSdial: module{	init:	fn(nil: ref Draw->Context): string;	connect:	fn(): string;}; Reply: adt{	fid:	int;	pid:	int;	addrs:	list of string;	err:	string;}; Cached: adt{	expire:	int;	query:	string;	addrs:	list of string;}; Ncache: con 16;cache:= array[Ncache] of ref Cached;nextcache := 0; rlist: list of ref Reply; ndbfile := "/lib/ndb/local";ndb: ref Db;mntpt := "/net";myname: string; stderr: ref Sys->FD; verbose := 0;dialmod: CSdial; init(ctxt: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	stderr = sys->fildes(2);	attrdb = load Attrdb Attrdb->PATH;	if(attrdb == nil)		cantload(Attrdb->PATH);	attrdb->init();	ip = load IP IP->PATH;	if(ip == nil)		cantload(IP->PATH);	ip->init();	ipattr = load IPattr IPattr->PATH;	if(ipattr == nil)		cantload(IPattr->PATH);	ipattr->init(attrdb, ip); 	svcname := "#scs";	arg := load Arg Arg->PATH;	if (arg == nil)		cantload(Arg->PATH);	arg->init(args);	arg->setusage("cs [-v] [-x mntpt] [-f database] [-d dialmod]");	while((c := arg->opt()) != 0)		case c {		'v' or 'D' =>			verbose++;		'd' =>	# undocumented hack to replace svc/cs/cs			f := arg->arg();			if(f != nil){				dialmod = load CSdial f;				if(dialmod == nil)					cantload(f);			}		'f' =>			ndbfile = arg->earg();		'x' =>			mntpt = arg->earg();			svcname = "#scs"+svcpt(mntpt);		* =>			arg->usage();		} 	if(arg->argv() != nil)		arg->usage();	arg = nil; 	srv = load Srv Srv->PATH;	# hosted Inferno only	if(srv != nil)		srv->init(); 	sys->remove(svcname+"/cs");	sys->unmount(svcname, mntpt);	publish(svcname);	if(sys->bind(svcname, mntpt, Sys->MBEFORE) < 0)		error(sys->sprint("can't bind #s on %s: %r", mntpt));	file := sys->file2chan(mntpt, "cs");	if(file == nil)		error(sys->sprint("can't make %s/cs: %r", mntpt));	sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);	refresh();	if(dialmod != nil){		e := dialmod->init(ctxt);		if(e != nil)			error(sys->sprint("can't initialise dial-on-demand: %s", e));	}	spawn cs(file);} svcpt(s: string): string{	for(i:=0; i<len s; i++)		if(s[i] == '/')			s[i] = '_';	return s;} publish(dir: string){	d := Sys->nulldir;	d.mode = 8r777;	if(sys->wstat(dir, d) < 0)		sys->fprint(sys->fildes(2), "cs: can't publish %s: %r\n", dir);} cantload(m: string){	error(sys->sprint("cannot load %s: %r", m));} error(s: string){	sys->fprint(sys->fildes(2), "cs: %s\n", s);	raise "fail:error";} refresh(){	myname = sysname();	if(ndb == nil){		ndb2 := Db.open(ndbfile);		if(ndb2 == nil){			err := sys->sprint("%r");			ndb2 = Db.open("/lib/ndb/inferno");	# try to get service map at least			if(ndb2 == nil)				sys->fprint(sys->fildes(2), "cs: warning: can't open %s: %s\n", ndbfile, err);	# continue without it		}		ndb = Db.open(mntpt+"/ndb");		if(ndb != nil)			ndb = ndb.append(ndb2);		else			ndb = ndb2;	}else		ndb.reopen();} sysname(): string{	t := rf("/dev/sysname");	if(t != nil)		return t;	t = rf("#e/sysname");	if(t == nil){		s := rf(mntpt+"/ndb");		if(s != nil){			db := Db.sopen(t);			if(db != nil){				(e, nil) := db.find(nil, "sys");				if(e != nil)					t = e.findfirst("sys");			}		}	}	if(t != nil){		fd := sys->open("/dev/sysname", Sys->OWRITE);		if(fd != nil)			sys->fprint(fd, "%s", t);	}	return t;} rf(name: string): string{	fd := sys->open(name, Sys->OREAD);	buf := array[Sys->NAMEMAX] of byte;	n := sys->read(fd, buf, len buf);	if(n <= 0)		return nil;	return string buf[0:n];} cs(file: ref Sys->FileIO){	pidc := chan of int;	donec := chan of ref Reply;	for (;;) {		alt {		(nil, buf, fid, wc) := <-file.write =>			cleanfid(fid);	# each write cancels previous requests			if(dialmod != nil){				e := dialmod->connect();				if(e != nil){					if(len e > 5 && e[0:5]=="fail:")						e = e[5:];					if(e == "")						e = "unknown error";					wc <-= (0, "cs: dial on demand: "+e);					break;				}			}			if(wc != nil){				nbytes := len buf;				query := string buf;				if(query == "refresh"){					refresh();					wc <-= (nbytes, nil);					break;				}				now := time();				r := ref Reply;				r.fid = fid;				spawn request(r, query, nbytes, now, wc, pidc, donec);				r.pid = <-pidc;				rlist = r :: rlist;			} 		(off, nbytes, fid, rc) := <-file.read =>			if(rc != nil){				r := findfid(fid);				if(r != nil)					reply(r, off, nbytes, rc);				else					rc <-= (nil, "unknown request");			} else				;	# cleanfid(fid);		# compensate for csendq in file2chan 		r := <-donec =>			r.pid = 0;		}	}} findfid(fid: int): ref Reply{	for(rl := rlist; rl != nil; rl = tl rl){		r := hd rl;		if(r.fid == fid)			return r;	}	return nil;} cleanfid(fid: int){	rl := rlist;	rlist = nil;	for(; rl != nil; rl = tl rl){		r := hd rl;		if(r.fid != fid)			rlist = r :: rlist;		else			killgrp(r.pid);	}} killgrp(pid: int){	if(pid != 0){		fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);		if(fd == nil || sys->fprint(fd, "killgrp") < 0)			sys->fprint(stderr, "cs: can't killgrp %d: %r\n", pid);	}} request(r: ref Reply, query: string, nbytes: int, now: int, wc: chan of (int, string), pidc: chan of int, donec: chan of ref Reply){	pidc <-= sys->pctl(Sys->NEWPGRP, nil);	if(query != nil && query[0] == '!'){		# general query		(r.addrs, r.err) = genquery(query[1:]);	}else{		(r.addrs, r.err) = xlate(query, now);		if(r.addrs == nil && r.err == nil)			r.err = "cs: can't translate address";	}	if(r.err != nil){		if(verbose)			sys->fprint(stderr, "cs: %s: %s\n", query, r.err);		wc <-= (0, r.err);	} else		wc <-= (nbytes, nil);	donec <-= r;} reply(r: ref Reply, off: int, nbytes: int, rc: chan of (array of byte, string)){	if(r.err != nil){		rc <-= (nil, r.err);		return;	}	addr: string = nil;	if(r.addrs != nil){		addr = hd r.addrs;		r.addrs = tl r.addrs;	}	off = 0;	# this version ignores offset	rc <-= reads(addr, off, nbytes);} ## return the file2chan reply for a read of the given string#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);} lookcache(query: string, now: int): ref Cached{	for(i:=0; i<len cache; i++){		c := cache[i];		if(c != nil && c.query == query && now < c.expire){			if(verbose)				sys->print("cache: %s -> %s\n", query, hd c.addrs);			return c;		}	}	return nil;} putcache(query: string, addrs: list of string, now: int){	ce := ref Cached;	ce.expire = now+120;	ce.query = query;	ce.addrs = addrs;	cache[nextcache] = ce;	nextcache = (nextcache+1)%Ncache;} xlate(address: string, now: int): (list of string, string){	n: int;	l, rl, results: list of string;	repl, netw, mach, service: string; 	ce := lookcache(address, now);	if(ce != nil && ce.addrs != nil)		return (ce.addrs, nil); 	(n, l) = sys->tokenize(address, "!\n");	if(n < 2)		return (nil, "bad format request"); 	netw = hd l;	if(netw == "net")		netw = "tcp";	# TO DO: better (needs lib/ndb)	if(!isnetwork(netw))		return (nil, "network unavailable "+netw);	l = tl l; 	if(!isipnet(netw)) {		repl = mntpt + "/" + netw + "/clone ";		for(;;){			repl += hd l;			if((l = tl l) == nil)				break;			repl += "!";		}		return (repl :: nil, nil);	# no need to cache	} 	if(n != 3)		return (nil, "bad format request");	mach = hd l;	service = hd tl l; 	if(!isnumeric(service)) {		s := xlatesvc(netw, service);		if(s == nil){			if(srv != nil)				s = srv->ipn2p(netw, service);			if(s == nil)				return (nil, "cs: can't translate service");		}		service = s;	} 	attr := ipattr->dbattr(mach);	if(mach == "*")		l = "" :: nil;	else if(attr != "ip") {		# Symbolic server == "$SVC"		if(mach[0] == '$' && len mach > 1 && ndb != nil){			(s, nil) := ipattr->findnetattr(ndb, "sys", myname, mach[1:]);			if(s == nil){				names := dblook("infernosite", "", mach[1:]);				if(names == nil)					return (nil, "cs: can't translate "+mach);				s = hd names;			}			mach = s;			attr = ipattr->dbattr(mach);		}		if(attr == "sys"){			results = dblook("sys", mach, "ip");			if(results != nil)				attr = "ip";		}		if(attr != "ip"){			err: string;			(results, err) = querydns(mach, "ip");			if(err != nil)				return (nil, err);		}else if(results == nil)			results = mach :: nil;		l = results;		if(l == nil){			if(srv != nil)				l = srv->iph2a(mach);			if(l == nil)				return (nil, "cs: unknown host");		}	} else		l = mach :: nil; 	while(l != nil) {		s := hd l;		l = tl l;		if(s != "")			s[len s] = '!';		s += service; 		repl = mntpt+"/"+netw+"/clone "+s;		if(verbose)			sys->fprint(stderr, "cs: %s!%s!%s -> %s\n", netw, mach, service, repl); 		rl = repl :: rl;	}	rl = reverse(rl);	putcache(address, rl, now);	return (rl, nil);} querydns(name: string, rtype: string): (list of string, string){	fd := sys->open(mntpt+"/dns", Sys->ORDWR);	if(fd == nil)		return (nil, nil);	if(sys->fprint(fd, "%s %s", name, rtype) < 0)		return (nil, sys->sprint("%r"));	rl: list of string;	buf := array[256] of byte;	sys->seek(fd, big 0, 0);	while((n := sys->read(fd, buf, len buf)) > 0){		# name rtype value		(nf, fld) := sys->tokenize(string buf[0:n], " \t");		if(nf != 3){			sys->fprint(stderr, "cs: odd result from dns: %s\n", string buf[0:n]);			continue;		}		rl = hd tl tl fld :: rl;

Lines 1–500 of 676.