ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/collab/srvmgr.b/
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;
}
}