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(s); 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[512] 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; dnetw := netw; if(s != nil){ (divert, err) := ipattr->findnetattr(ndb, "ip", s, "divert-"+netw); if(err == nil && divert != nil){ dnetw = divert; if(!isnetwork(dnetw)) return (nil, "network unavailable "+dnetw); # XXX should only give up if all addresses fail? } } if(s != "") s[len s] = '!'; s += service; repl = mntpt+"/"+dnetw+"/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;Lines 1–500 of 686. Load more
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 module CSdial: 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(s); 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[512] 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; dnetw := netw; if(s != nil){ (divert, err) := ipattr->findnetattr(ndb, "ip", s, "divert-"+netw); if(err == nil && divert != nil){ dnetw = divert; if(!isnetwork(dnetw)) return (nil, "network unavailable "+dnetw); # XXX should only give up if all addresses fail? } } if(s != "") s[len s] = '!'; s += service; repl = mntpt+"/"+dnetw+"/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;