/appl/cmd/auth/factotum/factotum.b
Brainfuck | 1066 lines | 979 code | 87 blank | 0 comment | 121 complexity | b13e49e91157e7c3b436551596478125 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
- implement Factotum, Authio;
- #
- # Copyright Š 2003-2004 Vita Nuova Holdings Limited
- #
- include "sys.m";
- sys: Sys;
- Rread, Rwrite: import Sys;
- include "draw.m";
- include "string.m";
- str: String;
- include "keyring.m";
- include "authio.m";
- include "arg.m";
- include "readdir.m";
- Factotum: module
- {
- init: fn(nil: ref Draw->Context, nil: list of string);
- };
- #confirm, log
- Files: adt {
- ctl: ref Sys->FileIO;
- rpc: ref Sys->FileIO;
- proto: ref Sys->FileIO;
- needkey: ref Sys->FileIO;
- };
- Debug: con 0;
- debug := Debug;
- files: Files;
- authio: Authio;
- keymanc: chan of (list of ref Attr, int, chan of (list of ref Key, string));
- init(nil: ref Draw->Context, args: list of string)
- {
- sys = load Sys Sys->PATH;
- str = load String String->PATH;
- authio = load Authio "$self";
- svcname := "#sfactotum";
- mntpt := "/mnt/factotum";
- arg := load Arg Arg->PATH;
- if(arg != nil){
- arg->init(args);
- arg->setusage("auth/factotum [-d] [-m /mnt/factotum] [-s factotum]");
- while((o := arg->opt()) != 0)
- case o {
- 'd' => debug++;
- 'm' => mntpt = arg->earg();
- 's' => svcname = "#s"+arg->earg();
- * => arg->usage();
- }
- args = arg->argv();
- if(args != nil)
- arg->usage();
- arg = nil;
- }
- sys->unmount(nil, mntpt);
- if(sys->bind(svcname, mntpt, Sys->MREPL) < 0)
- err(sys->sprint("can't bind %s on %s: %r", svcname, mntpt));
- files.ctl = sys->file2chan(mntpt, "ctl");
- files.rpc = sys->file2chan(mntpt, "rpc");
- files.proto = sys->file2chan(mntpt, "proto");
- files.needkey = sys->file2chan(mntpt, "needkey");
- if(files.ctl == nil || files.rpc == nil || files.proto == nil || files.needkey == nil)
- err(sys->sprint("can't create %s/*: %r", mntpt));
- keymanc = chan of (list of ref Attr, int, chan of (list of ref Key, string));
- spawn factotumsrv();
- }
- user(): string
- {
- fd := sys->open("/dev/user", Sys->OREAD);
- if(fd == nil)
- return nil;
- b := array[Sys->NAMEMAX] of byte;
- n := sys->read(fd, b, len b);
- if(n <= 0)
- return nil;
- return string b[0:n];
- }
- err(s: string)
- {
- sys->fprint(sys->fildes(2), "factotum: %s\n", s);
- raise "fail:error";
- }
- rlist: list of ref Fid;
- factotumsrv()
- {
- sys->pctl(Sys->NEWPGRP|Sys->FORKFD|Sys->FORKENV, nil);
- if(debug == 0)
- privacy();
- allkeys := array[0] of ref Key;
- pidc := chan of int;
- donec := chan of ref Fid;
- # keyc := chan of (list of ref Attr, chan of (ref Key, string));
- needfid := -1;
- needed, needy: list of (int, list of ref Attr, chan of (list of ref Key, string));
- needread: Sys->Rread;
- needtag := 0;
- for(;;) X: alt{
- r := <-donec =>
- r.pid = 0;
- cleanfid(r.fid);
- (off, nbytes, nil, rc) := <-files.ctl.read =>
- if(rc == nil)
- break;
- s := "";
- for(i := 0; i < len allkeys; i++)
- if((k := allkeys[i]) != nil)
- s += k.safetext()+"\n";
- rc <-= reads(s, off, nbytes);
- (nil, data, nil, wc) := <-files.ctl.write =>
- if(wc == nil)
- break;
- (nf, flds) := sys->tokenize(string data, "\n\r");
- if(nf > 1){
- # compatibility with plan 9; has the advantage you can tell which key is wrong
- wc <-= (0, "multiline write not allowed");
- break;
- }
- if(flds == nil || (hd flds)[0] == '#'){
- wc <-= (len data, nil);
- break;
- }
- s := hd flds;
- for(i := 0; i < len s && s[i] != ' '; i++){
- # skip
- }
- verb := s[0:i];
- if(i < len s)
- i++;
- s = s[i:];
- case verb {
- "key" =>
- k := Key.mk(parseline(s));
- if(k == nil){
- wc <-= (len data, nil); # ignore it
- break;
- }
- if(lookattrval(k.attrs, "proto") == nil){
- wc <-= (0, "key without proto");
- break;
- }
- allkeys = addkey(allkeys, k);
- wc <-= (len data, nil);
- "delkey" =>
- attrs := parseline(s);
- for(al := attrs; al != nil; al = tl al){
- a := hd al;
- if(a.name[0] == '!' && (a.val != nil || a.tag != Aquery)){
- wc <-= (0, "cannot specify values for private fields");
- break X;
- }
- }
- if(delkey(allkeys, attrs) == 0)
- wc <-= (0, "no matching keys");
- else
- wc <-= (len data, nil);
- "debug" =>
- wc <-= (len data, nil);
- * =>
- wc <-= (0, "unknown verb");
- }
- (nil, nbytes, fid, rc) := <-files.rpc.read =>
- if(rc == nil)
- break;
- r := findfid(fid);
- if(r == nil){
- rc <-= (nil, "no rpc pending");
- break;
- }
- alt{
- r.read <-= (nbytes, rc) =>
- ;
- * =>
- rc <-= (nil, "concurrent rpc read not allowed");
- }
- (nil, data, fid, wc) := <-files.rpc.write =>
- if(wc == nil){
- cleanfid(fid);
- break;
- }
- r := findfid(fid);
- if(r == nil){
- r = ref Fid(fid, 0, nil, nil, chan[1] of (array of byte, Rwrite), chan[1] of (int, Rread), 0, nil);
- spawn request(r, pidc, donec);
- r.pid = <-pidc;
- rlist = r :: rlist;
- }
- # this non-blocking write avoids a potential deadlock situation that
- # can happen when a proto module calls findkey at the same time
- # a client tries to write to the rpc file. this might not be the correct fix!
- alt{
- r.write <-= (data, wc) =>
- ;
- * =>
- wc <-= (-1, "concurrent rpc write not allowed");
- }
- (off, nbytes, nil, rc) := <-files.proto.read =>
- if(rc == nil)
- break;
- rc <-= reads(readprotos(), off, nbytes);
- (nil, nil, nil, wc) := <-files.proto.write =>
- if(wc != nil)
- wc <-= (0, "illegal operation");
- (nil, nil, fid, rc) := <-files.needkey.read =>
- if(rc == nil)
- break;
- if(needfid >= 0 && fid != needfid){
- rc <-= (nil, "file in use");
- break;
- }
- needfid = fid;
- if(needy != nil){
- (tag, attr, kc) := hd needy;
- needy = tl needy;
- needed = (tag, attr, kc) :: needed;
- rc <-= (sys->aprint("needkey tag=%ud %s", tag, attrtext(attr)), nil);
- break;
- }
- if(needread != nil){
- rc <-= (nil, "already reading");
- break;
- }
- needread = rc;
- (nil, data, fid, wc) := <-files.needkey.write =>
- if(wc == nil){
- if(needfid == fid){
- needfid = -1; # TO DO? give needkey errors back to request
- needread = nil;
- }
- break;
- }
- if(needfid >= 0 && fid != needfid){
- wc <-= (0, "file in use");
- break;
- }
- needfid = fid;
- tagline := parseline(string data);
- if(len tagline != 1 || (t := lookattrval(tagline, "tag")) == nil){
- wc <-= (0, "no tag");
- break;
- }
- tag := int t;
- nl: list of (int, list of ref Attr, chan of (list of ref Key, string));
- found := 0;
- for(l := needed; l != nil; l = tl l){
- (ntag, attrs, kc) := hd l;
- if(tag == ntag){
- found = 1;
- k := findkey(allkeys, attrs);
- if(k != nil)
- kc <-= (k :: nil, nil);
- else
- kc <-= (nil, "needkey "+attrtext(attrs));
- while((l = tl l) != nil)
- nl = hd l :: nl;
- break;
- }
- nl = hd l :: nl;
- }
- if(found)
- wc <-= (len data, nil);
- else
- wc <-= (0, "tag not found");
- (attrs, required, kc) := <-keymanc =>
- # look for key and reply
- kl := findkeys(allkeys, attrs);
- if(kl != nil){
- kc <-= (kl, nil);
- break;
- }else if(!required || needfid == -1){
- kc <-= (nil, "needkey "+attrtext(attrs));
- break;
- }
- # query surrounding environment using needkey
- if(needread != nil){
- needed = (needtag, attrs, kc) :: needed;
- needread <-= (sys->aprint("needkey tag=%ud %s", needtag, attrtext(attrs)), nil);
- needread = nil;
- needtag++;
- }else
- needy = (needtag++, attrs, kc) :: needy;
- }
- }
- findfid(fid: int): ref Fid
- {
- 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 if(r.pid)
- kill(r.pid);
- }
- }
- kill(pid: int)
- {
- fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
- if(fd != nil)
- sys->fprint(fd, "kill");
- }
- privacy()
- {
- fd := sys->open("#p/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE);
- if(fd == nil || sys->fprint(fd, "private") < 0)
- sys->fprint(sys->fildes(2), "factotum: warning: unable to make memory private: %r\n");
- }
- 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);
- }
- readprotos(): string
- {
- readdir := load Readdir Readdir->PATH;
- if(readdir == nil)
- return "unknown\n";
- (dirs, nil) := readdir->init("/dis/auth/proto", Readdir->NAME|Readdir->COMPACT);
- s := "";
- for(i := 0; i < len dirs; i++){
- n := dirs[i].name;
- if(len n > 4 && n[len n-4:] == ".dis")
- s += n[0: len n-4]+"\n";
- }
- return s;
- }
- Ogok, Ostart, Oread, Owrite, Oauthinfo, Oattr: con iota;
- ops := array[] of {
- (Ostart, "start"),
- (Oread, "read"),
- (Owrite, "write"),
- (Oauthinfo, "authinfo"),
- (Oattr, "attr"),
- };
- request(r: ref Fid, pidc: chan of int, donec: chan of ref Fid)
- {
- pidc <-= sys->pctl(0, nil);
- rpc := rio(r);
- while(rpc != nil){
- if(rpc.cmd == Ostart){
- (proto, attrs, e) := startproto(string rpc.arg);
- if(e != nil){
- reply(rpc, "error "+e);
- rpc = rio(r);
- continue;
- }
- r.attrs = attrs; # saved for attr request
- ok(rpc);
- io := ref IO(r, nil);
- {
- err := proto->interaction(attrs, io);
- if(debug && err != nil)
- sys->fprint(sys->fildes(2), "factotum: failure: %s\n", err);
- if(r.err == nil)
- r.err = err;
- r.done = 1;
- }exception ex{
- "*" =>
- r.done = 0;
- r.err = "exception "+ex;
- }
- if(r.err != nil)
- io.error(r.err);
- rpc = finish(r);
- r.attrs = nil;
- r.err = nil;
- r.done = 0;
- r.ai = nil;
- }else
- reply(rpc, "no current protocol");
- }
- flushreq(r, donec);
- }
- startproto(request: string): (Authproto, list of ref Attr, string)
- {
- attrs := parseline(request);
- if(debug > 1)
- sys->print("-> %s <-\n", attrtext(attrs));
- p := lookattrval(attrs, "proto");
- if(p == nil)
- return (nil, nil, "did not specify protocol");
- if(debug > 1)
- sys->print("proto=%s\n", p);
- if(any(p, "./")) # avoid unpleasantness
- return (nil, nil, "illegal protocol: "+p);
- proto := load Authproto "/dis/auth/proto/"+p+".dis";
- if(proto == nil)
- return (nil, nil, sys->sprint("protocol %s: %r", p));
- if(debug)
- sys->print("start %s\n", p);
- e: string;
- {
- e = proto->init(authio);
- }exception ex{
- "*" =>
- e = "exception "+ex;
- }
- if(e != nil)
- return (nil, nil, e);
- return (proto, attrs, nil);
- }
- finish(r: ref Fid): ref Rpc
- {
- while((rpc := rio(r)) != nil)
- case rpc.cmd {
- Owrite =>
- phase(rpc, "protocol phase error");
- Oread =>
- if(r.err != nil)
- reply(rpc, "error "+r.err);
- else
- done(rpc, r.ai);
- Oauthinfo =>
- if(r.done){
- if(r.ai == nil)
- reply(rpc, "error no authinfo available");
- else{
- a := packai(r.ai);
- if(rpc.nbytes-3 < len a)
- reply(rpc, sys->sprint("toosmall %d", len a + 3));
- else
- okdata(rpc, a);
- }
- }else
- reply(rpc, "error authentication unfinished");
- Ostart =>
- return rpc;
- * =>
- reply(rpc, "error unexpected request");
- }
- return nil;
- }
- flushreq(r: ref Fid, donec: chan of ref Fid)
- {
- for(;;) alt{
- donec <-= r =>
- exit;
- (nil, wc) := <-r.write =>
- wc <-= (0, "write rpc protocol error");
- (nil, rc) := <-r.read =>
- rc <-= (nil, "read rpc protocol error");
- }
- }
- rio(r: ref Fid): ref Rpc
- {
- req: array of byte;
- for(;;) alt{
- (data, wc) := <-r.write =>
- if(req != nil){
- wc <-= (0, "rpc pending; read to clear");
- break;
- }
- req = data;
- wc <-= (len data, nil);
- (nbytes, rc) := <-r.read =>
- if(req == nil){
- rc <-= (nil, "no rpc pending");
- break;
- }
- (cmd, arg) := op(req, ops);
- req = nil;
- rpc := ref Rpc(r, cmd, arg, nbytes, rc);
- case cmd {
- Ogok =>
- reply(rpc, "error unknown rpc");
- break;
- Oattr =>
- if(r.attrs == nil)
- reply(rpc, "error no attributes");
- else
- reply(rpc, "ok "+attrtext(r.attrs));
- break;
- * =>
- return rpc;
- }
- }
- }
- ok(rpc: ref Rpc)
- {
- reply(rpc, "ok");
- }
- okdata(rpc: ref Rpc, a: array of byte)
- {
- b := array[len a + 3] of byte;
- b[0] = byte 'o';
- b[1] = byte 'k';
- b[2] = byte ' ';
- b[3:] = a;
- rpc.rc <-= (b, nil);
- }
- done(rpc: ref Rpc, ai: ref Authinfo)
- {
- rpc.r.ai = ai;
- rpc.r.done = 1;
- if(ai != nil)
- reply(rpc, "done haveai");
- else
- reply(rpc, "done");
- }
- phase(rpc: ref Rpc, s: string)
- {
- reply(rpc, "phase "+s);
- }
- needkey(rpc: ref Rpc, attrs: list of ref Attr)
- {
- reply(rpc, "needkey "+attrtext(attrs));
- }
- reply(rpc: ref Rpc, s: string)
- {
- rpc.rc <-= reads(s, 0, rpc.nbytes);
- }
- puta(a: array of byte, n: int, v: array of byte): int
- {
- if(n < 0)
- return -1;
- c := len v;
- if(n+2+c > len a)
- return -1;
- a[n++] = byte c;
- a[n++] = byte (c>>8);
- a[n:] = v;
- return n + len v;
- }
- packai(ai: ref Authinfo): array of byte
- {
- a := array[1024] of byte;
- i := puta(a, 0, array of byte ai.cuid);
- i = puta(a, i, array of byte ai.suid);
- i = puta(a, i, array of byte ai.cap);
- i = puta(a, i, ai.secret);
- if(i < 0)
- return nil;
- return a[0:i];
- }
- op(a: array of byte, ops: array of (int, string)): (int, array of byte)
- {
- arg: array of byte;
- for(i := 0; i < len a; i++)
- if(a[i] == byte ' '){
- if(i+1 < len a)
- arg = a[i+1:];
- break;
- }
- s := string a[0:i];
- for(i = 0; i < len ops; i++){
- (cmd, name) := ops[i];
- if(s == name)
- return (cmd, arg);
- }
- return (Ogok, arg);
- }
- parseline(s: string): list of ref Attr
- {
- fld := str->unquoted(s);
- rfld := fld;
- for(fld = nil; rfld != nil; rfld = tl rfld)
- fld = (hd rfld) :: fld;
- attrs: list of ref Attr;
- for(; fld != nil; fld = tl fld){
- n := hd fld;
- a := "";
- tag := Aattr;
- for(i:=0; i<len n; i++)
- if(n[i] == '='){
- a = n[i+1:];
- n = n[0:i];
- tag = Aval;
- }
- if(len n == 0)
- continue;
- if(tag == Aattr && len n > 1 && n[len n-1] == '?'){
- tag = Aquery;
- n = n[0:len n-1];
- }
- attrs = ref Attr(tag, n, a) :: attrs;
- }
- return attrs;
- }
- Attr.text(a: self ref Attr): string
- {
- case a.tag {
- Aattr =>
- return a.name;
- Aval =>
- return a.name+"="+a.val;
- Aquery =>
- return a.name+"?";
- * =>
- return "??";
- }
- }
- attrtext(attrs: list of ref Attr): string
- {
- s := "";
- sp := 0;
- for(; attrs != nil; attrs = tl attrs){
- if(sp)
- s[len s] = ' ';
- sp = 1;
- s += (hd attrs).text();
- }
- return s;
- }
- lookattr(attrs: list of ref Attr, n: string): ref Attr
- {
- for(; attrs != nil; attrs = tl attrs)
- if((a := hd attrs).tag != Aquery && a.name == n)
- return a;
- return nil;
- }
- lookattrval(attrs: list of ref Attr, n: string): string
- {
- if((a := lookattr(attrs, n)) != nil)
- return a.val;
- return nil;
- }
- anyattr(attrs: list of ref Attr, n: string): ref Attr
- {
- for(; attrs != nil; attrs = tl attrs)
- if((a := hd attrs).name == n)
- return a;
- return nil;
- }
- reverse[T](l: list of T): list of T
- {
- r: list of T;
- for(; l != nil; l = tl l)
- r = hd l :: r;
- return r;
- }
- setattrs(lv: list of ref Attr, rv: list of ref Attr): list of ref Attr
- {
- # new attributes
- nl: list of ref Attr;
- for(rl := rv; rl != nil; rl = tl rl)
- if(anyattr(lv, (hd rl).name) == nil)
- nl = ref(*hd rl) :: nl;
- # new values
- for(; lv != nil; lv = tl lv){
- a := lookattr(rv, (hd lv).name); # won't take queries
- if(a != nil)
- nl = ref *a :: nl;
- }
- return reverse(nl);
- }
- delattrs(lv: list of ref Attr, rv: list of ref Attr): list of ref Attr
- {
- nl: list of ref Attr;
- for(; lv != nil; lv = tl lv)
- if(anyattr(rv, (hd lv).name) == nil)
- nl = hd lv :: nl;
- return reverse(nl);
- }
- ignored(s: string): int
- {
- return s == "role" || s == "disabled";
- }
- matchattr(attrs: list of ref Attr, pat: ref Attr): int
- {
- return (b := lookattr(attrs, pat.name)) != nil && (pat.tag == Aquery || b.val == pat.val) ||
- ignored(pat.name);
- }
- matchattrs(pub: list of ref Attr, secret: list of ref Attr, pats: list of ref Attr): int
- {
- for(pl := pats; pl != nil; pl = tl pl)
- if(!matchattr(pub, hd pl) && !matchattr(secret, hd pl))
- return 0;
- return 1;
- }
- sortattrs(attrs: list of ref Attr): list of ref Attr
- {
- a := array[len attrs] of ref Attr;
- i := 0;
- for(l := attrs; l != nil; l = tl l)
- a[i++] = hd l;
- shellsort(a);
- for(i = 0; i < len a; i++)
- l = a[i] :: l;
- return l;
- }
- # sort into decreasing order (we'll reverse the list)
- shellsort(a: array of ref Attr)
- {
- n := len a;
- for(gap := n; gap > 0; ) {
- gap /= 2;
- max := n-gap;
- ex: int;
- do{
- ex = 0;
- for(i := 0; i < max; i++) {
- j := i+gap;
- if(a[i].name > a[j].name || a[i].name == nil) {
- t := a[i]; a[i] = a[j]; a[j] = t;
- ex = 1;
- }
- }
- }while(ex);
- }
- }
- findkey(keys: array of ref Key, attrs: list of ref Attr): ref Key
- {
- if(debug)
- sys->print("findkey %q\n", attrtext(attrs));
- for(i := 0; i < len keys; i++)
- if((k := keys[i]) != nil && matchattrs(k.attrs, k.secrets, attrs))
- return k;
- return nil;
- }
- findkeys(keys: array of ref Key, attrs: list of ref Attr): list of ref Key
- {
- if(debug)
- sys->print("findkey %q\n", attrtext(attrs));
- kl: list of ref Key;
- for(i := 0; i < len keys; i++)
- if((k := keys[i]) != nil && matchattrs(k.attrs, k.secrets, attrs))
- kl = k :: kl;
- return reverse(kl);
- }
- delkey(keys: array of ref Key, attrs: list of ref Attr): int
- {
- nk := 0;
- for(i := 0; i < len keys; i++)
- if((k := keys[i]) != nil)
- if(matchattrs(k.attrs, k.secrets, attrs)){
- nk++;
- keys[i] = nil;
- }
- return nk;
- }
- Key.mk(attrs: list of ref Attr): ref Key
- {
- k := ref Key;
- for(; attrs != nil; attrs = tl attrs){
- a := hd attrs;
- if(a.name != nil){
- if(a.name[0] == '!')
- k.secrets = a :: k.secrets;
- else
- k.attrs = a :: k.attrs;
- }
- }
- if(k.attrs != nil || k.secrets != nil)
- return k;
- return nil;
- }
- addkey(keys: array of ref Key, k: ref Key): array of ref Key
- {
- for(i := 0; i < len keys; i++)
- if(keys[i] == nil){
- keys[i] = k;
- return keys;
- }
- n := array[len keys+1] of ref Key;
- n[0:] = keys;
- n[len keys] = k;
- return n;
- }
- Key.text(k: self ref Key): string
- {
- s := attrtext(k.attrs);
- if(s != nil && k.secrets != nil)
- s[len s] = ' ';
- return s + attrtext(k.secrets);
- }
- Key.safetext(k: self ref Key): string
- {
- s := attrtext(sortattrs(k.attrs));
- sp := s != nil;
- for(sl := k.secrets; sl != nil; sl = tl sl){
- if(sp)
- s[len s] = ' ';
- s += sys->sprint("%s?", (hd sl).name);
- }
- return s;
- }
- any(s: string, t: string): int
- {
- for(i := 0; i < len s; i++)
- for(j := 0; j < len t; j++)
- if(s[i] == t[j])
- return 1;
- return 0;
- }
- IO.findkey(io: self ref IO, attrs: list of ref Attr, extra: string): (ref Key, string)
- {
- (kl, err) := io.findkeys(attrs, extra);
- if(kl != nil)
- return (hd kl, err);
- return (nil, err);
- }
- IO.findkeys(nil: self ref IO, attrs: list of ref Attr, extra: string): (list of ref Key, string)
- {
- ea := parseline(extra);
- for(; ea != nil; ea = tl ea)
- attrs = hd ea :: attrs;
- kc := chan of (list of ref Key, string);
- keymanc <-= (attrs, 1, kc); # TO DO: 1 => 0 for not needed
- return <-kc;
- }
- IO.needkey(nil: self ref IO, attrs: list of ref Attr, extra: string): (ref Key, string)
- {
- ea := parseline(extra);
- for(; ea != nil; ea = tl ea)
- attrs = hd ea :: attrs;
- kc := chan of (list of ref Key, string);
- keymanc <-= (attrs, 1, kc);
- (kl, err) := <-kc;
- if(kl != nil)
- return (hd kl, err);
- return (nil, err);
- }
- IO.read(io: self ref IO): array of byte
- {
- io.ok();
- while((rpc := rio(io.f)) != nil)
- case rpc.cmd {
- * =>
- phase(rpc, "protocol phase error");
- Oauthinfo =>
- reply(rpc, "error authentication unfinished");
- Owrite =>
- io.rpc = rpc;
- if(rpc.arg == nil)
- rpc.arg = array[0] of byte;
- return rpc.arg;
- }
- exit;
- }
- IO.readn(io: self ref IO, n: int): array of byte
- {
- while((buf := io.read()) != nil && len buf < n)
- io.toosmall(n);
- return buf;
- }
- IO.write(io: self ref IO, buf: array of byte, n: int): int
- {
- io.ok();
- while((rpc := rio(io.f)) != nil)
- case rpc.cmd {
- Oread =>
- if(rpc.nbytes-3 >= n){
- okdata(rpc, buf[0:n]);
- return n;
- }
- io.rpc = rpc;
- io.toosmall(n+3);
- Oauthinfo =>
- reply(rpc, "error authentication unfinished");
- * =>
- phase(rpc, "protocol phase error");
- }
- exit;
- }
- IO.rdwr(io: self ref IO): array of byte
- {
- io.ok();
- while((rpc := rio(io.f)) != nil)
- case rpc.cmd {
- Oread =>
- io.rpc = rpc;
- if(rpc.nbytes >= 3)
- return nil;
- io.toosmall(128+3); # make them read something
- Owrite =>
- io.rpc = rpc;
- if(rpc.arg == nil)
- rpc.arg = array[0] of byte;
- return rpc.arg;
- Oauthinfo =>
- reply(rpc, "error authentication unfinished");
- * =>
- phase(rpc, "protocol phase error");
- }
- exit;
- }
- IO.reply2read(io: self ref IO, buf: array of byte, n: int): int
- {
- if(io.rpc == nil)
- return 0;
- rpc := io.rpc;
- if(rpc.cmd != Oread){
- io.rpc = nil;
- phase(rpc, "internal phase error");
- return 0;
- }
- if(rpc.nbytes-3 < n){
- io.toosmall(n+3);
- return 0;
- }
- io.rpc = nil;
- okdata(rpc, buf[0:n]);
- return 1;
- }
- IO.ok(io: self ref IO)
- {
- if(io.rpc != nil){
- reply(io.rpc, "ok");
- io.rpc = nil;
- }
- }
- IO.toosmall(io: self ref IO, n: int)
- {
- if(io.rpc != nil){
- reply(io.rpc, sys->sprint("toosmall %d", n));
- io.rpc = nil;
- }
- }
- IO.error(io: self ref IO, s: string)
- {
- if(io.rpc != nil){
- io.rpc.rc <-= (nil, "error "+s);
- io.rpc = nil;
- }
- }
- IO.done(io: self ref IO, ai: ref Authinfo)
- {
- io.f.ai = ai;
- io.ok();
- while((rpc := rio(io.f)) != nil)
- case rpc.cmd {
- Oread or Owrite =>
- done(rpc, ai);
- return;
- * =>
- phase(rpc, "protocol phase error");
- }
- }
- memrandom(a: array of byte, n: int)
- {
- if(0){
- # speed up testing
- for(i := 0; i < len a; i++)
- a[i] = byte i;
- return;
- }
- fd := sys->open("/dev/notquiterandom", Sys->OREAD);
- if(fd == nil)
- err("can't open /dev/notquiterandom");
- if(sys->read(fd, a, n) != n)
- err("can't read /dev/notquiterandom");
- }
- eqbytes(a, b: array of byte): int
- {
- if(len a != len b)
- return 0;
- for(i := 0; i < len a; i++)
- if(a[i] != b[i])
- return 0;
- return 1;
- }
- netmkaddr(addr, net, svc: string): string
- {
- 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);
- }