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/8/25)
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/8/25)
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/8/25)
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/8/25)
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/8/25)
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/8/25)
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/8/25)