diff options
Diffstat (limited to 'appl/collab/srvmgr.b')
| -rw-r--r-- | appl/collab/srvmgr.b | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/appl/collab/srvmgr.b b/appl/collab/srvmgr.b new file mode 100644 index 00000000..4794f65d --- /dev/null +++ b/appl/collab/srvmgr.b @@ -0,0 +1,190 @@ +implement Srvmgr; + +include "sys.m"; + sys: Sys; + +include "srvmgr.m"; +include "service.m"; +include "cfg.m"; + +Srvinfo: adt { + name: string; + path: string; + args: list of string; +}; + +services: list of ref Srvinfo; + +init(srvdir: string): (string, chan of ref Srvreq) +{ + sys = load Sys Sys->PATH; + cfg := load Cfg Cfg->PATH; + cfgpath := srvdir + "/services.cfg"; + if (cfg == nil) + return (sys->sprint("cannot load %s: %r", Cfg->PATH), nil); + err := cfg->init(cfgpath); + if (err != nil) + return (err, nil); + + (err, services) = parsecfg(cfgpath, srvdir, cfg); + if (err != nil) + return (err, nil); + + rc := chan of ref Srvreq; + spawn srv(rc); + return (nil, rc); +} + +parsecfg(p, srvdir: string, cfg: Cfg): (string, list of ref Srvinfo) +{ + srvlist: list of ref Srvinfo; + Record, Tuple: import cfg; + + for (slist := cfg->getkeys(); slist != nil; slist = tl slist) { + name := hd slist; + matches := cfg->lookup(name); + if (len matches > 1) { + (nil, duplicate) := hd tl matches; + primary := hd duplicate.tuples; + lnum := primary.lnum; + err := sys->sprint("%s:%d: duplicate service name %s", p, lnum, name); + return (err, nil); + } + (nil, r) := hd matches; + lnum := (hd r.tuples).lnum; + + (path, tuple) := r.lookup("path"); + if (path == nil) { + err := sys->sprint("%s:%d: missing path for service %s", p, lnum, name); + return (err, nil); + } + if (path[0] != '/') + path = srvdir + "/" + path; + + args: list of string = nil; + for (tuples := tl r.tuples; tuples != nil; tuples = tl tuples) { + t := hd tuples; + arg := t.lookup("arg"); + if (arg != nil) + args = arg :: args; + } + nargs: list of string = nil; + for (; args != nil; args = tl args) + nargs = hd args :: nargs; + srvlist = ref Srvinfo(name, path, args) ::srvlist; + } + if (srvlist == nil) { + err := sys->sprint("%s: no services", p); + return (err, nil); + } + return (nil, srvlist); +} + +srv(rc: chan of ref Srvreq) +{ + for (;;) { + req := <- rc; + id := req.sname + " " + req.id; + pick r := req { + Acquire => + # r.user not used, but could control access + service := acquire(id); + err := ""; + if (service.fd == nil) { + (err, service.root, service.fd) = startservice(req.sname); + if (err != nil) + release(id); + } + r.reply <-= (err, service.root, service.fd); + Release => + release(id); + } + } +} + +# +# returns (error, service root, service FD) +# +startservice(name: string): (string, string, ref Sys->FD) +{ +sys->print("startservice [%s]\n", name); + srv: ref Srvinfo; + for (sl := services; sl != nil; sl = tl sl) { + s := hd sl; + if (s.name == name) { + srv = s; + break; + } + } + if (srv == nil) + return ("unknown service", nil, nil); + + service := load Service srv.path; + if (service == nil) { + err := sys->sprint("cannot load %s: %r", srv.path); + return (err, nil, nil); + } + + return service->init(srv.args); +} + +Srvmap: adt { + id: string; + root: string; + fd: ref Sys->FD; + nref: int; + next: cyclic ref Srvmap; +}; + +PRIME: con 211; +buckets := array[PRIME] of ref Srvmap; + +hash(id: string): int +{ + # HashPJW + h := 0; + for (i := 0; i < len id; i++) { + h = (h << 4) + id[i]; + g := h & int 16rf0000000; + if (g != 0) { + h = h ^ ((g >> 24) & 16rff); + h = h ^ g; + } + } + if (h < 0) + h &= ~(1<<31); + return int (h % PRIME); +} + +acquire(id: string): ref Srvmap +{ + h := hash(id); + for (p := buckets[h]; p != nil; p = p.next) + if (p.id == id) { + p.nref++; + return p; + } + p = ref Srvmap(id, nil, nil, 1, buckets[h]); + buckets[h] = p; + return p; +} + +release(id: string) +{ + h :=hash(id); + prev: ref Srvmap; + for (p := buckets[h]; p != nil; p = p.next) { + if (p.id == id){ + p.nref--; + if (p.nref == 0) { + sys->print("release [%s]\n", p.id); + if (prev == nil) + buckets[h] = p.next; + else + prev.next = p.next; + } + return; + } + prev = p; + } +} |
