usr/src/doc/rad-dev/a-protocol.xml
author David Powell <david.e.powell@oracle.com>
Wed, 03 Aug 2011 13:07:44 -0700
changeset 760 5220578b3b77
child 761 ffd45e0b2905
permissions -rw-r--r--
18775 Formal protocol documentation needed

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<!--
  PDL HEADER START

  Public Documentation License Notice

  The contents of this Documentation are subject to the Public
  Documentation License Version 1.01 (the "License"); you may only
  use this Documentation if you comply with the terms of this License.
  A copy of the License is available at
  http://www.opensolaris.org/os/community/documentation/license.

  PDL HEADER END

  Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
-->

<appendix xmlns:rm="rad-macros">
<title><command>rad</command> binary protocol</title>

<!--
  While it's not a very traditional way to document binary protocols,
  BNF might be a clearer way to communicate some of the optional
  paths.  We should consider adding it as an addition to the textual /
  diagrammatical description.
-->

<para>
In addition to supporting multiple transports, <command>rad</command>
is capable of talking different protocols.  The default (and currently
only) protocol is a proprietary binary protocol designated
<literal>rad</literal>.  This appendix documents version 1 of this
protocol.
</para>

<section><title>Overview</title>
    <para> 
    The <literal>rad</literal> protocol is a bi-directional binary protocol
    that operates over a single stream.  Communication in both directions
    takes the form of discrete messages.  These messages are framed using
    <acronym>RPC</acronym> record marking (See <quote>RECORD MARKING
    STANDARD</quote> in <citation>RPC</citation>).
    </para>

    <para> 
    The individual messages take the formats documented below.  Even though
    <acronym>RPC</acronym> record marking permits skipping messages of
    unknown format, both the client and the server are free to immediately
    drop the connection when an invalid message is seen.
    </para>

    <para>
    The <literal>rad</literal> protocol is built using
    <citation>XDR</citation>, so for simplicity and clarity the
    <acronym>XDR</acronym> primitive type names and syntax will be used
    throughout this appendix.  For example:
    </para>

    <itemizedlist>
    <listitem>
    <quote><literal>FOO&lt;&gt;</literal></quote> would represent a
    variable-length array of <literal>FOO</literal>s, communicated as
    an <literal>unsigned int</literal> containing the size followed by
    that number of <literal>FOO</literal>s.
    </listitem>
    <listitem>
    <quote><literal>FOO[n]</literal></quote> would represent a
    fixed-length array of a predetermined size, communicated only as
    the <literal>n</literal> <literal>FOO</literal>s.
    </listitem>
    <listitem>
    <quote><literal>FOO *</literal></quote> would represent an optional
    value, communicated as a <literal>boolean</literal> value followed
    by a <literal>FOO</literal> if and only if the
    <literal>boolean</literal> value was true.
    </listitem>
    </itemizedlist>

</section>

<section><title>Common data formats</title>
    <para>
    There are a few types and concepts that appear repeatedly
    throughout the <literal>rad</literal> protocol.  They are described
    here.
    </para>

    <section id="op-data-def"><title>Operations</title>
    <para>
    The various operations one can perform against the server are
    communicated with an operation code.
    </para>

    <rm:proto name="OP-CODE">
	<rm:data field="op_code" size="4" type="int">
	<rm:codedef label="operation" prefix="oc">
	    <rm:code num="0" name="INVOKE" />
	    <rm:code num="1" name="GETATTR" />
	    <rm:code num="2" name="SETATTR" />
	    <rm:code num="3" name="LOOKUP" />
	    <rm:code num="4" name="DEFINE" />
	    <rm:code num="5" name="LIST" />
	    <rm:code num="6" name="SUB" />
	    <rm:code num="7" name="UNSUB" />
	</rm:codedef>
	</rm:data>
    </rm:proto>
    </section>

    <section id="error-data-def"><title>Errors</title>
    <para>
    Errors in the <literal>rad</literal> protocol are communicated by
    an error code, and optionally structured error data.
    </para>

    <rm:proto name="ERROR-CODE">
	<rm:data field="error_code" size="4" type="int">
	<rm:codedef label="error" prefix="ec">
	    <rm:code num="0" name="ok" />
	    <rm:code num="1" name="object" />
	    <rm:code num="2" name="nomem" />
	    <rm:code num="3" name="notfound" />
	    <rm:code num="4" name="priv" />
	    <rm:code num="5" name="system" />
	    <rm:code num="6" name="exists" />
	    <rm:code num="7" name="mismatch" />
	    <rm:code num="8" name="illegal" />
	</rm:codedef>
	</rm:data>
    </rm:proto>
    
    <para>
    For object-specific errors, <literal>EC-OBJECT</literal>, the
    format of the structured data is defined by the object's interface
    definition.  For the remainder, save for <literal>EC-OK</literal>
    (which is defined for completeness, but should never appear in the
    protocol), the format is defined during the initial connection
    handshake.  See <xref linkend="proto-handshake" />.
    </para>
    </section>

    <section id="time-data-def"><title>Time</title>
    <para>
    When a time needs to be represented in the <literal>rad</literal>
    protocol, it is communicated as seconds and nanoseconds offset from
    the epoch (January 1, 1970 UTC).  
    </para>
    
    <para>
    It is important to note that the accuracy of such time data is
    determined by its context.  In most, if not all, cases, the full
    nanosecond precision is only relevant when compared to time data
    obtained from the same host.
    </para>

