|
NAME
| |
Srv, dirread9p, emalloc9p, erealloc9p, estrdup9p, postfd, readbuf,
readstr, respond, srv, threadpostmountsrv, walkandclone – 9P file
service
|
SYNOPSIS
| |
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
typedef struct Srv {
| |
Tree* tree;
void (*attach)(Req *r);
void (*auth)(Req *r);
void (*open)(Req *r);
void (*create)(Req *r);
void (*read)(Req *r);
void (*write)(Req *r);
void (*remove)(Req *r);
void (*flush)(Req *r);
void (*stat)(Req *r);
void (*wstat)(Req *r);
void (*walk)(Req *r);
char* (*walk1)(Fid *fid, char *name, Qid *qid);
char* (*clone)(Fid *oldfid, Fid *newfid);
void (*destroyfid)(Fid *fid);
void (*destroyreq)(Req *r);
void (*start)(Srv *s);
void (*end)(Srv *s);
void* aux;
int infd;
int outfd;
int srvfd;
int nopipe;
|
} Srv;
int srv(Srv *s)
void threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag)
int postfd(char *srvname, int fd)
void respond(Req *r, char *error)
ulong readstr(Req *r, char *src)
ulong readbuf(Req *r, void *src, ulong nsrc)
typedef int Dirgen(int n, Dir *dir, void *aux)
void dirread9p(Req *r, Dirgen *gen, void *aux)
void walkandclone(Req *r, char *(*walk1)(Fid *old, char *name,
void *v),
| |
| |
char *(*clone)(Fid *old, Fid *new, void *v), void *v)
|
|
void* emalloc9p(ulong n)
void* erealloc9p(void *v, ulong n)
char* estrdup9p(char *s)
extern int chatty9p;
|
DESCRIPTION
| |
The function srv serves a 9P session by reading requests from
s−>infd, dispatching them to the function pointers kept in Srv,
and writing the responses to s−>outfd. (Typically, threadpostmountsrv
initializes the infd and outfd structure members. See the description
below.)
Req and Fid structures are allocated one-to-one with uncompleted
requests and active fids, and are described in 9p-fid(3).
The behavior of srv depends on whether there is a file tree (see
9p-file(3)) associated with the server, that is, whether the tree
element is nonzero. The differences are made explicit in the discussion
of the service loop below. The aux element is the client’s, to
do with as it pleases.
Srv does not return until the 9P conversation is finished. Since
it is usually run in a separate process so that the caller can
exit, the service loop has little chance to return gracefully
on out of memory errors. It calls emalloc9p, erealloc9p, and estrdup9p
to obtain its memory. The default implementations of these functions
act as malloc, realloc, and
strdup but abort the program if they run out of memory. If alternate
behavior is desired, clients can link against alternate implementations
of these functions.
threadpostmountsrv is a wrapper that creates a separate process
in which to run srv. It does the following:
| |
If s−>nopipe is zero (the common case), initialize s−>infd and s−>outfd
to be one end of a freshly allocated pipe, with s−>srvfd initialized
as the other end.
If name is non-nil, call postfd(s−>srvfd, name) to post s−>srvfd
as /srv/name.
Fork a child process via rfork(3) or procrfork (see thread(3)),
using the RFFDG, RFNAMEG, and RFMEM flags. The child process calls
close(s->srvfd) and then srv(s); it will exit once srv returns.
If mtpt is non-nil, call amount(s−>srvfd, mtpt, flag, ""); otherwise,
close s−>srvfd.
The parent returns to the caller.
|
If any error occurs during this process, the entire process is
terminated by calling sysfatal(3).
Service functions
The functions in a Srv structure named after 9P transactions are
called to satisfy requests as they arrive. If a function is provided,
it must arrange for respond to be called when the request is satisfied.
The only parameter of each service function is a Req* parameter
(say r). The incoming request parameters are stored in r−>ifcall;
r−>fid and
r−>newfid are pointers to Fid structures corresponding to the numeric
fids in r−>ifcall; similarly, r−>oldreq is the Req structure corresponding
to r−>ifcall.oldtag. The outgoing response data should be stored
in r−>ofcall. The one exception to this rule is that stat should
fill in r−>d rather than r−>ofcall.stat: the library will convert
the structure into the
machine-independent wire representation. Similarly, wstat may
consult r−>d rather than decoding r−>ifcall.stat itself. When a
request has been handled, respond should be called with r and
an error string. If the request was satisfied successfully, the
error string should be a nil pointer. Note that it is permissible
for a function to return without itself
calling respond, as long as it has arranged for respond to be
called at some point in the future by another proc sharing its
address space, but see the discussion of flush below. Once respond
has been called, the Req* as well as any pointers it once contained
must be considered freed and not referenced.
If the service loop detects an error in a request (e.g., an attempt
to reuse an extant fid, an open of an already open fid, a read
from a fid opened for write, etc.) it will reply with an error
without consulting the service functions.
The service loop provided by srv (and indirectly by threadpostmountsrv
) is single-threaded. If it is expected that some requests might
block, arranging for alternate processes to handle them is suggested.
The constraints on the service functions are as follows. These
constraints are checked while the server executes. If a service
function fails to do something it ought to have, srv will call
end and then abort.
Auth If authentication is desired, the auth function should record
that afid is the new authentication fid and set afid->qid and ofcall.qid.
Auth may be nil, in which case it will be treated as having responded
with the error “argv0: authentication not required,” where argv0
is the program name variable as set by ARGBEGIN (see arg(3)).
AttachThe attach function should check the authentication state
of afid if desired, and set r−>fid−>qid and ofcall.qid to the qid
of the file system root. Attach may be nil only if file trees
are in use; in this case, the qid will be filled from the root
of the tree, and no authentication will be done.
Walk If file trees are in use, walk is handled internally, and
srv−>walk is never called.
| |
If file trees are not in use, walk should consult r−>ifcall.wname
and r−>ifcall.nwname, filling in ofcall.qid and ofcall.nqid, and
also copying any necessary aux state from r−>fid to r−>newfid when
the two are different. As long as walk sets ofcall.nqid appropriately,
it can respond with a nil error string even when 9P demands an
error (e.g., in
the case of a short walk); the library detects error conditions
and handles them appropriately.
Because implementing the full walk message is intricate and prone
to error, the helper routine walkandclone will handle the request
given pointers to two functions walk1 and (optionally) clone .
Clone, if non-nil, is called to signal the creation of newfid
from oldfid. Typically a clone routine will copy or increment
a reference count in oldfid’s aux
element. Walk1 should walk fid to name, initializing fid−>qid to
the new path’s qid. Both should return nil on success or an error
message on error. Walkandclone will call respond after handling
the request.
|
Walk1, Clone
| |
If the client provides functions srv−>walk1 and (optionally) srv−>clone,
the 9P service loop will call walkandclone with these functions
to handle the request. Unlike the walk1 above, srv−>walk1 must
fill in both fid−>qid and *qid with the new qid on a successful
walk.
|
Open If file trees are in use, the file metadata will be consulted
on open, create, remove, and wstat to see if the requester has
the appropriate permissions. If not, an error will be sent back
without consulting a service function.
If not using file trees or the user has the appropriate permissions,
open is called with r−>ofcall.qid already initialized to the one
stored in the Fid structure (that is, the one returned in the
previous walk). If the qid changes, both should be updated.
CreateThe create function must fill in both r−>fid−>qid and r−>ofcall.qid
on success. When using file trees, create should allocate a new
File with createfile; note that createfile may return nil (because,
say, the file already exists). If the create function is nil,
srv behaves as though it were a function that always responded
with the error “create
Remove
| |
should mark the file as removed, whether by calling removefile
when using file trees, or by updating an internal data structure.
In general it is not a good idea to clean up the aux information
associated with the corresponding File at this time, to avoid
memory errors if other fids have references to that file. Instead,
it is suggested that
remove simply mark the file as removed (so that further operations
on it know to fail) and wait until the file tree’s destroy function
is called to reclaim the aux pointer. If not using file trees,
it is prudent to take the analogous measures. If remove is not
provided, all remove requests will draw “remove prohibited” errors.
|
Read The read function must be provided; it fills r−>ofcall.data
with at most r−>ifcall.count bytes of data from offset r−>ifcall.offset
of the file. It also sets r−>ofcall.count to the number of bytes
being returned. If using file trees, srv will handle reads of
directories internally, only calling read for requests on files.
Readstr and readbuf are useful for
| |
satisfying read requests on a string or buffer. Consulting the
request in r−>ifcall, they fill r−>ofcall.data and set r−>ofcall.count;
they do not call respond. Similarly, dirread9p can be used to
handle directory reads in servers not using file trees. The passed
gen function will be called as necessary to fill dir with information
for the nth entry in
the directory. The string pointers placed in dir should be fresh
copies made with estrdup9p; they will be freed by dirread9p after
each successful call to gen. Gen should return zero if it successfully
filled dir, minus one on end of directory.
|
Write The write function is similar but need not be provided. If
it is not, all writes will draw “write prohibited” errors. Otherwise,
write should attempt to write the r−>ifcall.count bytes of r−>ifcall.data
to offset r−>ifcall.offset of the file, setting r−>ofcall.count
to the number of bytes actually written. Most programs consider
it an error to write less than
Stat Stat should fill r−>d with the stat information for r−>fid.
If using file trees, r−>d will have been initialized with the stat
info from the tree, and stat itself may be nil.
WstatThe wstat consults r−>d in changing the metadata for r−>fid
as described in stat(9p). When using file trees, srv will take
care to check that the request satisfies the permissions outlined
in stat(9p). Otherwise wstat should take care to enforce permissions
where appropriate.
Flush Servers that always call respond before returning from the
service functions need not provide a flush implementation: flush
is only necessary in programs that arrange for respond to be called
asynchronously. Flush should cause the request r−>oldreq to be
cancelled or hurried along. If oldreq is cancelled, this should
be signalled by calling
| |
respond on oldreq with error string ‘interrupted’. Flush must
respond to r with a nil error string. Flush may respond to r before
forcing a response to r−>oldreq. In this case, the library will
delay sending the Rflush message until the response to r−>oldreq
has been sent.
|
Destroyfid, destroyreq, start, and end are auxiliary functions,
not called in direct response to 9P requests.
Destroyfid
| |
When a Fid’s reference count drops to zero (i.e., it has been
clunked and there are no outstanding requests referring to it),
destroyfid is called to allow the program to dispose of the fid−>aux
pointer.
|
Destroyreq
| |
Similarly, when a Req’s reference count drops to zero (i.e., it
has been handled via respond and other outstanding pointers to
it have been closed), destroyreq is called to allow the program
to dispose of the r−>aux pointer.
|
Start Before the 9P service loop begins, the service proc calls
start so that the server can run any initialization that must
be done from inside the service proc.
End Once the 9P service loop has finished (end of file been reached
on the service pipe or a bad message has been read), end is called
(if provided) to allow any final cleanup. For example, it was
used by the Palm Pilot synchronization file system (never finished)
to gracefully terminate the serial conversation once the file
system had been
| |
unmounted. After calling end, the service loop (which runs in
a separate process from its caller) terminates using _exits (see
exits(3)).
|
If the chatty9p flag is at least one, a transcript of the 9P session
is printed on standard error. If the chatty9p flag is greater
than one, additional unspecified debugging output is generated.
By convention, servers written using this library accept the −D
option to increment chatty9p.
|
EXAMPLES
| |
/usr/local/plan9/src/lib9p/ramfs.c is an example of a simple single-threaded
file server. On Plan 9, see archfs, cdfs, nntpfs, webfs, and sshnet
for more examples.
In general, the File interface is appropriate for maintaining
arbitrary file trees (as in ramfs). The File interface is best
avoided when the tree structure is easily generated as necessary;
this is true when the tree is highly structured (as in cdfs and
nntpfs) or is maintained elsewhere.
|
SOURCE
SEE ALSO
|
|