diff options
Diffstat (limited to 'appl')
| -rw-r--r-- | appl/lib/dial.b | 384 | ||||
| -rw-r--r-- | appl/lib/mkfile | 2 |
2 files changed, 386 insertions, 0 deletions
diff --git a/appl/lib/dial.b b/appl/lib/dial.b new file mode 100644 index 00000000..42f9119b --- /dev/null +++ b/appl/lib/dial.b @@ -0,0 +1,384 @@ +implement Dial; + +include "sys.m"; + sys: Sys; + +include "dial.m"; + +# +# the dialstring is of the form '[/net/]proto!dest' +# +dial(addr: string, local: string): ref Connection +{ + if(sys == nil) + sys = load Sys Sys->PATH; + (netdir, proto, rem) := dialparse(addr); + if(netdir != nil) + return csdial(netdir, proto, rem, local); + + c := csdial("/net", proto, rem, local); + if(c != nil) + return c; + err := sys->sprint("%r"); + if(lookstr(err, "refused") >= 0) + return nil; + c = csdial("/net.alt", proto, rem, local); + if(c != nil) + return c; + # ignore the least precise one + alterr := sys->sprint("%r"); + if(lookstr(alterr, "translate")>=0 || lookstr(alterr, "does not exist")>=0) + sys->werrstr(err); + else + sys->werrstr(alterr); + return nil; +} + +# +# ask the connection server to translate +# +csdial(netdir: string, proto: string, rem: string, local: string): ref Connection +{ + fd := sys->open(netdir+"/cs", Sys->ORDWR); + if(fd == nil){ + # no connection server, don't translate + return call(netdir+"/"+proto+"/clone", rem, local); + } + + if(sys->fprint(fd, "%s!%s", proto, rem) < 0) + return nil; + + # try each recipe until we get one that works + besterr, err: string; + sys->seek(fd, big 0, 0); + for(;;){ + (clonefile, addr) := csread(fd); + if(clone == nil) + break; + c := call(redir(clonefile, netdir), addr, local); + if(c != nil) + return c; + err = sys->sprint("%r"); + if(lookstr(err, "does not exist") < 0) + besterr = err; + } + if(besterr != nil) + sys->werrstr(besterr); + else + sys->werrstr(err); + return nil; +} + +call(clonefile: string, dest: string, local: string): ref Connection +{ + (cfd, convdir) := clone(clonefile); + if(cfd == nil) + return nil; + + if(local != nil) + rv := sys->fprint(cfd, "connect %s %s", dest, local); + else + rv = sys->fprint(cfd, "connect %s", dest); + if(rv < 0) + return nil; + + fd := sys->open(convdir+"/data", Sys->ORDWR); + if(fd == nil) + return nil; + return ref Connection(fd, cfd, convdir); +} + +clone(clonefile: string): (ref Sys->FD, string) +{ + pdir := parent(clonefile); + if(pdir == nil){ + sys->werrstr(sys->sprint("bad clone file name: %q", clonefile)); + return (nil, nil); + } + cfd := sys->open(clonefile, Sys->ORDWR); + if(cfd == nil) + return (nil, nil); + lno := readchan(cfd); + if(lno == nil) + return (nil, nil); + return (cfd, pdir+"/"+lno); +} + +readchan(cfd: ref Sys->FD): string +{ + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(cfd, buf, len buf); + if(n < 0) + return nil; + if(n == 0){ + sys->werrstr("empty clone file"); + return nil; + } + return string int string buf[0: n]; +} + +redir(old: string, newdir: string): string +{ + # because cs is in a different name space, replace the mount point + # assumes the mount point is directory in root (eg, /net/proto/clone) + if(len old > 1 && old[0] == '/'){ + p := lookc(old[1:], '/'); + if(p >= 0) + return newdir+"/"+old[1+p+1:]; + } + return newdir+"/"+old; +} + +lookc(s: string, c: int): int +{ + for(i := 0; i < len s; i++) + if(s[i] == c) + return i; + return -1; +} + +backc(s: string, i: int, c: int): int +{ + if(i >= len s) + return -1; + while(i >= 0 && s[i] != c) + i--; + return i; +} + +lookstr(s: string, t: string): int +{ + lt := len t; # we know it's not zero +Search: + for(i := 0; i <= len s - lt; i++){ + for(j := 0; j < lt; j++) + if(s[i+j] != t[j]) + continue Search; + return i; + } + return -1; +} + +# +# [[/netdir/]proto!]remainder +# +dialparse(addr: string): (string, string, string) +{ + p := lookc(addr, '!'); + if(p < 0) + return (nil, "net", addr); + if(addr[0] != '/' && addr[0] != '#') + return (nil, addr[0: p], addr[p+1:]); + p2 := backc(addr, p, '/'); + if(p2 <= 0) + return (addr[0: p], "net", addr[p+1:]); # plan 9 returns proto "" + return (addr[0: p2], addr[p2+1: p], addr[p+1:]); +} + +# +# announce a network service +# +announce(addr: string): ref Connection +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + (naddr, clonefile) := nettrans(addr); + if(naddr == nil) + return nil; + + (ctl, convdir) := clone(clonefile); + if(ctl == nil){ + sys->werrstr(sys->sprint("announce %r")); + return nil; + } + + if(sys->fprint(ctl, "announce %s", naddr) < 0){ + sys->werrstr(sys->sprint("announce writing %s: %r", clonefile)); + return nil; + } + + return ref Connection(nil, ctl, convdir); +} + +# +# listen for an incoming call on announced connection +# +listen(ac: ref Connection): ref Connection +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + pdir := parent(ac.dir); # ac.dir should be /netdir/N + if(pdir == nil){ + sys->werrstr(sys->sprint("listen directory format: %q", ac.dir)); + return nil; + } + + ctl := sys->open(ac.dir+"/listen", Sys->ORDWR); + if(ctl == nil){ + sys->werrstr(sys->sprint("listen opening %s: %r", ac.dir+"/listen")); + return nil; + } + + lno := readchan(ctl); + if(lno == nil){ + sys->werrstr(sys->sprint("listen reading %s/listen: %r", ac.dir)); + return nil; + } + return ref Connection(nil, ctl, pdir+"/"+lno); + +} + +# +# translate an address [[/netdir/]proto!rem] using /netdir/cs +# returning (newaddress, clonefile) +# +nettrans(addr: string): (string, string) +{ + (netdir, proto, rem) := dialparse(addr); + if(proto == nil || proto == "net"){ + sys->werrstr(sys->sprint("bad dial string: %s", addr)); + return (nil, nil); + } + if(netdir == nil) + netdir = "/net"; + + # try to translate using connection server + fd := sys->open(netdir+"/cs", Sys->ORDWR); + if(fd == nil){ + # use it untranslated + if(rem == nil){ + sys->werrstr(sys->sprint("bad dial string: %s", addr)); + return (nil, nil); + } + return (rem, netdir+"/"+proto+"/clone"); + } + if(sys->fprint(fd, "%s!%s", proto, rem) < 0) + return (nil, nil); + sys->seek(fd, big 0, 0); + (clonefile, naddr) := csread(fd); + if(clonefile == nil) + return (nil, nil); + + return (naddr, redir(clonefile, netdir)); +} + +csread(fd: ref Sys->FD): (string, string) +{ + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return (nil, nil); + line := string buf[0: n]; + p := lookc(line, ' '); + if(p < 0) + return (nil, nil); + if(p == 0){ + sys->werrstr("cs: no translation"); + return (nil, nil); + } + return (line[0:p], line[p+1:]); +} + +# +# accept a call, return an fd to the open data file +# +accept(c: ref Connection): ref Sys->FD +{ + if(sys == nil) + sys = load Sys Sys->PATH; + sys->fprint(c.cfd, "accept %s", lastname(c.dir)); # ignore return value, network might not need accepts + return sys->open(c.dir+"/data", Sys->ORDWR); +} + +# +# reject a call, tell device the reason for the rejection +# +reject(c: ref Connection, why: string): int +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if(sys->fprint(c.cfd, "reject %s %q", lastname(c.dir), why) < 0) + return -1; + return 0; +} + +lastname(dir: string): string +{ + p := backc(dir, len dir-1, '/'); + if(p < 0) + return dir; + return dir[p+1:]; # N in /net/N +} + +parent(dir: string): string +{ + p := backc(dir, len dir-1, '/'); + if(p < 0) + return nil; + return dir[0: p]; +} + +netmkaddr(addr, net, svc: string): string +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if(net == nil) + net = "net"; + (n, nil) := sys->tokenize(addr, "!"); + if(n <= 1){ + if(svc== nil) + return sys->sprint("%s!%s", net, addr); + return sys->sprint("%s!%s!%s", net, addr, svc); + } + if(svc == nil || n > 2) + return addr; + return sys->sprint("%s!%s", addr, svc); +} + +netinfo(c: ref Connection): ref Conninfo +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if((dir := c.dir) == nil){ + if(c.dfd == nil) + return nil; + dir = parent(sys->fd2path(c.dfd)); + if(dir == nil) + return nil; + } + ci := ref Conninfo; + ci.dir = dir; + ci.root = parent(dir); + while((p := parent(ci.root)) != nil && p != "/") + ci.root = p; + (ok, d) := sys->stat(ci.dir); + if(ok >= 0) + ci.spec = sys->sprint("#%c%d", d.dtype, d.dev); + (ci.lsys, ci.lserv) = getendpoint(ci.dir, "local"); + (ci.rsys, ci.rserv) = getendpoint(ci.dir, "remote"); + p = parent(ci.dir); + if(p == nil) + return nil; + if(len p >= 5 && p[0:5] == "/net/") + p = p[5:]; + ci.laddr = sys->sprint("%s!%s!%s", p, ci.lsys, ci.lserv); + ci.raddr = sys->sprint("%s!%s!%s", p, ci.rsys, ci.rserv); + return ci; +} + +getendpoint(dir: string, file: string): (string, string) +{ + fd := sys->open(dir+"/"+file, Sys->OREAD); + buf := array[128] of byte; + if(fd == nil || (n := sys->read(fd, buf, len buf)) <= 0) + return ("???", "???"); # compatible, but probably poor defaults + if(n > 0 && buf[n-1] == byte '\n') + n--; + s := string buf[0: n]; + p := lookc(s, '!'); + if(p < 0) + return (s, "???"); + return (s[0:p], s[p+1:]); +} diff --git a/appl/lib/mkfile b/appl/lib/mkfile index a6eaf912..6784a568 100644 --- a/appl/lib/mkfile +++ b/appl/lib/mkfile @@ -113,6 +113,7 @@ TARG=\ string.dis\ strinttab.dis\ styx.dis\ + styxflush.dis\ styxlib.dis\ styxpersist.dis\ styxservers.dis\ @@ -234,3 +235,4 @@ csv.dis: $ROOT/module/csv.m json.dis: $ROOT/module/json.m lists.dis: $ROOT/module/lists.m vac.dis: $ROOT/module/vac.m $ROOT/module/venti.m +dial.dis: $ROOT/module/dial.m |