<rm:proto name="TIME-DATA">
    <rm:data field="secs" size="8" type="hyper">
	number of seconds
    </rm:data>
    <rm:data field="nsecs" size="4" type="int">
	number of nanoseconds (<mathphrase>0 &le; nsec &le;
	10<superscript>9</superscript></mathphrase>)
    </rm:data>
</rm:proto>
    </section>

    <section id="name-data-def"><title>Object names</title>
    <para>
    <command>rad</command> object names are structured, consisting of a
    domain and one or more key-value pairs.  When an object name or
    pattern needs to be represented in the <literal>rad</literal>
    protocol, this structure is flattened to a canonical string
    format.  This string format consists of the domain, followed by a
    colon (':'), followed by comma-separated key-value pairs.  Each
    key-value pair consists of the name, followed by an equals sign
    ('='), followed by the value.
    </para>

    <para>
    Because keys and values may contain the special characters '=' or
    ',', a quoting algorithm is applied when constructing the string.
    The following substitutions are applied to keys and values:
    </para>

    <table><title>Object Name Escaping</title>
    <tgroup cols='2'>
    <thead>
    <row><entry>Plain Text</entry><entry>Escaped Text</entry></row>
    </thead>
    <tbody>
    <row><entry>\</entry><entry>\S</entry></row>
    <row><entry>,</entry><entry>\C</entry></row>
    <row><entry>=</entry><entry>\E</entry></row>
    </tbody>
    </tgroup>
    </table>

    <para>
    Note that under this transformation, backslashes are only found as
    part of quoted characters, and commas and equals signs, when
    present, always have their special meaning.  Object names that use
    no special characters are passed through unchanged.  These facts
    can be used to simplify parsing or preprocessing of
    string-formatted object names.
    </para>

<rm:proto name="NAME-DATA">
    <rm:data field="name" size="variable" type="string&lt;&gt;">
	flattened object name
    </rm:data>
