196 lines · 6.7 KB · Text
RawDownload
= plumbmsg: plumbing message module     include "plumbmsg.m";    plumbmsg := load Plumbmsg Plumbmsg->PATH;    Msg: import plumbmsg;     Msg: adt    {        src:    string;        dst:    string;        dir:    string;        kind:   string;        attr:   string;        data:   array of byte;        # used by applications        send:   fn(msg: self ref Msg): int;        recv:   fn(): ref Msg;        # used by plumb and send, recv        pack:   fn(msg: self ref Msg): array of byte;        unpack: fn(b: array of byte): ref Msg;    };        Attr: adt    {        name: string;        val:  string;    };        init:         fn(willsend: int, rcvport: string, maxdata: int): int;    shutdown:     fn();    string2attrs: fn(s: string): list of ref Attr;    attrs2string: fn(l: list of ref Attr): string;    lookup:       fn(attrs: list of ref Attr, name: string): (int, string);    Plumbmsg is an interface for message-passing between applica‐tions via the plumber(8).  It allows applications to  receivemessages  from  the plumber on a logical input port, and sendmessages to other applications via the plumber. Init must be called once when the application starts, to  setup its plumbing connections.  Applications can choose to sendmessages, receive them, or do both.  Note  that  the  plumbermust  be  running  before  any of these functions are useful.Normally, that is done by the window system's  initialisationprocedure,  but  in specialised systems, plumbing can be usedfor attribute-oriented communication even  without  a  windowsystem. If  the application will be sending messages via the plumber,the value willsend must be non-zero, and init  will  open  anappropriate  channel  to the plumber; if the application willnot send messages, the value should be zero. If the application  is  prepared  to  receive  messages,  theparameter  rcvport  names  its logical input port, which mustalso be known to the plumber (ie, it must be named as a  pos‐sible  destination  in plumbing(6)); init will open an appro‐priate channel to receive messages  from  the  plumber.   Theparameter maxdata gives the size in bytes of the largest mes‐sage the application is prepared  to  receive.   Applicationsthat only send messages set rcvport to nil. Init  returns  returns -1 if for any reason either connectioncannot be set up correctly, in particular if the  plumber  isnot  running  or  the  input  port  is unknown.  Otherwise itreturns a non-negative value. The following program fragment establishes input  and  outputplumbing for an application `edit':        plumbed := 0;       plumbmsg = load Plumbmsg Plumbmsg->PATH;       if(plumbmsg->init(1, "edit", 1000) >= 0)            plumbed = 1; The  variable plumbed is set to allow the application to dis‐able its plumbing user interface (and  not  attempt  to  sendmessages) if initialisation fails. The  Msg  adt  encapsulates  the  message data routed betweenapplications  and  provides  member  functions  to  send  andreceive them.  Its components are used as follows: src    The name of the program generating the message. dst    The  output port to which the plumber should route the       message.   In  practice,  destination  is  often  left       empty,  and the destination port will be determined by       the plumber applying the automatic routing rules  (cf.       plumbing(6)) dir    The  directory  in which to interpret the data (eg, if       the data is a local file name). kind   The format of the data.  Currently, `text' is the only       type  that  applications  understand, but the plumbing       system can route any kind of data. attr   A string containing name=value  pairs  (eg,  click=7),       separated  by tabs.  Normally the value should be cre‐       ated using attrs2string and parsed using string2attrs,       described below. data   The  message to be conveyed.  If kind is text, and the       message is a string s, data will be `array of byte  s'       (ie, its UTF encoding). Plumbing  messages  are  created  directly  using Limbo's refoperator, giving the desired value to each field.  For  exam‐ple:             msg := ref Msg(                 "WmSh",                 "",                 workdir->init(),                 "text",                 attr,                 array of byte text); The  plumbing  messages  are exchanged with the plumber usingtwo member functions: m.send(msg)       Writes a plumbing message to the plumber.  It  returns       the  number  of  bytes written (the result of write in       sys-read(2) which does the writing).  It returns -1 if       the plumber cannot route the message. Msg.recv()       Reads  a  plumbing  message from the file representing       the application's input  port,  previously  opened  by       init.  It waits for a message, and returns a reference       to a Msg that contains the message data. Shutdown sends a message  to  the  plumber  that  shuts  downplumbing for the application's input port rcvport.  An appli‐cation must call it before it exits if it has an active inputport. String2attrs  takes  a string containing a tab-separated listof attribute pairs and returns a list of references  to  Attradts. Attr2string converts a list of references to Attr adts into astring of  the  form  name=valuename=value  .  .  .   .   Thename=value pairs are separated by a single tab. Lookup  searches  attrs  for an attribute name, and if found,returns the tuple (1,value).  If name is  not  found,  lookupreturns (0, nil).    Packed message formatThe  format  of  plumbing messages as transmitted, and memberfunctions that encapsulate it, are documented here  for  com‐pleteness, and in case the details are useful in interpretingplumbing messages outside the Inferno environment. Plumbing messages have a fixed structure: five lines of  textgiving  UTF  representations  of  the corresponding fields ofMsg, then a line giving the length of data in  decimal,  fol‐lowed by the bytes of data:        source\n       destination\n       directory\n       kind\n       attributes\n       n\n       n bytes The details are encapsulated in two functions: m.pack()       Pack  packs  the  contents Msg m into an array of byte       for subsequent  transmission  using  write  (see  sys-       read(2)). Msg.unpack(b)       Unpack  unpacks  an  array of byte b to form a copy of       the original Msg, which it returns. == files/chan/plumb.input       file to send messages to the plumber /chan/plumb.rcvport       file to receive messages routed to  the  logical  name       rcvport == source/appl/lib/plumbmsg.b == bugsShutdown  should  not  be  needed:  the plumber(8), as a fileserver, must know that a particular client has vanished.