STYXLIB(2)                                             STYXLIB(2)

     NAME
          styxlib - Styx server implementation module

     SYNOPSIS
          include "styxlib.m";
          styxlib := load Styxlib Styxlib->PATH;
          Styxserver, Chan: import styxlib;

          Styxserver: adt {
               new: fn(fd: ref Sys->FD): (chan of ref Tmsg, ref Styxserver);
               reply: fn(srv: self ref Styxserver, m: ref Rmsg): int;

               devattach: fn(srv: self ref Styxserver, m: ref Tmsg.Attach):
                              ref Chan;
               devclone: fn(srv: self ref Styxserver, m: ref Tmsg.Clone):
                              ref Chan;
               devflush: fn(srv: self ref Styxserver, m: ref Tmsg.Flush);
               devwalk: fn(srv: self ref Styxserver, m: ref Tmsg.Walk,
                              gen: Dirgenmod, tab: array of Dirtab): ref Chan;
               devclunk: fn(srv: self ref Styxserver, m: ref Tmsg.Clunk):
                              ref Chan;
               devstat: fn(srv: self ref Styxserver, m: ref Tmsg.Stat,
                              gen: Dirgenmod, tab: array of Dirtab);
               devdirread: fn(srv: self ref Styxserver, m: ref Tmsg.Read,
                         gen: Dirgenmod, tab: array of Dirtab);
               devopen: fn(srv: self ref Styxserver, m: ref Tmsg.Open,
                         gen: Dirgenmod, tab: array of Dirtab): ref Chan;
               devremove: fn(srv: self ref Styxserver, m: ref Tmsg.Remove):
                              ref Chan;

               fidtochan: fn(srv: self ref Styxserver, fid: int): ref Chan;
               newchan: fn(srv: self ref Styxserver, fid: int): ref Chan;
               chanfree: fn(srv: self ref Styxserver, c: ref Chan);
               chanlist: fn(srv: self ref Styxserver): list of ref Chan;
          };
          Chan: adt {
               fid: int;
               qid: Sys->Qid;
               open: int;
               mode: int;
               uname: string;
               path: string;
               data: array of byte;
               isdir: fn(c: self ref Chan): int;
          };
          Dirtab: adt {
               name: string;
               qid: Sys->Qid;
               length: big;
               perm: int;

     Page 1                       Plan 9            (printed 11/17/24)

     STYXLIB(2)                                             STYXLIB(2)

          };
          Dirgenmod: module {
               dirgen: fn(srv: ref Styxlib->Styxserver, c: ref Styxlib->Chan,
                    tab: array of Styxlib->Dirtab, i: int): (int, Sys->Dir);
          };
          Tmsg: adt {
               tag: int;
               pick {
               Readerror => error: string;
               Attach => fid: int; uname, aname: string;
               Clone => fid, newfid: int;
               Clunk => fid: int;
               Create => fid, perm, mode: int; name: string;
               Flush => oldtag: int;
               Nop =>
               Open => fid, mode: int;
               Read =>  fid, count: int; offset: big;
               Remove => fid: int;
               Stat => fid: int;
               Walk => fid: int; name: string;
               Write => fid: int; offset: big; data: array of byte;
               Wstat => fid: int; stat: Sys->Dir;
               }
          };
          Rmsg: adt {
               tag: int;
               pick {
               Attach  => fid: int; qid: Sys->Qid;
               Clone => fid: int'
               Clunk => fid: int;
               Create => fid: int; qid: Sys->Qid;
               Error => err: string;
               Flush =>
               Nop =>
               Open => fid: int; qid: Sys->Qid;
               Read => fid: int; data: array of byte;
               Remove => fid: int;
               Stat => fid: int; stat: Sys->Dir;
               Walk => fid: int; qid: Sys->Qid;
               Write => fid, count: int;
               Wstat => fid: int;
               }
          };
          readbytes: fn(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read;
          readnum: fn(m: ref Tmsg.Read, val, size: int): ref Rmsg.Read;
          readstr: fn(m: ref Tmsg.Read, d: string): ref Rmsg.Read;
          openok: fn(omode, perm: int, uname, funame, fgname: string): int;
          openmode: fn(o: int): int;
          devdir: fn(c: ref Chan, qid: Sys->Qid, n: string, length: big,
               user: string, perm: int): Sys->Dir;
          dirgenmodule: fn(): Dirgenmod;
          d2tmsg: fn(d: array of byte): (int, ref Tmsg);

     Page 2                       Plan 9            (printed 11/17/24)

     STYXLIB(2)                                             STYXLIB(2)

          d2rmsg: fn(d: array of byte): (int, ref Rmsg);
          rmsg2d: fn(m: ref Rmsg, d: array of byte): int;
          tmsg2s: fn(m: ref Tmsg): string;
          rmsg2s: fn(m: ref Rmsg): string;
          convD2M: fn(d: array of byte, f: Sys->Dir): array of byte;
          convM2D: fn(d: array of byte): (array of byte, Sys->Dir);

          MAXRPC: con 128 + Sys->ATOMICIO;
          DIRLEN: con 116;

     DESCRIPTION
          The Styxlib module provides a framework for writing Limbo-
          implemented Styx servers.  Many of its functions parallel
          the generic device driver functions found in the kernel.  A
          thorough reading of section 5 of the manual is advised
          before using this module. Styxserver.new starts a thread
          reading Styx messages from fd. Fd should be a pipe or a data
          channel with a Styx client at its other end, usually mounted
          (see sys-bind(2)) in an Inferno namespace.  It returns a
          tuple of the form (tchan, srv) . As each complete message is
          read, it is bundled into a Tmsg adt and sent down tchan.
          Srv.reply takes one parameter, m, an Rmsg to be written to
          fd. Each Styx T-message (R-message) has a corresponding Tmsg
          (Rmsg) pick variant.  Tmsg.Readerror will be sent when an
          error has occurred reading from fd.

          Styxlib provides a number of utility functions to assist in
          the construction of certain of the more common styles of
          Styx servers. These may be used as appropriate in any par-
          ticular implementation. Most Tmsg types have an associated
          srv.devtype() handler function. The first argument to all of
          these functions is m, the Tmsg which is to be handled.  Many
          of them also take the following arguments.

          tab       An array of Dirtab adts, often hard-coded.

          gen       A module that implements the function
                    dirgen(srv, c, tab, i) which is called to enumer-
                    ate each item i on srv in the file represented by
                    c. Tab is passed through unchanged from the call-
                    ing function.  Dirgen returns a tuple of the form
                    (n,dir).  If successful, n should be 1, and dir
                    the i'th directory in c. If unsuccessful, n should
                    be -1.  Styxlib implements the function
                    dirgenmodule which returns a module implementing a
                    version of dirgen that generates entries directly
                    from tab.

          Between them, the dev* functions maintain a table associat-
          ing the integer fid found in Styx messages and a Chan adt
          holding current information about the fid. A Chan holds the

     Page 3                       Plan 9            (printed 11/17/24)

     STYXLIB(2)                                             STYXLIB(2)

          following information:

          fid       The fid used in the Styx messages.

          qid       The qid for the file. (see intro(5))

          open      Non-zero if the file has been opened for I/O.
                    Clone and walk requests should be refused for open
                    files.

          mode      The mode that the file was opened in (see sys-
                    open(2))

          uname     The username as given in the original Tattach from
                    which this Chan derives.

          path      The name of the file as used in its last walk
                    request.

          data      This can be used to store arbitrary data associ-
                    ated with the file.

        T-message Handler Functions
          The following generic handler functions are provided. Each
          one makes an appropriate reply to the client.

          srv.devattach(m)
                    Devattach creates a new Chan adt associated with
                    m.fid and returns it.

          srv.devclone(m)
                    Devclone copies the Chan associated with m.fid to
                    make a new Chan associated with m.newfid and
                    returns the new Chan.

          srv.devflush(m)
                    Devflush does nothing other than making the appro-
                    priate Rmsg.Flush reply.

          srv.devremove(m)
                    Devremove removes the Chan associated with m from
                    the fid table, replies to the client with a "per-
                    mission denied" message and returns the old Chan.

          srv.devclunk(m)
                    Devclunk removes the Chan associated with m from
                    the fid table and returns it.

          srv.devwalk(m, gen, tab)
                    Devwalk walks the Chan associated with m.fid
                    (which should represent a directory) to a file
                    within that directory. It returns that Chan.

     Page 4                       Plan 9            (printed 11/17/24)

     STYXLIB(2)                                             STYXLIB(2)

          srv.devopen(m, gen, tab)
                    Devopen searches the items returned by gen->dirgen
                    for an item with a qid matching the qid in the
                    Chan associated with m.fid.  It then validates the
                    requested open mode against the file's permissions
                    (see openok, below).

          srv.devstat(m, gen, tab)
                    Devstat searches the items returned by gen->dirgen
                    for an item with a qid matching the qid in the
                    Chan associated with m.fid.  If found, it returns
                    the associated status information to the client.
                    If it is a directory and is not found, it is
                    assumed to be the containing directory, and suit-
                    able status information is fabricated.

          srv.devdirread(m, gen, tab)
                    There is no generic devread function, but when
                    m.fid represents a directory, devdirread can be
                    used to return to the client the appropriate items
                    as generated by gen->dirgen.  See also readbytes,
                    readnum, and readstr below.

        Message Conversion Functions
          The following functions provide facilities to parse and
          unparse Styx messages.

          d2tmsg(d) D2tmsg tries to convert a Styx message in d to its
                    appropriate Tmsg variant. It returns a tuple of
                    the form (n,tmsg).  If the conversion is success-
                    ful, n holds the number of bytes used by the mes-
                    sage and tmsg the Tmsg itself. If the message was
                    valid so far, but incomplete, then n will be zero;
                    if there was an error converting the message, then
                    n will be -1.

          d2rmsg(d) D2rmsg works in the same fashion as d2tmsg, con-
                    verting instead to an Rmsg.

          rmsg2d(m,d)
                    Rmsg2d packs m into the data buffer d, which
                    should be at least MAXRPC bytes long.  It returns
                    the number of bytes used by the message.

          convD2M(d,f)
                    ConvD2M packs f into d in Styx format. It returns
                    the slice of d that has not been filled.

          convM2D(d)
                    ConvM2D unpacks d (a buffer holding a Dir in Styx
                    format, which must be at least DIRLEN bytes long)
                    and returns a tuple of the form (r, f) where r is

     Page 5                       Plan 9            (printed 11/17/24)

     STYXLIB(2)                                             STYXLIB(2)

                    the slice of d remaining after the conversion, and
                    f is the converted Dir.

          tmsg2s(m) Tmsg2s returns m as a printable string.

          rmsg2s(m) Tmsg2s returns m as a printable string.

        Utility Functions
          These functions provide general facilities useful in imple-
          menting Styx servers.

          readbytes(m,d)
                    Readbytes returns an appropriate Rmsg.Read given
                    the data d in the file referred to by m.fid.

          readnum(m, val, size)
                    Readnum formats val as a decimal, right justified
                    in size bytes, padded with spaces, and returns the
                    result of readbytes on that data.

          readstr(m,s)
                    Readstr converts s to an array of byte and returns
                    the result of readbytes on that data.

          openok(omode, perm, uname, funame, fgname)
                    Openok checks a requested open mode (see open(5))
                    against a file with permissions perm, owned by
                    funame and group fgname. Uname is the user trying
                    to open the file.  It returns non-zero if access
                    should be allowed.

          openmode(o)
                    Openmode verifies that o is a valid open mode. It
                    returns -1 if o contains unrecognised flags; oth-
                    erwise it returns o with the Sys->OTRUNC and
                    Sys->ORCLOSE flags cleared.

          devdir(c, qid, name, length, user, perm)
                    Devdir builds a Sys->Dir adt given channel c and
                    assorted other information.

        Fid Table Manipulation
          These functions provide direct access to the table of Chans
          maintained by the dev* functions. This table is maintained
          on a per-Styxserver basis.

          srv.fidtochan(fid)
                    Fidtochan returns the Chan associated with fid, or
                    nil if none exists.

          srv.newchan(fid)
                    Newchan creates a new Chan associated with fid and

     Page 6                       Plan 9            (printed 11/17/24)

     STYXLIB(2)                                             STYXLIB(2)

                    enters it in the table.

          srv.chanfree(c)
                    Chanfree deletes c from the table.

          srv.chanlist()
                    Chanlist returns a list containing all of the
                    Chans currently entered in the table.

        Constants
          Styxlib defines a number of constants applicable to the
          writing of Styx servers, including:

          Ttype and Rtype
                    These enumerate the Styx message type constants
                    defined by the Styx protocol (e.g.  Tattach,
                    Rerror).

          Einuse, Ebadfid, Eopen, Enotfound, Enotdir, Eperm, Ebadarg,
                    Eexists
                    These provide standard strings for commonly used
                    error conditions, to be used in Rmsg.Error
                    replies.

     FILES
          /dev/time
          /dev/user

     SOURCE
          /appl/lib/styxlib.b

     SEE ALSO
          intro(5)

     BUGS
          Tmsg2d is unimplemented.

          Openok does not check group permissions.

          Devdir always creates a Dir with the same access time (the
          time that styxlib was initialised).

     Page 7                       Plan 9            (printed 11/17/24)