</rm:proto>

    <example>
    An object name in the domain <quote>com.example</quote> with the
    following keys and values:

    <informaltable>
    <tgroup cols='2'>
    <thead>
    <row><entry>Key</entry><entry>Value</entry></row>
    </thead>
    <tbody>
    <row><entry>directory</entry><entry>C:\</entry></row>
    <row><entry>first,last</entry><entry>Doe,John</entry></row>
    </tbody>
    </tgroup>
    </informaltable>

    would be represented by the string
    <quote>com.example:directory=C:\S,first\Clast=Doe\CJohn</quote>.
    </example>
    </section>

    <section id="adr-data"><title><acronym>ADR</acronym> data</title>
    <para>
    Central to the <literal>rad</literal> protocol is the communication
    of data defined by <acronym>ADR</acronym>.  Most
    <acronym>ADR</acronym> primitives map directly to
    <acronym>XDR</acronym> types:
    </para>

    <table>
    <title>Primitive <acronym>ADR</acronym> to Wire Type Mapping</title>
    <tgroup cols='2'>
    <thead>
    <row>
    <entry><acronym>ADR</acronym> Type</entry>
    <entry>Wire Type</entry>
    </row>
    </thead>
    <tbody>
    <row>
    <entry>boolean</entry>
    <entry><acronym>XDR</acronym> <literal>boolean</literal></entry>
    </row>
    <row>
    <entry>integer</entry>
    <entry><acronym>XDR</acronym> <literal>int</literal></entry>
    </row>
    <row>
    <entry>uinteger</entry>
    <entry><acronym>XDR</acronym> <literal>unsigned int</literal></entry>
    </row>
    <row>
    <entry>long</entry>
    <entry><acronym>XDR</acronym> <literal>hyper</literal></entry>
    </row>
    <row>
    <entry>ulong</entry>
    <entry><acronym>XDR</acronym> <literal>unsigned hyper</literal></entry>
    </row>
    <row>
    <entry>float</entry>
    <entry><acronym>XDR</acronym> <literal>float</literal></entry>
    </row>
    <row>
    <entry>double</entry>
    <entry><acronym>XDR</acronym> <literal>double</literal></entry>
    </row>
    <row>
    <entry>string</entry>
    <entry><acronym>XDR</acronym> <literal>string&lt;&gt;</literal></entry>
    </row>
    <row>
    <entry>opaque</entry>
    <entry><acronym>XDR</acronym> <literal>opaque&lt;&gt;</literal></entry>
    </row>
    <row>
    <entry>password</entry>
    <entry><acronym>XDR</acronym> <literal>string&lt;&gt;</literal></entry>
    </row>
    <row>
    <entry>time</entry>
    <entry><literal>TIME-DATA</literal>
    (see <xref linkend="time-data-def" />)</entry>
    </row>
    <row>
    <entry>name</entry>
    <entry><literal>NAME-DATA</literal>
    (see <xref linkend="name-data-def" />)</entry>
    </row>
    </tbody>
    </tgroup>
    </table>

    <para>
    Optional <acronym>ADR</acronym> data of any type is represented as
    <acronym>XDR</acronym> optional data.
    </para>

    <rm:proto name="OPTIONAL-DATA">
	<rm:data field="data" size="varies" type="ADR-DATA *">
	    Optional encoded data
	</rm:data>
    </rm:proto>

    <para>
    Array data is communicated as an <acronym>XDR</acronym> array of
    the element data.  That is, it is represented by an
    <acronym>XDR</acronym> <literal>unsigned int</literal> whose value
    is the number of array elements, followed by that number of element
    data.
    </para>

    <rm:proto name="ARRAY-DATA">
	<rm:data field="elements" size="varies" type="ADR-DATA&lt;&gt;">
	    array elements
	</rm:data>
    </rm:proto>

    <para>
    Structure data is communicated by communicating each structure
    field in the order they are defined.  This may consist of a mixture
    of optional and non-optional data.
    </para>

    <rm:proto name="STRUCT-DATA">
	<rm:data field="fields" size="varies" type="ADR-DATA[n]">
	    fixed-length array of structure fields
	    (n = type-defined field count)
	</rm:data>
    </rm:proto>

    <para>
    Enumeration data is communicated as an <acronym>XDR</acronym>
    <literal>unsigned int</literal> whose value is the 1-based index
    into the list of enumerated values (i.e. 1 would be the first
    enumerated value, n would be the nth enumerated value).  A value of
    0 represents the fallback value.  For enumerations without a
    fallback value, the 0 value is unused.
    </para>

    <rm:proto name="ENUM-DATA">
	<rm:data field="value" size="4" type="unsigned int">
	    0 if fallback, 1-based index otherwise
	</rm:data>
    </rm:proto>

    <para>
    Union data is communicated as an <acronym>XDR</acronym>
    <literal>unsigned int</literal> whose value is the 1-based index
    into the list of union arms.  A value of 0 represents the default
    arm.  When a non-default arm is selected, the arm is followed by
    that arm's data.  When the default arm is selected, it is first
    followed by the discriminant data value, and then is followed by
    the default arm's data.
    </para>

    <rm:proto name="UNION-DATA (non default)">
	<rm:data field="arm_index" size="4" type="unsigned int">
	    1-based index into list of arms
	</rm:data>
	<rm:data field="arm_data" size="varies" type="ADR-DATA">
	    Arm data
	</rm:data>
    </rm:proto>

    <rm:proto name="UNION-DATA (default)">
	<rm:data field="arm_index" size="4" type="unsigned int">
	    0
	</rm:data>
	<rm:data field="discriminant" size="4" type="ENUM-DATA | boolean">
	    discriminant value
	</rm:data>
	<rm:data field="arm_data" size="varies" type="ADR-DATA">
	    Arm data
	</rm:data>
    </rm:proto>

    </section>

    <!-- Consider switching this section and the previous section -->
    <section><title><acronym>ADR</acronym> types</title>
    <para>
    As discussed in <xref linkend="adr-data" />, the
    <acronym>ADR</acronym> data communicated by the
    <literal>rad</literal> protocol has a structure determined by its
    type.  Before that data can be communicated to the client, however,
    the types themselves must be communicated.
    </para>

    <para>
    For efficiency, type data is communicated using a system of type
    spaces and type references.  A type space is an array of type
    definitions that is referenced by a protocol-defined set of
    consumers.  A consumer referring to a type will use a type
    reference, which points to either a primitive type or to an element
    of the type space.
    </para>

    <para>
    Both type references and type spaces need to refer to the various
    types.  They do this using an integral type code:
    </para>

    <rm:proto name="TYPE-CODE">
	<rm:data field="type_code" size="4" type="int">
	<rm:codedef label="type" prefix="tc">
	    <rm:code num="0" name="void" />
	    <rm:code num="1" name="boolean" />
	    <rm:code num="2" name="integer" />
	    <rm:code num="3" name="uinteger" />
	    <rm:code num="4" name="long" />
	    <rm:code num="5" name="ulong" />
	    <rm:code num="6" name="float" />
	    <rm:code num="7" name="double" />
	    <rm:code num="8" name="time" />
	    <rm:code num="9" name="string" />
	    <rm:code num="10" name="opaque" />
	    <rm:code num="11" name="password" />
	    <rm:code num="12" name="name" />
	    <rm:code num="13" name="enum" />
	    <rm:code num="14" name="array" />
	    <rm:code num="15" name="struct" />
	    <rm:code num="16" name="union" />
	</rm:codedef>
	</rm:data>
    </rm:proto>

    <para>
    Concretely, a type reference consists of an XDR int whose value is
    one of the above type constants.  If the type is an enum, array,
    union, or struct, the type reference also includes an XDR int whose
    value is an index into the current type space:
    </para>

    <rm:proto name="TYPEREF (basic type)">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    a primitive type code
	</rm:data>
    </rm:proto>

    <rm:proto name="TYPEREF (derived type)">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    <literal>TC-ENUM</literal>, <literal>TC-ARRAY</literal>,
	    <literal>TC-STRUCT</literal>, or <literal>TC-UNION</literal>
	</rm:data>
	<rm:data field="type_index" size="4" type="int">
	    type space index
	</rm:data>
    </rm:proto>

    <para>
    A type space is a topologically sorted array of type definitions.
    A derived type may only reference derived types defined earlier in
    the type space.  That is, a type reference used by the type at
    index <mathphrase>X</mathphrase> may only reference a primitive
    type or a derived type a index less than
    <mathphrase>X</mathphrase>.  Recursively defined types are not
    supported.
    </para>

    <para>
    Apart from a common distinguishing type code, each derived type is
    defined differently.  Arrays are the simplest.  An array definition
    consists of only a reference to the element type:
    </para>

    <rm:proto name="ARRAY-TYPE">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    <literal>TC-ARRAY</literal>
	</rm:data>
	<rm:data field="element_type" size="varies" type="TYPEREF">
	    the element type
	</rm:data>
    </rm:proto>

    <para>
    A structure type definition consists of a name and an array of
    field definitions.  The order in which the fields are specified in
    <literal>STRUCT-TYPE</literal> is the order used to serialize the
    fields in <literal>STRUCT-DATA</literal>.
    </para>

    <rm:proto name="FIELD-TYPE">
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    the field name
	</rm:data>
	<rm:data field="optional" size="4" type="boolean">
	    is the field value optional?
	</rm:data>
	<rm:data field="type" size="varies" type="TYPEREF">
	    the field type
	</rm:data>
    </rm:proto>

    <rm:proto name="STRUCT-TYPE">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    <literal>TC-STRUCT</literal>
	</rm:data>
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    the structure name
	</rm:data>
	<rm:data field="fields" size="varies" type="FIELD-TYPE&lt;&gt;">
	    ordered list of structure fields
	</rm:data>
    </rm:proto>

    <para>
    A union type definition consists of a name, a reference to the
    discriminant type, optionally a default arm specification, and an
    array of arm definitions.  The arm index referenced by
    <literal>UNION-DATA</literal> is an index into the array of arms
    defined by the corresponding <literal>UNION-TYPE</literal>.
    </para>

    <rm:proto name="ARM-TYPE">
	<rm:data field="value" size="4" type="ENUM-DATA | boolean">
	    the discriminant value that selects this arm
	</rm:data>
	<rm:data field="optional" size="4" type="boolean">
	    is the arm's value optional?
	</rm:data>
	<rm:data field="type" size="varies" type="TYPEREF">
	    the arm's type
	</rm:data>
    </rm:proto>

    <rm:proto name="UNION-TYPE (without default arm)">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    <literal>TC-UNION</literal>
	</rm:data>
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    the union name
	</rm:data>
	<rm:data field="disc_type" size="varies" type="TYPEREF">
	    the discriminant type
	</rm:data>
	<rm:data field="hasdefault" size="4" type="boolean">
	    false
	</rm:data>
	<rm:data field="arms" size="varies" type="ARM-TYPE&lt;&gt;">
	    ordered list of arms
	</rm:data>
    </rm:proto>

    <rm:proto name="UNION-TYPE (with default arm)">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    <literal>TC-UNION</literal>
	</rm:data>
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    the union name
	</rm:data>
	<rm:data field="disc_type" size="varies" type="TYPEREF">
	    the discriminant type
	</rm:data>
	<rm:data field="hasdefault" size="4" type="boolean">
	    true
	</rm:data>
	<rm:data field="def_optional" size="4" type="boolean">
	    is the default arm value optional?
	</rm:data>
	<rm:data field="def_type" size="varies" type="TYPEREF">
	    the default arm type
	</rm:data>
	<rm:data field="arms" size="varies" type="ARM-TYPE&lt;&gt;">
	    ordered list of arms
	</rm:data>
    </rm:proto>

    <para>
    Lastly, an enum type definition consists of a name, an optional
    fallback value, and a list of enumeration values.  The value index
    referenced by <literal>ENUM-DATA</literal> is an index into the
    array of values defined by the corresponding
    <literal>ENUM-TYPE</literal>.
    </para>

    <rm:proto name="VALUE-TYPE">
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    the value's name
	</rm:data>
	<rm:data field="value" size="4" type="int">
	    the value's assigned value
	</rm:data>
    </rm:proto>

    <rm:proto name="ENUM-TYPE">
	<rm:data field="type_code" size="4" type="TYPE-CODE">
	    <literal>TC-ENUM</literal>
	</rm:data>
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    the enum name
	</rm:data>
	<rm:data field="fb_name" size="varies" type="string&lt;&gt; *">
	    the fallback value's name, if one exists
	</rm:data>
	<rm:data field="values" size="varies" type="VALUE-TYPE&lt;&gt;">
	    ordered list of values
	</rm:data>
    </rm:proto>

    <para>
    The type space itself is an array.  Each element is one of these
    four type definitions (<literal>ARRAY-TYPE</literal>,
    <literal>STRUCT-TYPE</literal>, <literal>UNION-TYPE</literal>, or
    <literal>ENUM-TYPE</literal>).
    </para>

    <rm:proto name="TYPESPACE">
	<rm:data field="types" size="varies" type="?-TYPE&lt;&gt;">
	    ordered list of types in the type space
	</rm:data>
    </rm:proto>

    </section>

    <section><title><acronym>API</acronym> definitions</title>
    <para>
    The ultimate description of the interactions permitted with a
    particular object is its <acronym>API</acronym> definition.  An
    <acronym>API</acronym> definition contains many elements: an
    interface name, a list of versioned <acronym>API</acronym> names
    the <acronym>API</acronym> supports, and definitions of the
    <acronym>API</acronym>'s attributes, methods, and events.
    </para>

    <para>
    Each <acronym>API</acronym> name has a set of stabilities and
    versions:
    </para>

    <rm:proto name="STABILITY-CODE">
	<rm:data field="stability_code" size="4" type="int">
	<rm:codedef label="stability" prefix="sc">
	    <rm:code num="1" name="private" />
	    <rm:code num="2" name="uncommitted" />
	    <rm:code num="3" name="committed" />
	</rm:codedef>
	</rm:data>
    </rm:proto>

    <rm:proto name="VERSION-DATA">
	<rm:data field="stability" size="4" type="STABILITY-CODE">
	    stability version applies to
	</rm:data>
	<rm:data field="major" size="4" type="int">
	    major version number
	</rm:data>
	<rm:data field="minor" size="4" type="int">
	    minor version number
	</rm:data>
    </rm:proto>

    <rm:proto name="APINAME-DATA">
	<rm:data field="api_name" size="varies" type="string&lt;&gt;">
	    API name
	</rm:data>
	<rm:data field="versions" size="varies" type="VERSION-DATA&lt;&gt;">
	    API versions by stability
	</rm:data>
    </rm:proto>

    <para>
    An attribute consists of a name, stability, various flags, a type,
    and separate, optional read and write error types.
    </para>

    <rm:proto name="ATTRIBUTE-TYPE">
	<rm:data field="aname" size="varies" type="string&lt;&gt;">
	    attribute name
	</rm:data>
	<rm:data field="stability" size="4" type="STABILITY-CODE">
	    stability
	</rm:data>
	<rm:data field="readable" size="4" type="boolean">
	    is attribute readable?
	</rm:data>
	<rm:data field="writable" size="4" type="boolean">
	    is attribute writable?
	</rm:data>
	<rm:data field="optional" size="4" type="boolean">
	    is attribute optional?
	</rm:data>
	<rm:data field="type" size="varies" type="TYPEREF">
	    attribute type
	</rm:data>
	<rm:data field="read_error" size="varies" type="TYPEREF *">
	    error data on read, if applicable
	</rm:data>
	<rm:data field="write_error" size="varies" type="TYPEREF *">
	    error data on write, if applicable
	</rm:data>
    </rm:proto>

    <para>
    A method looks a lot like an attribute.  It has a name, stability,
    a result type, only a single optional error type, and an array of
    argument definitions.
    </para>

    <rm:proto name="ARGUMENT-TYPE">
	<rm:data field="argname" size="varies" type="string&lt;&gt;">
	    argument name
	</rm:data>
	<rm:data field="optional" size="4" type="boolean">
	    is argument optional?
	</rm:data>
	<rm:data field="type" size="varies" type="TYPEREF">
	    argument type
	</rm:data>
    </rm:proto>

    <rm:proto name="METHOD-TYPE">
	<rm:data field="mname" size="varies" type="string&lt;&gt;">
	    method name
	</rm:data>
	<rm:data field="stability" size="4" type="STABILITY-CODE">
	    stability
	</rm:data>
	<rm:data field="optional" size="4" type="boolean">
	    is result optional?
	</rm:data>
	<rm:data field="result_type" size="varies" type="TYPEREF">
	    result type
	</rm:data>
	<rm:data field="error" size="varies" type="TYPEREF *">
	    error data, if applicable
	</rm:data>
	<rm:data field="args" size="varies" type="ARGUMENT-TYPE&lt;&gt;">
	    arguments
	</rm:data>
    </rm:proto>

    <para>
    An event consists only of a name, stability, and type.
    </para>

    <rm:proto name="EVENT-TYPE">
	<rm:data field="ename" size="varies" type="string&lt;&gt;">
	    method name
	</rm:data>
	<rm:data field="stability" size="4" type="STABILITY-CODE">
	    stability
	</rm:data>
	<rm:data field="event_type" size="varies" type="TYPEREF">
	    event type
	</rm:data>
    </rm:proto>

    <para>
    Finally, an API definition combines all the above elements.
    </para>

    <rm:proto name="API-TYPE">
	<rm:data field="ifname" size="varies" type="string&lt;&gt;">
	    interface name (domain)
	</rm:data>
	<rm:data field="apis" size="varies" type="APINAME-DATA&lt;&gt;">
	    API names and versions implemented
	</rm:data>
	<rm:data field="types" size="varies" type="TYPESET&lt;&gt;">
	    Types used by API definition
	</rm:data>
	<rm:data field="attributes" size="varies" type="ATTRIBUTE-TYPE&lt;&gt;">
	    API attributes
	</rm:data>
	<rm:data field="methods" size="varies" type="METHOD-TYPE&lt;&gt;">
	    API methods
	</rm:data>
	<rm:data field="events" size="varies" type="EVENT-TYPE&lt;&gt;">
	    API events
	</rm:data>
    </rm:proto>

    </section>

