#lang scribble/doc @(require scribble/manual (for-label scheme (planet "main.ss" ("murphy" "packed-io.plt" 1 0)) "../exception.ss" "../errno.ss" "../network.ss")) @title[#:tag "network"]{Low-level Network Layer} @defmodule[(planet "network.ss" ("murphy" "9p.plt" 1 0))]{ This module re-exports all bindings from the @scheme["network/message.ss"] and @scheme["network/event-loop.ss"] modules. } @section{Protocol Messages} @defmodule[(planet "message.ss" ("murphy" "9p.plt" 1 0) "network")]{ Structures and utilities to represent messages exchanged between 9P clients and servers. See @hyperlink["http://swtch.com/plan9port/man/man9/intro.html"]{intro(9P)} for the original protocol message definition overview. } @subsection{Messages} @defstruct[message ([tag (box/c (or/c natural-number/c #f))]) #:transparent]{ The base type of all 9P protocol messages. } @defstruct[(message:t message) () #:transparent]{ The base type of all 9P protocol messages initiating a request. These messages are normally only transmitted from the client to the server. } @defstruct[(message:r message) () #:transparent]{ The base type of all 9P protocol messages responding to a request. These messages are normally only transmitted from the server to the client. } @defstruct[(message:t:version message:t) ([max-size natural-number/c] [version string?]) #:transparent]{ Protocol negotiation request. See @hyperlink["http://swtch.com/plan9port/man/man9/version.html"]{version(9P)} for more information. } @defstruct[(message:r:version message:r) ([max-size natural-number/c] [version string?]) #:transparent]{ Protocol negotiation response. See @hyperlink["http://swtch.com/plan9port/man/man9/version.html"]{version(9P)} for more information. } @defstruct[(message:t:auth message:t) ([afid (box/c (or/c natural-number/c #f))] [user string?] [root string?]) #:transparent]{ Request to open an authentication channel. See @hyperlink["http://swtch.com/plan9port/man/man9/attach.html"]{auth(9P)} for more information. } @defstruct[(message:r:auth message:r) ([aqid qid?]) #:transparent]{ Authentication channel open response. See @hyperlink["http://swtch.com/plan9port/man/man9/attach.html"]{auth(9P)} for more information. } @defstruct[(message:t:attach message:t) ([fid (box/c (or/c natural-number/c #f))] [afid natural-number/c] [user string?] [root string?]) #:transparent]{ Filesystem root attach request. See @hyperlink["http://swtch.com/plan9port/man/man9/attach.html"]{attach(9P)} for more information. } @defstruct[(message:r:attach message:r) ([qid qid?]) #:transparent]{ Attached filesystem root response. See @hyperlink["http://swtch.com/plan9port/man/man9/attach.html"]{attach(9P)} for more information. } @defstruct[(message:r:error message:r) ([name string?]) #:transparent]{ Generic error response. See @hyperlink["http://swtch.com/plan9port/man/man9/error.html"]{error(9P)} for more information. The only message that has no corresponding request message but may be sent in response to most other requests. } @defstruct[(message:t:flush message:t) ([old-tag natural-number/c]) #:transparent]{ Previous message flush request. See @hyperlink["http://swtch.com/plan9port/man/man9/flush.html"]{flush(9P)} for more information. The higher-level code in this 9P implementation handles this message automatically. } @defstruct[(message:r:flush message:r) () #:transparent]{ Flushed previous message response. See @hyperlink["http://swtch.com/plan9port/man/man9/flush.html"]{flush(9P)} for more information. The higher-level code in this 9P implementation handles this message automatically. } @defstruct[(message:t:walk message:t) ([from-fid natural-number/c] [to-fid (box/c (or/c natural-number/c #f))] [names (vectorof string?)]) #:transparent]{ File walk request. See @hyperlink["http://swtch.com/plan9port/man/man9/walk.html"]{walk(9P)} for more information. } @defstruct[(message:r:walk message:r) ([qids (vectorof qid?)]) #:transparent]{ Walked file response. See @hyperlink["http://swtch.com/plan9port/man/man9/walk.html"]{walk(9P)} for more information. } @defstruct[(message:t:open message:t) ([fid natural-number/c] [mode natural-number/c]) #:transparent]{ File open request. See @hyperlink["http://swtch.com/plan9port/man/man9/open.html"]{open(9P)} for more information. } @defstruct[(message:r:open message:r) ([qid qid?] [i/o-unit natural-number/c]) #:transparent]{ Opened file response. See @hyperlink["http://swtch.com/plan9port/man/man9/open.html"]{open(9P)} for more information. } @defstruct[(message:t:openfd message:t) ([fid natural-number/c] [mode natural-number/c]) #:transparent]{ File open as descriptor request. See @hyperlink["http://swtch.com/plan9port/man/man9/openfd.html"]{openfd(9P)} for more information. The higher-level code in this 9P implementation doesn't support this message since it is impossible to implement when TCP sockets are used as the transport layer. } @defstruct[(message:r:openfd message:r) ([qid qid?] [i/o-unit natural-number/c] [fd natural-number/c]) #:transparent]{ Opened file open as descriptor response. See @hyperlink["http://swtch.com/plan9port/man/man9/openfd.html"]{openfd(9P)} for more information. The higher-level code in this 9P implementation doesn't support this message since it is impossible to implement when TCP sockets are used as the transport layer. } @defstruct[(message:t:create message:t) ([fid natural-number/c] [name string?] [permissions natural-number/c] [mode natural-number/c]) #:transparent]{ File creation request. See @hyperlink["http://swtch.com/plan9port/man/man9/open.html"]{create(9P)} for more information. } @defstruct[(message:r:create message:r) ([qid qid?] [i/o-unit natural-number/c]) #:transparent]{ Created file response. See @hyperlink["http://swtch.com/plan9port/man/man9/open.html"]{create(9P)} for more information. } @defstruct[(message:t:read message:t) ([fid natural-number/c] [offset natural-number/c] [size natural-number/c]) #:transparent]{ Data read request. See @hyperlink["http://swtch.com/plan9port/man/man9/read.html"]{read(9P)} for more information. } @defstruct[(message:r:read message:r) ([data natural-number/c]) #:transparent]{ Read data response. See @hyperlink["http://swtch.com/plan9port/man/man9/read.html"]{read(9P)} for more information. } @defstruct[(message:t:write message:t) ([fid natural-number/c] [offset natural-number/c] [data bytes?]) #:transparent]{ Data write request. See @hyperlink["http://swtch.com/plan9port/man/man9/read.html"]{write(9P)} for more information. } @defstruct[(message:r:write message:r) ([size natural-number/c]) #:transparent]{ Written data response. See @hyperlink["http://swtch.com/plan9port/man/man9/read.html"]{write(9P)} for more information. } @defstruct[(message:t:clunk message:t) ([fid natural-number/c]) #:transparent]{ File handle clunk request. See @hyperlink["http://swtch.com/plan9port/man/man9/clunk.html"]{clunk(9P)} for more information. } @defstruct[(message:r:clunk message:r) () #:transparent]{ Clunked file handle response. See @hyperlink["http://swtch.com/plan9port/man/man9/clunk.html"]{clunk(9P)} for more information. } @defstruct[(message:t:remove message:t) ([fid natural-number/c]) #:transparent]{ File and handle remove request. See @hyperlink["http://swtch.com/plan9port/man/man9/remove.html"]{remove(9P)} for more information. } @defstruct[(message:r:remove message:r) () #:transparent]{ Removed file and handle response. See @hyperlink["http://swtch.com/plan9port/man/man9/remove.html"]{remove(9P)} for more information. } @defstruct[(message:t:stat message:t) ([fid natural-number/c]) #:transparent]{ Directory entry information request. See @hyperlink["http://swtch.com/plan9port/man/man9/stat.html"]{stat(9P)} for more information. } @defstruct[(message:r:stat message:r) ([stat stat?]) #:transparent]{ Directory entry information response. See @hyperlink["http://swtch.com/plan9port/man/man9/stat.html"]{stat(9P)} for more information. } @defstruct[(message:t:wstat message:t) ([fid natural-number/c] [stat stat?]) #:transparent]{ Directory entry information change request. See @hyperlink["http://swtch.com/plan9port/man/man9/stat.html"]{wstat(9P)} for more information. } @defstruct[(message:r:wstat message:r) () #:transparent]{ Changed directory entry information response. See @hyperlink["http://swtch.com/plan9port/man/man9/stat.html"]{wstat(9P)} for more information. } @subsection{Input and Output} @defparam[max-message-size size natural-number/c]{ The maximum bytes size allowable for a protocol message. @scheme[read-message] and @scheme[write-message] throw errors if the messages they handle exceed this size. The parameter is also used during protocol negotiation between client and server. Inside the server, on threads handling client requests, the parameter is set to the value negotiated with the client. } @defproc[(read-message [in input-port? (current-input-port)]) message?]{ Reads a 9P message in binary format from the input port @scheme[in]. } @defproc[(write-message [message message?] [out output-port? (current-output-port)]) void?]{ Writes the given 9P @scheme[message] to the output port @scheme[out] in binary format. } @subsection[#:tag "network/packing"]{Packings} @defparam[current-tags table (and/c hash? (not/c immutable?))]{ A table tracking message tag numbers that are in use in the current thread. } @defthing[tag/p packing?]{ A 16 bit unsigned integer packing. Values produced and consumed by the packing are contained within boxes. When a box containg @scheme[#f] is packed, a random number that is not yet present as a key in @scheme[(current-tags)] is selected, registered in the table with the associated value @scheme[#t] and placed in the box. If a tag needs to be generated but there are too many tags registered in the table, @scheme[tag/p] raises a 9P filesystem exception with the message @scheme[EAGAIN]. } @defparam[current-fids table (and/c hash? (not/c immutable?))]{ A table tracking file identifiers that are in use in the current thread. } @defthing[fid/p packing?]{ A 32 bit unsigned integer packing. Values produced and consumed by the packing are contained within boxes. When a box containg @scheme[#f] is packed, a random number that is not yet present as a key in @scheme[(current-fids)] is selected, registered in the table with the associated value @scheme[#t] and placed in the box. If a tag needs to be generated but there are too many tags registered in the table, @scheme[tag/p] raises a 9P filesystem exception with the message @scheme[EMFILE]. } @section{Event Loops} @defmodule[(planet "event-loop.ss" ("murphy" "9p.plt" 1 0) "network")]{ Basic event loops to implement 9P network clients and servers over TCP sockets. } @defproc[(start-client [hostname string?] [port-no (integer-in 1 65535) 564] [local-hostname (or/c string? #f) #f] [local-port-no (or/c (integer-in 1 65535) #f) #f]) (values (-> message:t? message:r?) (-> any/c any) custodian?)]{ Starts a client event loop in a new thread managed by a new custodian after establishing a TCP connection to the server specified by the @scheme[hostname] and @scheme[port-no] and negotiating the 9P protocol version. Returns three values: @itemlist[ @item{ A request submitting procedure. You pass a message of type @scheme[message:t] to the procedure, it creates a fresh reply channel, communicates the message and the channel to the event loop thread, waits for a reply on the channel and returns the reply message of type @scheme[message:r]. Replies of type @scheme[message:r:error] are automatically converted into exceptions of type @scheme[exn:fail:filesystem:9p]. Messages of type @scheme[message:t:flush] and @scheme[message:r:flush] are handled automatically by converting from and to exceptions of type @scheme[exn:break]. } @item{ A file identifier clearing procedure. File identifiers can be auto-generated when serializing request messages for network transport but they cannot be dropped from the table of current identifiers fully automatically: In case you have allocated a file identifier automatically but you do not clunk or remove it, for example because the request message for which the identifier was created returned an error response, you should call this procedure with the file identifier as an argument to release the identifier for future reuse. } @item{ The custodian managing the event loop thread and the network connection. Simply shutdown the custodian to terminate the client event loop. } ] } @defproc[(start-server [t-handler (-> message:t? message:r?)] [fid-cleanup (-> any/c any)] [port-no (integer-in 0 65535) 564] [max-allow-wait natural-number/c 4] [reuse? any/c #f] [hostname (or/c string? #f) #f]) custodian?]{ Starts a server event loop in a new thread managed by a new custodian after starting to listen on a TCP socket for the specified @scheme[hostname] and @scheme[port-no]. Another thread with a message processing loop is spawned for every incoming connection. Each of the message processors calls @scheme[t-handler] on incoming messages of type @scheme[message:t], again in a new thread for every incoming request. Thus multiple requests from the same client can run in parallel. When @scheme[t-handler] returns a response of type @scheme[message:r], that message is communicated back to the message processor which sends it to the client. When @scheme[t-handler] raises an exception of type @scheme[exn:fail:filesystem:9p], it is caught and converted into a in a response of type @scheme[message:r:error]. @scheme[t-handler] should respond sensibly to breaks since client requests to flush a message that has not been responded to will result in a break being sent to the message's handling thread. @scheme[t-handler] is called in a dynamic environment with the following differences to the one in which @scheme[start-server] was called: @scheme[current-custodian] is parameterized to the custodian managing the whole server event loop, @scheme[max-message-size] is parameterized to the negotiated maximum message size of the current 9P connection, @scheme[current-tags] is parameterized to @scheme[#f], @scheme[current-fids] is parameterized to a hash table that can be used to track file identifiers. This hash table is the same for all request handlers spawned for the same client connection. Initially it contains a single entry mapping the file identifier @scheme[#xffffffff] to the value @scheme[#f]. When the message processor for a client connection terminates, it calls @scheme[fid-cleanup] on any values stored in the hash table that are not @scheme[#f]. @scheme[start-server] returns the custodian managing the event loop threads and the network connections. Simply shutdown the custodian to terminate the server event loop. }