Rread: type chan of (array of byte, string); Rwrite: type chan of (int, string); FileIO: adt { read: chan of (int, int, int, Rread); write: chan of (int, array of byte, int, Rwrite); }; file2chan: fn(dir, file: string): ref FileIO;
## returns nil on failure.
Although file semantics are used, the goal does not have to be the creation of a server to replicate the duties of existing file systems (for example, persistent storage of data). The server can be designed to provide new services.
Note: The interface to the file2chan function has changed since Inferno 1.0. The interface formerly had a third argument, flags, that specified the options for mounting the server device #s. The mount of the #s device must now be performed explicitly, before the file2chan function is called.
sys->bind("#s", "/chan", sys->MBEFORE);
file2chan: fn(dir, file: string): ref FileIO;The file2chan function returns a FileIO type holding two channels used by the system to deliver Tread and Twrite Styx messages to the server. See Introduction to Limbo Modules in Chapter 7. The arguments are:
dir | The existing directory where the file is to be created. |
file | The file created by file2chan. It is held in a directory containing just that one file unioned in dir. |
To allow the client to complete its read request successfully, the server should respond by sending into rc a tuple, (data, nil), where data satisfies the client's request.
To create an unsuccessful read request for the client, the server should respond on rc with a tuple,(nil, errmsg), where errmsg is a string describing the error condition. The errmsg string becomes the system error for the client (See print, fprint, sprint - print formatted output).
The client blocks in its read system call until the server sends its reply.
The server should respond with a tuple (count, string) on wc, the channel received in the tuple from the write channel. To indicate a successful write operation to the client the server's response should be (count, nil), where count is the number of bytes (for example, len data) received from the client.
To make the client's write request fail, the server's response should be (0, errmsg), where errmsg describes the problem. The errmsg string becomes the system error for the client (See print, fprint, sprint - print formatted output).
The client blocks in its write system call until the server sends its reply.
sys->bind("#s", "/chan", sys->MBEFORE); dbio := file2chan("/chan", "dbctl"); for(;;) alt{ (off, count, fid, rc) := <-dbio.read => ;#someone's reading from my file #if they've sent a query, give them #the results (off, data, fid, wc) := <-dbio.write => ;#someone's writing to my file #maybe a query, maybe adding data to DB }The following program illustrates the tuples received when a read or write is performed on the file created using file2chan.
implement Fchan; # # On the server: # # $ bind '#I' /net # $ fchan /dir file # $ lib/srv # # On the client: # # $ bind '#I' /net # $ lib/cs # $ mount net!server_name /path/dir # $ cd /path/dir # $ ls # $ cat file # does sys->read on file # # # $ echo something > file # does sys->write to file # include "sys.m"; sys: Sys; include "draw.m"; Context: import Draw ; progname: string; Fchan: module { init: fn(nil: ref Context, argv: list of string); }; init(nil: ref Context, argv: list of string) { progname = hd argv; argv = tl argv; sys = load Sys Sys->PATH; stderr := sys->fildes(2); if(len argv != 2){ sys->fprint(stderr,"%s: usage: <dirname> <file>\n", progname); return; } dirname := hd argv; argv = tl argv; filename := hd argv; argv = tl argv; # Note explicit binding of #s for Inferno 2.0 file2chan sys->bind("#s", dirname, sys->MBEFORE); f2chanio := sys->file2chan(dirname, filename); # Inferno1.1 way: # f2chanio := sys->file2chan(dirname, filename, # sys->MBEFORE); if(f2chanio == nil) { sys->fprint(stderr, "%s: f2chanio file2chan: %r\n", progname); return; } spawn waiter(f2chanio, dirname, filename); } waiter(f2chanio: ref sys->FileIO, dirname, filename: string) { t : string ; # now wait for read or write on /dirname/filename for (;;) { alt { (woffset, wdata, wfid, wc) := <-f2chanio.write => if(wc != nil){ # print write data on server sys->print("\nFileIO.write:"); sys->print("\n-------------\n"); sys->print("offset :%d\n", woffset); sys->print("data :%s\n", string wdata); sys->print("len (data) :%d\n", len wdata); sys->print("fid :%d\n", wfid); sys->print("wc :%x\n", wc); wc <- = (len wdata, nil); # report success continue ; } (roffset, rcount, rfid, rc) := <-f2chanio.read => if(rc != nil){ # send read data back to client t = sys->sprint("%s\noffset:\t%d\ncount :\t%d\nfid :\t%d\nrc :\t%x\n", "\nFileIO.read:\n------------", roffset, rcount, rfid, rc); d := array of byte t; # cast for rc n := len d ; if(rcount >= n){ # OK to send as is rc <-= (d, nil); # deliver data to client n = 0; }else{ # truncate and send rc <-= (d[0:rcount], nil); d = d[rcount:n]; n -= rcount; } continue; } } # alt } # for } # waiter