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 12/21/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 12/21/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 12/21/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 12/21/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 12/21/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 12/21/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 12/21/24)