</section>
<section id="proto-handshake"><title>Connection Initialization</title>
    <para>
    Once a connection has been established between a client and server,
    a short synchronous handshake initiates the rad protocol.  The
    server begins by sending a <literal>SERVER-HELLO</literal>
    message.  This message specifies the minimum and maximum protocol
    versions (inclusive) recognized by the server.
    </para>

    <rm:proto name="SERVER-HELLO">
	<rm:data field="protocol" size="3" type="string[3]">
	    <quote>RAD</quote>
	</rm:data>
	<rm:data field="min_ver" size="4" type="int">
	    minimum supported version
	</rm:data>
	<rm:data field="max_ver" size="4" type="int">
	    maximum supported version
	</rm:data>
    </rm:proto>

    <para>
    The client then replies with a <literal>CLIENT-HELLO</literal>
    message specifying the version it wishes to use.  This version may
    not be less than the server's minimum version or greater than the
    server's maximum version.
    </para>

    <rm:proto name="CLIENT-HELLO">
	<rm:data field="protocol" size="3" type="string[3]">
	    <quote>RAD</quote>
	</rm:data>
	<rm:data field="version" size="4" type="int">
	    client-selected version
	</rm:data>
	<rm:data field="locale" size="varies" type="string&lt;256&gt;">
	    client locale
	</rm:data>
    </rm:proto>

    <para>
    Part of the <literal>rad</literal> protocol is the communication of
    structured error data on request failures.  For consistency with
    the object-specific errors that server-side objects are permitted
    to return, the errors returned by rad when requests fail for other
    reasons are also defined using <acronym>ADR</acronym>.  After the
    server receives and accepts a <literal>CLIENT-HELLO</literal>
    message, it replies with <literal>ERRORS</literal> to communicate
    those type definitions:
    </para>

    <rm:proto name="ERRORS">
	<rm:data field="error_space" size="varies" type="TYPESPACE">
	    typespace containing error types
	</rm:data>
	<rm:data field="errors" size="varies" type="TYPEREF&lt;&gt;">
	    errors types, starting with <literal>EC-NOMEM</literal>
	</rm:data>
    </rm:proto>

    <para>
    The <literal>TYPEREF</literal>s refer to the types defined in the
    <literal>error_space</literal> <literal>TYPESPACE</literal>.  The
    types define the first <literal>error_count</literal> errors,
    starting from the first non object-specific error,
    <literal>EC-NOMEM</literal>.  Any errors for which types are not
    defined have type <quote>void</quote>.
    </para>

    <!--
      These errors are defined in ADR for convenience and for
      architectural consistency.  However, from a interface perspective
      they should be part of the protocol and documented here.
    -->

    <para>
    At this point, the handshake is complete and normal client-server
    communication can occur.
    </para>
