<?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<></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 ≤ nsec ≤
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<>">
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<></literal></entry>
</row>
<row>
<entry>opaque</entry>
<entry><acronym>XDR</acronym> <literal>opaque<></literal></entry>
</row>
<row>
<entry>password</entry>
<entry><acronym>XDR</acronym> <literal>string<></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<>">
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<>">
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<>">
the structure name
</rm:data>
<rm:data field="fields" size="varies" type="FIELD-TYPE<>">
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<>">
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<>">
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<>">
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<>">
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<>">
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<>">
the enum name
</rm:data>
<rm:data field="fb_name" size="varies" type="string<> *">
the fallback value's name, if one exists
</rm:data>
<rm:data field="values" size="varies" type="VALUE-TYPE<>">
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<>">
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<>">
API name
</rm:data>
<rm:data field="versions" size="varies" type="VERSION-DATA<>">
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<>">
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<>">
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<>">
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<>">
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<>">
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<>">
interface name (domain)
</rm:data>
<rm:data field="apis" size="varies" type="APINAME-DATA<>">
API names and versions implemented
</rm:data>
<rm:data field="types" size="varies" type="TYPESET<>">
Types used by API definition
</rm:data>
<rm:data field="attributes" size="varies" type="ATTRIBUTE-TYPE<>">
API attributes
</rm:data>
<rm:data field="methods" size="varies" type="METHOD-TYPE<>">
API methods
</rm:data>
<rm:data field="events" size="varies" type="EVENT-TYPE<>">
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<256>">
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<>">
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<>">
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<>">
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<>">
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<>">
event name
</rm:data>
<rm:data field="payload" size="varies" type="opaque<>">
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<>">
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<>">
the method to invoke
</rm:data>
<rm:data field="arguments" size="varies" type="PAYLOAD-DATA<>">
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<>">
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<>">
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<>">
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<>">
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<>">
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>