summaryrefslogtreecommitdiff
path: root/appl/collab/servers/chatsrv.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/collab/servers/chatsrv.b')
-rw-r--r--appl/collab/servers/chatsrv.b263
1 files changed, 263 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;
+}