</section>


<section><title>Messages</title>
    <para>
    Normal communication consists of an asynchronous exchange of
    messages: <literal>REQUEST</literal>s from the client to the
    server, <literal>RESPONSE</literal>s from the server to the client,
    and <literal>EVENT</literal>s from the server to the client.
    </para>

    <para>
    A <literal>REQUEST</literal> is the <literal>rad</literal>
    equivalent of a low-level remote procedure call.  It consists of a
    client-selected, non-zero serial number, an operation code, and an
    opaque, operation-specific, variable-length payload.
    </para>

    <rm:proto name="REQUEST">
	<rm:data field="serial" size="8" type="hyper">
	    client-specified serial number
	</rm:data>
	<rm:data field="opcode" size="4" type="OP-CODE">
	    operation code (see <xref linkend="op-data-def" />)
	</rm:data>
	<rm:data field="payload" size="varies" type="opaque&lt;&gt;">
	    request payload
	</rm:data>
    </rm:proto>

    <para>
    The server will respond to every <literal>REQUEST</literal> with a
    <literal>RESPONSE</literal>.  The structure of a
    <literal>RESPONSE</literal> varies depending on whether the server
    was successful in processing the client's request.  A
    <literal>RESPONSE</literal> consists of the serial number of the
    corresponding <literal>REQUEST</literal>, a boolean indicating
    whether the request was successful, and either an
    operation-specific opaque payload (in the case of success), or an
    error code and an error-specific error payload (in the case of
    failure):
    </para>

    <rm:proto name="RESPONSE (successful)">
	<rm:data field="serial" size="8" type="hyper">
	    serial number of REQUEST
	</rm:data>
	<rm:data field="success" size="4" type="boolean">
	    true
	</rm:data>
	<rm:data field="payload" size="varies" type="opaque&lt;&gt;">
	    response payload
	</rm:data>
    </rm:proto>

    <rm:proto name="RESPONSE (failure)">
	<rm:data field="serial" size="8" type="hyper">
	    serial number of REQUEST
	</rm:data>
	<rm:data field="success" size="4" type="boolean">
	    false
	</rm:data>
	<rm:data field="error" size="4" type="ERROR-CODE">
	    error code (see <xref linkend="error-data-def" />)
	</rm:data>
	<rm:data field="payload" size="varies" type="opaque&lt;&gt;">
	    error payload
	</rm:data>
    </rm:proto>

    <para>
    The <literal>rad</literal> protocol doesn't require the client to
    wait for a <literal>RESPONSE</literal> before sending another
    <literal>REQUEST</literal>.  However, the server implementation may
    place limits on the number of outstanding requests that can be
    handled simultaneously.  A client with an outstanding
    <literal>REQUEST</literal> must assume that a subsequent
    <literal>REQUEST</literal> will block until the
    <literal>RESPONSE</literal> from the outstanding
    <literal>REQUEST</literal> is read.
    </para>

    <para>
    Lastly, a client may (via a <literal>REQUEST</literal>) subscribe
    to asynchronous event sources.  When an event occurs, the server
    will send an <literal>EVENT</literal> message to the client.  An
    <literal>EVENT</literal> message will include the object ID of the
    source object (more later), the time of the event, the name of the
    event, and an event-specific opaque payload.  An
    <literal>EVENT</literal> message is distinguished from a
    <literal>RESPONSE</literal> by having a serial number of 0.
    </para>

    <rm:proto name="EVENT">
	<rm:data field="serial" size="8" type="hyper">
	    0
	</rm:data>
	<rm:data field="source" size="8" type="hyper">
	    id of generating object
	</rm:data>
	<rm:data field="sequence" size="8" type="hyper">
	    sequence number of event
	</rm:data>
	<rm:data field="timestamp" size="12" type="TIME-DATA">
	    time of event
	</rm:data>
	<rm:data field="name" size="varies" type="string&lt;&gt;">
	    event name
	</rm:data>
	<rm:data field="payload" size="varies" type="opaque&lt;&gt;">
	    event payload
	</rm:data>
    </rm:proto>

