diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/collab/servers | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/collab/servers')
| -rw-r--r-- | appl/collab/servers/chatsrv.b | 263 | ||||
| -rw-r--r-- | appl/collab/servers/memfssrv.b | 20 | ||||
| -rw-r--r-- | appl/collab/servers/mpx.b | 301 | ||||
| -rw-r--r-- | appl/collab/servers/wbsrv.b | 226 |
4 files changed, 810 insertions, 0 deletions
diff --git a/appl/collab/servers/chatsrv.b b/appl/collab/servers/chatsrv.b new file mode 100644 index 00000000..072c0617 --- /dev/null +++ b/appl/collab/servers/chatsrv.b @@ -0,0 +1,263 @@ +implement Service; + +# +# simple text-based chat service +# + +include "sys.m"; + sys: Sys; + Qid: import Sys; + +include "styx.m"; + styx: Styx; + Tmsg, Rmsg: import Styx; + +include "styxservers.m"; + styxservers: Styxservers; + Styxserver, Navigator: import styxservers; + nametree: Nametree; + Tree: import nametree; + +include "../service.m"; + +Qdir, Qusers, Qmsgs: con iota; + +tc: chan of ref Tmsg; +srv: ref Styxserver; + +user := "inferno"; + +dir(name: string, perm: int, path: int): Sys->Dir +{ + d := sys->zerodir; + d.name = name; + d.uid = user; + d.gid = user; + d.qid.path = big path; + if(perm & Sys->DMDIR) + d.qid.qtype = Sys->QTDIR; + else + d.qid.qtype = Sys->QTFILE; + d.mode = perm; + return d; +} + +init(nil: list of string): (string, string, ref Sys->FD) +{ + sys = load Sys Sys->PATH; + styx = load Styx Styx->PATH; + if(styx == nil) + return (sys->sprint("can't load %s: %r", Styx->PATH), nil, nil); + styxservers = load Styxservers Styxservers->PATH; + if(styxservers == nil) + return (sys->sprint("can't load %s: %r", Styxservers->PATH), nil, nil); + nametree = load Nametree Nametree->PATH; + if(nametree == nil) + return (sys->sprint("can't load %s: %r", Nametree->PATH), nil, nil); + styx->init(); + styxservers->init(styx); + nametree->init(); + + (tree, treeop) := nametree->start(); + tree.create(big Qdir, dir(".", Sys->DMDIR|8r555, Qdir)); + tree.create(big Qdir, dir("users", 8r444, Qusers)); + tree.create(big Qdir, dir("msgs", 8r666, Qmsgs)); + + p := array [2] of ref Sys->FD; + if (sys->pipe(p) < 0){ + tree.quit(); + return (sys->sprint("cannot create pipe: %r"), nil, nil); + } + + nextmsg = ref Msg (0, nil, nil, nil); + + (tc, srv) = Styxserver.new(p[1], Navigator.new(treeop), big Qdir); + spawn chatsrv(tree); + + return (nil, "/", p[0]); +} + +chatsrv(tree: ref Tree) +{ + while((tmsg := <-tc) != nil){ + pick tm := tmsg { + Readerror => + break; + Flush => + cancelpending(tm.tag); + srv.reply(ref Rmsg.Flush(tm.tag)); + Open => + c := srv.open(tm); + if (c == nil) + break; + if (int c.path == Qmsgs){ + newmsgclient(tm.fid, c.uname); + #root[0].qid.vers++; # TO DO + } + Read => + c := srv.getfid(tm.fid); + if (c == nil || !c.isopen) { + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Ebadfid)); + break; + } + case int c.path { + Qdir => + srv.read(tm); + Qmsgs => + mc := getmsgclient(tm.fid); + if (mc == nil) { + srv.reply(ref Rmsg.Error(tm.tag, "internal error -- lost client")); + continue; + } + tm.offset = big 0; + msg := getnextmsg(mc); + if (msg == nil) { + if(mc.pending != nil) + srv.reply(ref Rmsg.Error(tm.tag, "read already pending")); + else + mc.pending = tm; + continue; + } + srv.reply(styxservers->readstr(tm, msg)); + Qusers => + srv.reply(styxservers->readstr(tm, usernames())); + * => + srv.reply(ref Rmsg.Error(tm.tag, "phase error -- bad path")); + } + Write => + c := srv.getfid(tm.fid); + if (c == nil || !c.isopen) { + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Ebadfid)); + continue; + } + if (int c.path != Qmsgs) { + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Eperm)); + continue; + } + writemsgclients(tm.fid, c.uname, string tm.data); + srv.reply(ref Rmsg.Write(tm.tag, len tm.data)); + Clunk => + c := srv.clunk(tm); + if (c != nil && int c.path == Qmsgs){ + closemsgclient(tm.fid); + # root[0].qid.vers++; # TO DO + } + * => + srv.default(tmsg); + } + } + tree.quit(); + sys->print("chatsrv exit\n"); +} + +Msg: adt { + fromfid: int; + from: string; + msg: string; + next: cyclic ref Msg; +}; + +Msgclient: adt { + fid: int; + name: string; + nextmsg: ref Msg; + pending: ref Tmsg.Read; + next: cyclic ref Msgclient; +}; + +nextmsg: ref Msg; +msgclients: ref Msgclient; + +usernames(): string +{ + s := ""; + for (c := msgclients; c != nil; c = c.next) + s += c.name+"\n"; + return s; +} + +newmsgclient(fid: int, name: string) +{ + writemsgclients(fid, nil, "+++ " + name + " has arrived"); + msgclients = ref Msgclient(fid, name, nextmsg, nil, msgclients); +} + +getmsgclient(fid: int): ref Msgclient +{ + for (c := msgclients; c != nil; c = c.next) + if (c.fid == fid) + return c; + return nil; +} + +cancelpending(tag: int) +{ + for (c := msgclients; c != nil; c = c.next) + if((tm := c.pending) != nil && tm.tag == tag){ + c.pending = nil; + break; + } +} + +closemsgclient(fid: int) +{ + prev: ref Msgclient; + s := ""; + for (c := msgclients; c != nil; c = c.next) { + if (c.fid == fid) { + if (prev == nil) + msgclients = c.next; + else + prev.next = c.next; + s = "--- " + c.name + " has left"; + break; + } + prev = c; + } + if (s != nil) + writemsgclients(fid, nil, s); +} + +writemsgclients(fromfid: int, from: string, msg: string) +{ + nm := ref Msg(0, nil, nil, nil); + nextmsg.fromfid = fromfid; + nextmsg.from = from; + nextmsg.msg = msg; + nextmsg.next = nm; + + for (c := msgclients; c != nil; c = c.next) { + if (c.pending != nil) { + s := msgtext(c, nextmsg); + srv.reply(styxservers->readstr(c.pending, s)); + c.pending = nil; + c.nextmsg = nm; + } + } + nextmsg = nm; +} + +getnextmsg(mc: ref Msgclient): string +{ +# uncomment next two lines to eliminate queued messages to self +# while(mc.nextmsg.next != nil && mc.nextmsg.fromfid == mc.fid) +# mc.nextmsg = mc.nextmsg.next; + if ((m := mc.nextmsg).next != nil){ + mc.nextmsg = m.next; + return msgtext(mc, m); + } + return nil; +} + +msgtext(mc: ref Msgclient, m: ref Msg): string +{ + prefix := ""; + if (m.from != nil) { + # not a system message + if (mc.fid == m.fromfid) + prefix = "<you>: "; + else + prefix = m.from + ": "; + } + return prefix + m.msg; +} diff --git a/appl/collab/servers/memfssrv.b b/appl/collab/servers/memfssrv.b new file mode 100644 index 00000000..8c44cd5d --- /dev/null +++ b/appl/collab/servers/memfssrv.b @@ -0,0 +1,20 @@ +implement Service; + +include "sys.m"; +include "../service.m"; +include "memfs.m"; + +init(nil : list of string) : (string, string, ref Sys->FD) +{ + sys := load Sys Sys->PATH; + memfs := load MemFS MemFS->PATH; + if (memfs == nil) { + err := sys->sprint("cannot load %s: %r", MemFS->PATH); + return (err, nil, nil); + } + err := memfs->init(); + if (err != nil) + return (err, nil, nil); + fd := memfs->newfs(1024 * 512); + return (nil, "/", fd); +} diff --git a/appl/collab/servers/mpx.b b/appl/collab/servers/mpx.b new file mode 100644 index 00000000..ea0c2c1c --- /dev/null +++ b/appl/collab/servers/mpx.b @@ -0,0 +1,301 @@ +implement Service; + +# +# 1 to many and many to 1 multiplexor +# + +include "sys.m"; + sys: Sys; + Qid: import Sys; + +include "styx.m"; + styx: Styx; + Tmsg, Rmsg: import styx; + +include "styxservers.m"; + styxservers: Styxservers; + Styxserver, Navigator: import styxservers; + nametree: Nametree; + Tree: import nametree; + +include "service.m"; + +include "messages.m"; + messages: Messages; + Msg, Msglist, Readreq, User: import messages; + +Qdir, Qroot, Qusers, Qleaf: con iota; + +srv: ref Styxserver; +clientidgen := 0; + +Einactive: con "not currently active"; + +toleaf: ref Msglist; +toroot: ref Msglist; +userlist: list of ref User; + +user := "inferno"; + +dir(name: string, perm: int, path: int): Sys->Dir +{ + d := sys->zerodir; + d.name = name; + d.uid = user; + d.gid = user; + d.qid.path = big path; + if(perm & Sys->DMDIR) + d.qid.qtype = Sys->QTDIR; + else + d.qid.qtype = Sys->QTFILE; + d.mode = perm; + return d; +} + +init(nil: list of string): (string, string, ref Sys->FD) +{ + sys = load Sys Sys->PATH; + styx = load Styx Styx->PATH; + if(styx == nil) + return (sys->sprint("can't load %s: %r", Styx->PATH), nil, nil); + styxservers = load Styxservers Styxservers->PATH; + if(styxservers == nil) + return (sys->sprint("can't load %s: %r", Styxservers->PATH), nil, nil); + nametree = load Nametree Nametree->PATH; + if(nametree == nil) + return (sys->sprint("can't load %s: %r", Nametree->PATH), nil, nil); + styx->init(); + styxservers->init(styx); +styxservers->traceset(1); + nametree->init(); + messages = load Messages Messages->PATH; + if(messages == nil) + return (sys->sprint("can't load %s: %r", Messages->PATH), nil, nil); + + (tree, treeop) := nametree->start(); + tree.create(big Qdir, dir(".", Sys->DMDIR|8r555, Qdir)); + tree.create(big Qdir, dir("leaf", 8r666, Qleaf)); + tree.create(big Qdir, dir("root", 8r666, Qroot)); + tree.create(big Qdir, dir("users", 8r444, Qusers)); + + p := array [2] of ref Sys->FD; + if (sys->pipe(p) < 0){ + tree.quit(); + return (sys->sprint("can't create pipe: %r"), nil, nil); + } + + toleaf = Msglist.new(); + toroot = Msglist.new(); + + tc: chan of ref Tmsg; + (tc, srv) = Styxserver.new(p[1], Navigator.new(treeop), big Qdir); + spawn mpx(tc, tree); + + return (nil, "/", p[0]); +} + +mpx(tc: chan of ref Tmsg, tree: ref Tree) +{ + root: ref User; + while((tmsg := <-tc) != nil){ + pick tm := tmsg { + Readerror => + break; + Open => + c := srv.getfid(tm.fid); + if(c == nil || c.isopen){ + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Ebadfid)); + continue; + } + case int c.path { + Qroot => + if(root != nil){ + srv.reply(ref Rmsg.Error(tm.tag, sys->sprint("interaction already directed by %s", root.name))); + continue; + } + c = srv.open(tm); + if (c == nil) + continue; + root = ref User(0, tm.fid, c.uname, nil); + root.initqueue(toroot); + Qleaf => + if(root == nil){ + srv.reply(ref Rmsg.Error(tm.tag, Einactive)); + continue; + } + c = srv.open(tm); + if (c == nil) + continue; + userarrives(tm.fid, c.uname); + # mpxdir[1].qid.vers++; # TO DO + * => + srv.open(tm); + } + Read => + c := srv.getfid(tm.fid); + if (c == nil || !c.isopen) { + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Ebadfid)); + continue; + } + case int c.path { + Qdir => + srv.read(tm); + Qroot => + tm.offset = big 0; + m := qread(toroot, root, tm, 1); + if(m != nil) + srv.reply(ref Rmsg.Read(tm.tag, m.data)); + Qleaf => + u := fid2user(tm.fid); + if (u == nil) { + srv.reply(ref Rmsg.Error(tm.tag, "internal error -- lost user")); + continue; + } + tm.offset = big 0; + m := qread(toleaf, u, tm, 0); + if(m == nil){ + if(root == nil) + srv.reply(ref Rmsg.Read(tm.tag, nil)); + else + qread(toleaf, u, tm, 1); # put us on the wait queue + }else + srv.reply(ref Rmsg.Read(tm.tag, m.data)); + Qusers => + srv.reply(styxservers->readstr(tm, usernames())); + * => + srv.reply(ref Rmsg.Error(tm.tag, "phase error -- bad path")); + } + Write => + c := srv.getfid(tm.fid); + if (c == nil || !c.isopen) { + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Ebadfid)); + continue; + } + case int c.path { + Qroot => + qwrite(toleaf, msg(root, 'M', tm.data)); + srv.reply(ref Rmsg.Write(tm.tag, len tm.data)); + Qleaf => + u := fid2user(tm.fid); + if(u == nil) { + srv.reply(ref Rmsg.Error(tm.tag, "internal error -- lost user")); + continue; + } + if(root == nil){ + srv.reply(ref Rmsg.Error(tm.tag, Einactive)); + continue; + } + qwrite(toroot, msg(u, 'm', tm.data)); + srv.reply(ref Rmsg.Write(tm.tag, len tm.data)); + * => + srv.reply(ref Rmsg.Error(tm.tag, Styxservers->Eperm)); + } + Flush => + cancelpending(tm.tag); + srv.reply(ref Rmsg.Flush(tm.tag)); + Clunk => + c := srv.getfid(tm.fid); + if(c.isopen){ + case int c.path { + Qroot => + # shut down? + qwrite(toleaf, msg(root, 'L', nil)); + root = nil; + Qleaf => + userleaves(tm.fid); + # mpxdir[1].qid.vers++; # TO DO + } + } + * => + srv.default(tmsg); + } + } + tree.quit(); + sys->print("mpx exit\n"); +} + +mpxseqgen := 0; + +time(): int +{ + return ++mpxseqgen; # server time; assumes 2^31-1 is large enough +} + +userarrives(fid: int, name: string) +{ + u := User.new(fid, name); + qwrite(toroot, msg(u, 'a', nil)); + u.initqueue(toleaf); # sees leaf messages from now on + userlist = u :: userlist; +} + +fid2user(fid: int): ref User +{ + for(ul := userlist; ul != nil; ul = tl ul) + if((u := hd ul).fid == fid) + return u; + return nil; +} + +userleaves(fid: int) +{ + ul := userlist; + userlist = nil; + u: ref User; + for(; ul != nil; ul = tl ul) + if((hd ul).fid != fid) + userlist = hd ul :: userlist; + else + u = hd ul; + if(u != nil) + qwrite(toroot, msg(u, 'l', nil)); +} + +usernames(): string +{ + s := ""; + for(ul := userlist; ul != nil; ul = tl ul){ + u := hd ul; + s += string u.id+" "+u.name+"\n"; + } + return s; +} + +qwrite(msgs: ref Msglist, m: ref Msg) +{ + pending := msgs.write(m); + for(; pending != nil; pending = tl pending){ + (u, req) := hd pending; + m = u.read(); # must succeed, or the code is wrong + data := m.data; + if(req.count < len data) + data = data[0:req.count]; + srv.reply(ref Rmsg.Read(req.tag, data)); + } +} + +qread(msgs: ref Msglist, u: ref User, tm: ref Tmsg.Read, wait: int): ref Msg +{ + m := u.read(); + if(m != nil){ + if(tm.count < len m.data) + m.data = m.data[0:tm.count]; + }else if(wait) + msgs.wait(u, ref Readreq(tm.tag, tm.fid, tm.count, tm.offset)); + return m; +} + +cancelpending(tag: int) +{ + toroot.flushtag(tag); + toleaf.flushtag(tag); +} + +msg(u: ref User, op: int, data: array of byte): ref Msg +{ + a := sys->aprint("%ud %d %c %s ", time(), u.id, op, u.name); + m := ref Msg(u, array[len a + len data] of byte, nil); + m.data[0:] = a; + m.data[len a:] = data; + return m; +} diff --git a/appl/collab/servers/wbsrv.b b/appl/collab/servers/wbsrv.b new file mode 100644 index 00000000..2492db7e --- /dev/null +++ b/appl/collab/servers/wbsrv.b @@ -0,0 +1,226 @@ +implement Service; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Chans, Display, Image, Rect, Point : import draw; + +include "../service.m"; + +WBW : con 234; +WBH : con 279; + +init(nil : list of string) : (string, string, ref Sys->FD) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + if (draw == nil) + return ("cannot load Draw module", nil, nil); + + p := array [2] of ref Sys->FD; + if (sys->pipe(p) == -1) + return (sys->sprint("cannot create pipe: %r"), nil, nil); + + display := Display.allocate(nil); + if (display == nil) + return (sys->sprint("cannot allocate display: %r"), nil, nil); + + r := Rect(Point(0,0), Point(WBW, WBH)); + wb := display.newimage(r, Draw->CMAP8, 0, Draw->White); + if (wb == nil) + return (sys->sprint("cannot allocate whiteboard image: %r"), nil, nil); + + nextmsg = ref Msg (nil, nil); + spawn wbsrv(p[1], wb); + return (nil, "/chan", p[0]); +} + +wbsrv(fd : ref Sys->FD, wb: ref Image) +{ + sys->pctl(Sys->FORKNS, nil); + sys->unmount(nil, "/chan"); + sys->bind("#s", "/chan", Sys->MREPL); + + bit := sys->file2chan("/chan", "wb.bit"); + strokes := sys->file2chan("/chan", "strokes"); + + hangup := chan of int; + spawn export(fd, hangup); + + nwbbytes := draw->bytesperline(wb.r, wb.depth) * wb.r.dy(); + bithdr := sys->aprint("%11s %11d %11d %11d %11d ", wb.chans.text(), 0, 0, WBW, WBH); + + for (;;) alt { + <-hangup => + sys->print("whiteboard:hangup\n"); + return; + + (offset, count, fid, r) := <-bit.read => + if (r == nil) { + closeclient(fid); + continue; + } + c := getclient(fid); + if (c == nil) { + # new client + c = newclient(fid); + data := array [len bithdr + nwbbytes] of byte; + data[0:] = bithdr; + wb.readpixels(wb.r, data[len bithdr:]); + c.bitdata = data; + } + if (offset >= len c.bitdata) { + rreply(r, (nil, nil)); + continue; + } + rreply(r, (c.bitdata[offset:], nil)); + + (offset, data, fid, w) := <-bit.write => + if (w != nil) + wreply(w, (0, "permission denied")); + + (offset, count, fid, r) := <-strokes.read => + if (r == nil) { + closeclient(fid); + continue; + } + c := getclient(fid); + if (c == nil) { + c = newclient(fid); + c.nextmsg = nextmsg; + } + d := c.nextmsg.data; + if (d == nil) { + c.pending = r; + c.pendlen = count; + continue; + } + c.nextmsg = c.nextmsg.next; + rreply(r, (d, nil)); + + (offset, data, fid, w) := <-strokes.write => + if (w == nil) { + closeclient(fid); + continue; + } + err := drawstrokes(wb, data); + if (err != nil) { + wreply(w, (0, err)); + continue; + } + wreply(w, (len data, nil)); + writeclients(data); + } +} + +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=>; + * =>; + } +} + +export(fd : ref Sys->FD, done : chan of int) +{ + sys->export(fd, "/", Sys->EXPWAIT); + done <-= 1; +} + +Msg : adt { + data : array of byte; + next : cyclic ref Msg; +}; + +Client : adt { + fid : int; + bitdata : array of byte; # bit file client + nextmsg : ref Msg; # strokes file client + pending : Sys->Rread; + pendlen : int; +}; + +nextmsg : ref Msg; +clients : list of ref Client; + +newclient(fid : int) : ref Client +{ + c := ref Client(fid, nil, nil, nil, 0); + clients = c :: clients; + return c; +} + +getclient(fid : int) : ref Client +{ + for(cl := clients; cl != nil; cl = tl cl) + if((c := hd cl).fid == fid) + return c; + return nil; +} + +closeclient(fid : int) +{ + nl: list of ref Client; + for(cl := clients; cl != nil; cl = tl cl) + if((hd cl).fid != fid) + nl = hd cl :: nl; + clients = nl; +} + +writeclients(data : array of byte) +{ + nm := ref Msg(nil, nil); + nextmsg.data = data; + nextmsg.next = nm; + + for(cl := clients; cl != nil; cl = tl cl){ + if ((c := hd cl).pending != nil) { + n := c.pendlen; + if (n > len data) + n = len data; + alt{ + c.pending <-= (data[0:n], nil) => ; + * => ; + } + c.pending = nil; + c.nextmsg = nm; + } + } + nextmsg = nm; +} + +# data: colour width p0 p1 pn* + +drawstrokes(wb: ref Image, data : array of byte) : string +{ + (n, toks) := sys->tokenize(string data, " "); + if (n < 6 || n & 1) + return "bad data"; + + colour, width, x, y : int; + (colour, toks) = (int hd toks, tl toks); + (width, toks) = (int hd toks, tl toks); + (x, toks) = (int hd toks, tl toks); + (y, toks) = (int hd toks, tl toks); + pen := wb.display.newimage(Rect(Point(0,0), Point(1,1)), Draw->CMAP8, 1, colour); + p0 := Point(x, y); + while (toks != nil) { + (x, toks) = (int hd toks, tl toks); + (y, toks) = (int hd toks, tl toks); + p1 := Point(x, y); + # could use poly() instead of line() + wb.line(p0, p1, Draw->Endsquare, Draw->Endsquare, width, pen, pen.r.min); + p0 = p1; + } + return nil; +} |
