implement Proxy; include "sys.m"; sys: Sys; include "srvmgr.m"; include "proxy.m"; Srvreq, Srvreply: import Srvmgr; init(root: string, fd: ref Sys->FD, rc: chan of ref Srvreq, user: string) { sys = load Sys Sys->PATH; if(fd == nil) raise "err: fd for export was nil"; sys->chdir(root); sys->bind("export/services", "export/services", Sys->MCREATE); sys->bind("#s", "export/services", Sys->MBEFORE); ctlio := sys->file2chan("export/services", "ctl"); if(ctlio == nil) raise "err: export/services file2chan failed -- " + root + "/export/services directory does not exist" ; hangup := chan of int; spawn export(fd, "export", hangup); fd = nil; for (;;) alt { <- hangup => # closedown all clients sys->print("client exit [%s]\n", user); rmclients(rc); return; (offset, count, fid, r) := <- ctlio.read => client := fid2client(fid); if (r == nil) { if (client != nil) rmclient(rc, client); continue; } if (client == nil) { rreply(r, (nil, "service not set")); continue; } rreply(r, reads(client.path, offset, count)); (offset, data, fid, w) := <- ctlio.write => client := fid2client(fid); if (w == nil) { if (client != nil) rmclient(rc, client); continue; } if (client != nil) { wreply(w, (0, "service set")); continue; } err := newclient(rc, user, fid, string data); if (err != nil) wreply(w, (0, err)); else wreply(w, (len data, nil)); } } rreply(rc: chan of (array of byte, string), reply: (array of byte, string)) { alt { rc <-= reply =>; * =>; } } wreply(wc: chan of (int, string), reply: (int, string)) { alt { wc <-= reply=>; * =>; } } reads(str: string, off: big, nbytes: int): (array of byte, string) { bstr := array of byte str; slen := len bstr; if(off < big 0 || off >= big slen) return (nil, nil); if(off + big nbytes > big slen) nbytes = slen - int off; # TODO potential bug truncating big to int if(nbytes <= 0) return (nil, nil); return (bstr[int off:int off+nbytes], nil); # TODO potential bug truncating big to int } export(exportfd: ref Sys->FD, dir: string, done: chan of int) { sys->export(exportfd, dir, Sys->EXPWAIT); done <-= 1; } Client: adt { fid: int; path: string; sname: string; id: string; }; clients: list of ref Client; freepaths: list of string; nextpath := 0; fid2client(fid: int): ref Client { for(cl := clients; cl != nil; cl = tl cl) if ((c := hd cl).fid == fid) return c; return nil; } newclient(rc: chan of ref Srvreq, user: string, fid: int, cmd: string): string { sys->print("new Client %s [%s]\n", user, cmd); for (i := 0; i < len cmd; i++) if (cmd[i] == ' ') break; if (i == 0 || i == len cmd) return "bad command"; sname := cmd[:i]; id := cmd[i:]; reply := chan of Srvreply; rc <-= ref Srvreq.Acquire(sname, id, user, reply); (err, root, fd) := <- reply; if (err != nil) return err; path := ""; if (freepaths != nil) (path, freepaths) = (hd freepaths, tl freepaths); else path = string nextpath++; sys->mount(fd, nil, "mnt", Sys->MREPL, nil); # connection to the active service fs mkdir("export/services/"+path); sys->bind("mnt/"+root, "export/services/"+path, Sys->MREPL|Sys->MCREATE); sys->unmount("mnt", nil); clients = ref Client(fid, path, sname, id) :: clients; return nil; } rmclient(rc: chan of ref Srvreq, client: ref Client) { sys->print("rmclient [%s %s]\n", client.sname, client.id); nl: list of ref Client; for(cl := clients; cl != nil; cl = tl cl) if((c := hd cl) == client){ sys->unmount("export/services/" + client.path, nil); freepaths = client.path :: freepaths; rc <-= ref Srvreq.Release(client.sname, client.id); } else nl = c :: nl; clients = nl; } rmclients(rc: chan of ref Srvreq) { for(cl := clients; cl != nil; cl = tl cl){ c := hd cl; sys->print("rmclients [%s %s]\n", c.sname, c.id); rc <-= ref Srvreq.Release(c.sname, c.id); } clients = nil; } mkdir(path: string) { sys->print("mkdir [%s]\n", path); sys->create(path, Sys->OREAD, 8r777 | Sys->DMDIR); }