</section>


<section><title>Operations</title>
    <para>
    Each operation that a client can perform against a
    <command>rad</command> server has its own request and response
    payloads.  To facilitate processing without needing to fully decode
    the payload, they are communicated as variable-lengthed
    <literal>opaque</literal> data in the <literal>REQUEST</literal>
    and <literal>RESPONSE</literal>.
    </para>

    <para>
    For consistency and flexibility, all <acronym>ADR</acronym> data
    referenced by these payloads is communicated as
    <literal>OPTIONAL-DATA</literal>, which in turn is wrapped as
    <literal>opaque</literal> data.
    </para>

    <rm:proto name="PAYLOAD-DATA">
	<rm:data field="data" size="varies" type="opaque&lt;&gt;">
	    encapsulated <literal>OPTIONAL-DATA</literal>
	</rm:data>
    </rm:proto>

    <section><title>INVOKE</title>

    <para>
    INVOKE makes a method call against a <command>rad</command> object,
    identified by its object ID.
    </para>

    <rm:proto name="INVOKE-REQUEST">
	<rm:data field="objectid" size="8" type="hyper">
	    Object ID, returned by lookup
	</rm:data>
	<rm:data field="mname" size="varies" type="string&lt;&gt;">
	    the method to invoke
	</rm:data>
	<rm:data field="arguments" size="varies" type="PAYLOAD-DATA&lt;&gt;">
	    array of method arguments
	</rm:data>
    </rm:proto>

    <rm:proto name="INVOKE-RESPONSE">
	<rm:data field="result" size="varies" type="PAYLOAD-DATA">
	    the return value of method call, if any
	</rm:data>
    </rm:proto>

    <rm:errors op="INVOKE">
	<rm:error code="EC-OBJECT">
	    The method call was made but failed for an object-specific
	    reason.
	</rm:error>
	<rm:error code="EC-NOTFOUND">
	    <literal>objectid</literal> isn't a known object id, or the
	    object doesn't have the method <literal>mname</literal>.
	</rm:error>
	<rm:error code="EC-MISMATCH">
	    The wrong number of arguments were provided, or a
	    non-optional argument was missing.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

    <section><title>GETATTR</title>

    <para>
    GETATTR reads an attribute of a <command>rad</command> object,
    identified by its object ID.
    </para>

    <rm:proto name="GETATTR-REQUEST">
	<rm:data field="objectid" size="8" type="hyper">
	    Object ID, returned by lookup
	</rm:data>
	<rm:data field="aname" size="varies" type="string&lt;&gt;">
	    the attribute to read
	</rm:data>
    </rm:proto>

    <rm:proto name="GETATTR-RESPONSE">
	<rm:data field="result" size="varies" type="PAYLOAD-DATA">
	    the value of the attribute
	</rm:data>
    </rm:proto>

    <rm:errors op="GETATTR">
	<rm:error code="EC-OBJECT">
	    An attempt to read the attribute was made, but failed for
	    an object-specific reason.
	</rm:error>
	<rm:error code="EC-NOTFOUND">
	    <literal>objectid</literal> isn't a known object id, or the
	    object doesn't have the attribute <literal>aname</literal>.
	</rm:error>
	<rm:error code="EC-ILLEGAL">
	    <literal>aname</literal> refers to a write-only attribute.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

    <section><title>SETATTR</title>

    <para>
    SETATTR reads an attribute of a <command>rad</command> object,
    identified by its object ID.  The response payload for a SETATTR
    request is empty.
    </para>

    <rm:proto name="SETATTR-REQUEST">
	<rm:data field="objectid" size="8" type="hyper">
	    Object ID, returned by lookup
	</rm:data>
	<rm:data field="aname" size="varies" type="string&lt;&gt;">
	    the attribute to write
	</rm:data>
	<rm:data field="value" size="varies" type="PAYLOAD-DATA">
	    the new value of the attribute
	</rm:data>
    </rm:proto>

    <rm:errors op="SETATTR">
	<rm:error code="EC-OBJECT">
	    An attempt to write the attribute was made, but failed for
	    an object-specific reason.
	</rm:error>
	<rm:error code="EC-NOTFOUND">
	    <literal>objectid</literal> isn't a known object id, or the
	    object doesn't have the attribute <literal>aname</literal>.
	</rm:error>
	<rm:error code="EC-MISMATCH">
	    <literal>aname</literal> has a non-optional value and
	    <literal>value</literal> was NULL.
	</rm:error>
	<rm:error code="EC-ILLEGAL">
	    <literal>aname</literal> refers to a read-only attribute.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

    <section><title>LOOKUP</title>

    <para>
    LOOKUP attempts to find the named object in the server's namespace,
    returning the object and API IDs of the object if it exists.  Since
    an object isn't usable until its API has been DEFINEed, the client
    may request the API definition be provided as part of the LOOKUP
    response.  For the same reason, the server may unilaterally decide
    to provide the API definition if it believes the client hasn't seen
    it yet.
    </para>

    <rm:proto name="LOOKUP-REQUEST">
	<rm:data field="name" size="varies" type="NAME-DATA">
	    object name
	</rm:data>
	<rm:data field="define" size="4" type="boolean">
	    include object definition?
	</rm:data>
    </rm:proto>

    <rm:proto name="LOOKUP-RESPONSE">
	<rm:data field="objectid" size="8" type="hyper">
	    ID of the object
	</rm:data>
	<rm:data field="apiid" size="8" type="hyper">
	    ID of the object's API
	</rm:data>
	<rm:data field="definition" size="varies" type="API-TYPE *">
	    the definition of the object's API
	</rm:data>
    </rm:proto>

    <rm:errors op="LOOKUP">
	<rm:error code="EC-NOTFOUND">
	    <literal>name</literal> doesn't exist.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

    <section><title>DEFINE</title>

    <para>
    DEFINE requests a definition of the specified API ID.
    </para>

    <rm:proto name="DEFINE-REQUEST">
	<rm:data field="apiid" size="8" type="hyper">
	    API ID
	</rm:data>
    </rm:proto>

    <rm:proto name="DEFINE-RESPONSE">
	<rm:data field="definition" size="varies" type="API-TYPE">
	    the definition of the API
	</rm:data>
    </rm:proto>

    <rm:errors op="DEFINE">
	<rm:error code="EC-NOTFOUND">
	    <literal>apiid</literal> isn't a known
	    <acronym>API</acronym> ID.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

    <section><title>LIST</title>

    <para>
    LIST requests an enumeration of all objects present in the server
    that match the specified object name pattern.  The empty string
    matches all server objects.
    </para>

    <rm:proto name="LIST-REQUEST">
	<rm:data field="pattern" size="varies" type="NAME-DATA">
	    object name pattern
	</rm:data>
    </rm:proto>

    <rm:proto name="LIST-RESPONSE">
	<rm:data field="names" size="varies" type="NAME-DATA&lt;&gt;">
	    the definition of the API
	</rm:data>
    </rm:proto>

    <rm:errors op="LIST">
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

    <section><title>SUB and UNSUB</title>

    <para>
    SUB and UNSUB subscribe and unsubscribe, respectively, to the named
    event of the specified object.  The response payload for a
    successful SUB or UNSUB is empty.
    </para>
    
    <para>
    Note that it is possible to receive an <literal>EVENT</literal>
    that has been UNSUBed after a successful UNSUB.
    </para>

    <rm:proto name="SUB-REQUEST">
	<rm:data field="objectid" size="8" type="hyper">
	    ID of the object
	</rm:data>
	<rm:data field="event" size="varies" type="string&lt;&gt;">
	    event name
	</rm:data>
    </rm:proto>

    <rm:proto name="UNSUB-RESPONSE">
	<rm:data field="objectid" size="8" type="hyper">
	    ID of the object
	</rm:data>
	<rm:data field="event" size="varies" type="string&lt;&gt;">
	    event name
	</rm:data>
    </rm:proto>

    <rm:errors op="SUB">
	<rm:error code="EC-NOTFOUND">
	    <literal>objectid</literal> isn't a known object id, or the
	    object doesn't have the event <literal>event</literal>.
	</rm:error>
	<rm:error code="EC-EXISTS">
	    The client is already subscribed to <literal>event</literal>.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    <rm:errors op="UNSUB">
	<rm:error code="EC-NOTFOUND">
	    <literal>objectid</literal> isn't a known object id, the
	    object doesn't have the event <literal>event</literal>, or
	    the client isn't subscribed to <literal>event</literal>.
	</rm:error>
	<rm:error code="EC-NOMEM">
	    The server had insufficient resources to complete the
	    operation.
	</rm:error>
	<rm:error code="EC-SYSTEM">
	    An unexpected internal error occurred.
	</rm:error>
    </rm:errors>

    </section>

</section>

</appendix>