summaryrefslogtreecommitdiffstats
path: root/cpukit/librpc
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/librpc')
-rw-r--r--cpukit/librpc/src/rpc/PSD.doc/nfs.rfc.ms1372
-rw-r--r--cpukit/librpc/src/rpc/PSD.doc/rpc.prog.ms2684
-rw-r--r--cpukit/librpc/src/rpc/PSD.doc/rpc.rfc.ms1302
-rw-r--r--cpukit/librpc/src/rpc/PSD.doc/rpcgen.ms1299
-rw-r--r--cpukit/librpc/src/rpc/PSD.doc/xdr.nts.ms1966
-rw-r--r--cpukit/librpc/src/rpc/PSD.doc/xdr.rfc.ms1058
6 files changed, 0 insertions, 9681 deletions
diff --git a/cpukit/librpc/src/rpc/PSD.doc/nfs.rfc.ms b/cpukit/librpc/src/rpc/PSD.doc/nfs.rfc.ms
deleted file mode 100644
index 0c9a8995b5..0000000000
--- a/cpukit/librpc/src/rpc/PSD.doc/nfs.rfc.ms
+++ /dev/null
@@ -1,1372 +0,0 @@
-.\"
-.\" Must use -- tbl -- with this one
-.\"
-.\" @(#)nfs.rfc.ms 2.2 88/08/05 4.0 RPCSRC
-.de BT
-.if \\n%=1 .tl ''- % -''
-..
-.ND
-.\" prevent excess underlining in nroff
-.if n .fp 2 R
-.OH 'Network File System: Version 2 Protocol Specification''Page %'
-.EH 'Page %''Network File System: Version 2 Protocol Specification'
-.if \\n%=1 .bp
-.SH
-\&Network File System: Version 2 Protocol Specification
-.IX NFS "" "" "" PAGE MAJOR
-.IX "Network File System" "" "" "" PAGE MAJOR
-.IX NFS "version-2 protocol specification"
-.IX "Network File System" "version-2 protocol specification"
-.LP
-.NH 0
-\&Status of this Standard
-.LP
-Note: This document specifies a protocol that Sun Microsystems, Inc.,
-and others are using. It specifies it in standard ARPA RFC form.
-.NH 1
-\&Introduction
-.IX NFS introduction
-.LP
-The Sun Network Filesystem (NFS) protocol provides transparent remote
-access to shared filesystems over local area networks. The NFS
-protocol is designed to be machine, operating system, network architecture,
-and transport protocol independent. This independence is
-achieved through the use of Remote Procedure Call (RPC) primitives
-built on top of an External Data Representation (XDR). Implementations
-exist for a variety of machines, from personal computers to
-supercomputers.
-.LP
-The supporting mount protocol allows the server to hand out remote
-access privileges to a restricted set of clients. It performs the
-operating system-specific functions that allow, for example, to
-attach remote directory trees to some local file system.
-.NH 2
-\&Remote Procedure Call
-.IX "Remote Procedure Call"
-.LP
-Sun's remote procedure call specification provides a procedure-
-oriented interface to remote services. Each server supplies a
-program that is a set of procedures. NFS is one such "program".
-The combination of host address, program number, and procedure
-number specifies one remote service procedure. RPC does not depend
-on services provided by specific protocols, so it can be used with
-any underlying transport protocol. See the
-.I "Remote Procedure Calls: Protocol Specification"
-chapter of this manual.
-.NH 2
-\&External Data Representation
-.IX "External Data Representation"
-.LP
-The External Data Representation (XDR) standard provides a common
-way of representing a set of data types over a network.
-The NFS
-Protocol Specification is written using the RPC data description
-language.
-For more information, see the
-.I " External Data Representation Standard: Protocol Specification."
-Sun provides implementations of XDR and
-RPC, but NFS does not require their use. Any software that
-provides equivalent functionality can be used, and if the encoding
-is exactly the same it can interoperate with other implementations
-of NFS.
-.NH 2
-\&Stateless Servers
-.IX "stateless servers"
-.IX servers stateless
-.LP
-The NFS protocol is stateless. That is, a server does not need to
-maintain any extra state information about any of its clients in
-order to function correctly. Stateless servers have a distinct
-advantage over stateful servers in the event of a failure. With
-stateless servers, a client need only retry a request until the
-server responds; it does not even need to know that the server has
-crashed, or the network temporarily went down. The client of a
-stateful server, on the other hand, needs to either detect a server
-crash and rebuild the server's state when it comes back up, or
-cause client operations to fail.
-.LP
-This may not sound like an important issue, but it affects the
-protocol in some unexpected ways. We feel that it is worth a bit
-of extra complexity in the protocol to be able to write very simple
-servers that do not require fancy crash recovery.
-.LP
-On the other hand, NFS deals with objects such as files and
-directories that inherently have state -- what good would a file be
-if it did not keep its contents intact? The goal is to not
-introduce any extra state in the protocol itself. Another way to
-simplify recovery is by making operations "idempotent" whenever
-possible (so that they can potentially be repeated).
-.NH 1
-\&NFS Protocol Definition
-.IX NFS "protocol definition"
-.IX NFS protocol
-.LP
-Servers have been known to change over time, and so can the
-protocol that they use. So RPC provides a version number with each
-RPC request. This RFC describes version two of the NFS protocol.
-Even in the second version, there are various obsolete procedures
-and parameters, which will be removed in later versions. An RFC
-for version three of the NFS protocol is currently under
-preparation.
-.NH 2
-\&File System Model
-.IX filesystem model
-.LP
-NFS assumes a file system that is hierarchical, with directories as
-all but the bottom-level files. Each entry in a directory (file,
-directory, device, etc.) has a string name. Different operating
-systems may have restrictions on the depth of the tree or the names
-used, as well as using different syntax to represent the "pathname",
-which is the concatenation of all the "components" (directory and
-file names) in the name. A "file system" is a tree on a single
-server (usually a single disk or physical partition) with a specified
-"root". Some operating systems provide a "mount" operation to make
-all file systems appear as a single tree, while others maintain a
-"forest" of file systems. Files are unstructured streams of
-uninterpreted bytes. Version 3 of NFS uses a slightly more general
-file system model.
-.LP
-NFS looks up one component of a pathname at a time. It may not be
-obvious why it does not just take the whole pathname, traipse down
-the directories, and return a file handle when it is done. There are
-several good reasons not to do this. First, pathnames need
-separators between the directory components, and different operating
-systems use different separators. We could define a Network Standard
-Pathname Representation, but then every pathname would have to be
-parsed and converted at each end. Other issues are discussed in
-\fINFS Implementation Issues\fP below.
-.LP
-Although files and directories are similar objects in many ways,
-different procedures are used to read directories and files. This
-provides a network standard format for representing directories. The
-same argument as above could have been used to justify a procedure
-that returns only one directory entry per call. The problem is
-efficiency. Directories can contain many entries, and a remote call
-to return each would be just too slow.
-.NH 2
-\&RPC Information
-.IX NFS "RPC information"
-.IP \fIAuthentication\fP
-The NFS service uses
-.I AUTH_UNIX ,
-.I AUTH_DES ,
-or
-.I AUTH_SHORT
-style
-authentication, except in the NULL procedure where
-.I AUTH_NONE
-is also allowed.
-.IP "\fITransport Protocols\fP"
-NFS currently is supported on UDP/IP only.
-.IP "\fIPort Number\fP"
-The NFS protocol currently uses the UDP port number 2049. This is
-not an officially assigned port, so later versions of the protocol
-use the \*QPortmapping\*U facility of RPC.
-.NH 2
-\&Sizes of XDR Structures
-.IX "XDR structure sizes"
-.LP
-These are the sizes, given in decimal bytes, of various XDR
-structures used in the protocol:
-.DS
-/* \fIThe maximum number of bytes of data in a READ or WRITE request\fP */
-const MAXDATA = 8192;
-
-/* \fIThe maximum number of bytes in a pathname argument\fP */
-const MAXPATHLEN = 1024;
-
-/* \fIThe maximum number of bytes in a file name argument\fP */
-const MAXNAMLEN = 255;
-
-/* \fIThe size in bytes of the opaque "cookie" passed by READDIR\fP */
-const COOKIESIZE = 4;
-
-/* \fIThe size in bytes of the opaque file handle\fP */
-const FHSIZE = 32;
-.DE
-.NH 2
-\&Basic Data Types
-.IX "NFS data types"
-.IX NFS "basic data types"
-.LP
-The following XDR definitions are basic structures and types used
-in other structures described further on.
-.KS
-.NH 3
-\&stat
-.IX "NFS data types" stat "" \fIstat\fP
-.DS
-enum stat {
- NFS_OK = 0,
- NFSERR_PERM=1,
- NFSERR_NOENT=2,
- NFSERR_IO=5,
- NFSERR_NXIO=6,
- NFSERR_ACCES=13,
- NFSERR_EXIST=17,
- NFSERR_NODEV=19,
- NFSERR_NOTDIR=20,
- NFSERR_ISDIR=21,
- NFSERR_FBIG=27,
- NFSERR_NOSPC=28,
- NFSERR_ROFS=30,
- NFSERR_NAMETOOLONG=63,
- NFSERR_NOTEMPTY=66,
- NFSERR_DQUOT=69,
- NFSERR_STALE=70,
- NFSERR_WFLUSH=99
-};
-.DE
-.KE
-.LP
-The
-.I stat
-type is returned with every procedure's results. A
-value of
-.I NFS_OK
-indicates that the call completed successfully and
-the results are valid. The other values indicate some kind of
-error occurred on the server side during the servicing of the
-procedure. The error values are derived from UNIX error numbers.
-.IP \fBNFSERR_PERM\fP:
-Not owner. The caller does not have correct ownership
-to perform the requested operation.
-.IP \fBNFSERR_NOENT\fP:
-No such file or directory. The file or directory
-specified does not exist.
-.IP \fBNFSERR_IO\fP:
-Some sort of hard error occurred when the operation was
-in progress. This could be a disk error, for example.
-.IP \fBNFSERR_NXIO\fP:
-No such device or address.
-.IP \fBNFSERR_ACCES\fP:
-Permission denied. The caller does not have the
-correct permission to perform the requested operation.
-.IP \fBNFSERR_EXIST\fP:
-File exists. The file specified already exists.
-.IP \fBNFSERR_NODEV\fP:
-No such device.
-.IP \fBNFSERR_NOTDIR\fP:
-Not a directory. The caller specified a
-non-directory in a directory operation.
-.IP \fBNFSERR_ISDIR\fP:
-Is a directory. The caller specified a directory in
-a non- directory operation.
-.IP \fBNFSERR_FBIG\fP:
-File too large. The operation caused a file to grow
-beyond the server's limit.
-.IP \fBNFSERR_NOSPC\fP:
-No space left on device. The operation caused the
-server's filesystem to reach its limit.
-.IP \fBNFSERR_ROFS\fP:
-Read-only filesystem. Write attempted on a read-only filesystem.
-.IP \fBNFSERR_NAMETOOLONG\fP:
-File name too long. The file name in an operation was too long.
-.IP \fBNFSERR_NOTEMPTY\fP:
-Directory not empty. Attempted to remove a
-directory that was not empty.
-.IP \fBNFSERR_DQUOT\fP:
-Disk quota exceeded. The client's disk quota on the
-server has been exceeded.
-.IP \fBNFSERR_STALE\fP:
-The "fhandle" given in the arguments was invalid.
-That is, the file referred to by that file handle no longer exists,
-or access to it has been revoked.
-.IP \fBNFSERR_WFLUSH\fP:
-The server's write cache used in the
-.I WRITECACHE
-call got flushed to disk.
-.LP
-.KS
-.NH 3
-\&ftype
-.IX "NFS data types" ftype "" \fIftype\fP
-.DS
-enum ftype {
- NFNON = 0,
- NFREG = 1,
- NFDIR = 2,
- NFBLK = 3,
- NFCHR = 4,
- NFLNK = 5
-};
-.DE
-.KE
-The enumeration
-.I ftype
-gives the type of a file. The type
-.I NFNON
-indicates a non-file,
-.I NFREG
-is a regular file,
-.I NFDIR
-is a directory,
-.I NFBLK
-is a block-special device,
-.I NFCHR
-is a character-special device, and
-.I NFLNK
-is a symbolic link.
-.KS
-.NH 3
-\&fhandle
-.IX "NFS data types" fhandle "" \fIfhandle\fP
-.DS
-typedef opaque fhandle[FHSIZE];
-.DE
-.KE
-The
-.I fhandle
-is the file handle passed between the server and the client.
-All file operations are done using file handles to refer to a file or
-directory. The file handle can contain whatever information the server
-needs to distinguish an individual file.
-.KS
-.NH 3
-\&timeval
-.IX "NFS data types" timeval "" \fItimeval\fP
-.DS
-struct timeval {
- unsigned int seconds;
- unsigned int useconds;
-};
-.DE
-.KE
-The
-.I timeval
-structure is the number of seconds and microseconds
-since midnight January 1, 1970, Greenwich Mean Time. It is used to
-pass time and date information.
-.KS
-.NH 3
-\&fattr
-.IX "NFS data types" fattr "" \fIfattr\fP
-.DS
-struct fattr {
- ftype type;
- unsigned int mode;
- unsigned int nlink;
- unsigned int uid;
- unsigned int gid;
- unsigned int size;
- unsigned int blocksize;
- unsigned int rdev;
- unsigned int blocks;
- unsigned int fsid;
- unsigned int fileid;
- timeval atime;
- timeval mtime;
- timeval ctime;
-};
-.DE
-.KE
-The
-.I fattr
-structure contains the attributes of a file; "type" is the type of
-the file; "nlink" is the number of hard links to the file (the number
-of different names for the same file); "uid" is the user
-identification number of the owner of the file; "gid" is the group
-identification number of the group of the file; "size" is the size in
-bytes of the file; "blocksize" is the size in bytes of a block of the
-file; "rdev" is the device number of the file if it is type
-.I NFCHR
-or
-.I NFBLK ;
-"blocks" is the number of blocks the file takes up on disk; "fsid" is
-the file system identifier for the filesystem containing the file;
-"fileid" is a number that uniquely identifies the file within its
-filesystem; "atime" is the time when the file was last accessed for
-either read or write; "mtime" is the time when the file data was last
-modified (written); and "ctime" is the time when the status of the
-file was last changed. Writing to the file also changes "ctime" if
-the size of the file changes.
-.LP
-"mode" is the access mode encoded as a set of bits. Notice that the
-file type is specified both in the mode bits and in the file type.
-This is really a bug in the protocol and will be fixed in future
-versions. The descriptions given below specify the bit positions
-using octal numbers.
-.TS
-box tab (&) ;
-cfI cfI
-lfL l .
-Bit&Description
-_
-0040000&This is a directory; "type" field should be NFDIR.
-0020000&This is a character special file; "type" field should be NFCHR.
-0060000&This is a block special file; "type" field should be NFBLK.
-0100000&This is a regular file; "type" field should be NFREG.
-0120000&This is a symbolic link file; "type" field should be NFLNK.
-0140000&This is a named socket; "type" field should be NFNON.
-0004000&Set user id on execution.
-0002000&Set group id on execution.
-0001000&Save swapped text even after use.
-0000400&Read permission for owner.
-0000200&Write permission for owner.
-0000100&Execute and search permission for owner.
-0000040&Read permission for group.
-0000020&Write permission for group.
-0000010&Execute and search permission for group.
-0000004&Read permission for others.
-0000002&Write permission for others.
-0000001&Execute and search permission for others.
-.TE
-.KS
-Notes:
-.IP
-The bits are the same as the mode bits returned by the
-.I stat(2)
-system call in the UNIX system. The file type is specified both in
-the mode bits and in the file type. This is fixed in future
-versions.
-.IP
-The "rdev" field in the attributes structure is an operating system
-specific device specifier. It will be removed and generalized in
-the next revision of the protocol.
-.KE
-.LP
-.KS
-.NH 3
-\&sattr
-.IX "NFS data types" sattr "" \fIsattr\fP
-.DS
-struct sattr {
- unsigned int mode;
- unsigned int uid;
- unsigned int gid;
- unsigned int size;
- timeval atime;
- timeval mtime;
-};
-.DE
-.KE
-The
-.I sattr
-structure contains the file attributes which can be set
-from the client. The fields are the same as for
-.I fattr
-above. A "size" of zero means the file should be truncated.
-A value of -1 indicates a field that should be ignored.
-.LP
-.KS
-.NH 3
-\&filename
-.IX "NFS data types" filename "" \fIfilename\fP
-.DS
-typedef string filename<MAXNAMLEN>;
-.DE
-.KE
-The type
-.I filename
-is used for passing file names or pathname components.
-.LP
-.KS
-.NH 3
-\&path
-.IX "NFS data types" path "" \fIpath\fP
-.DS
-typedef string path<MAXPATHLEN>;
-.DE
-.KE
-The type
-.I path
-is a pathname. The server considers it as a string
-with no internal structure, but to the client it is the name of a
-node in a filesystem tree.
-.LP
-.KS
-.NH 3
-\&attrstat
-.IX "NFS data types" attrstat "" \fIattrstat\fP
-.DS
-union attrstat switch (stat status) {
- case NFS_OK:
- fattr attributes;
- default:
- void;
-};
-.DE
-.KE
-The
-.I attrstat
-structure is a common procedure result. It contains
-a "status" and, if the call succeeded, it also contains the
-attributes of the file on which the operation was done.
-.LP
-.KS
-.NH 3
-\&diropargs
-.IX "NFS data types" diropargs "" \fIdiropargs\fP
-.DS
-struct diropargs {
- fhandle dir;
- filename name;
-};
-.DE
-.KE
-The
-.I diropargs
-structure is used in directory operations. The
-"fhandle" "dir" is the directory in which to find the file "name".
-A directory operation is one in which the directory is affected.
-.LP
-.KS
-.NH 3
-\&diropres
-.IX "NFS data types" diropres "" \fIdiropres\fP
-.DS
-union diropres switch (stat status) {
- case NFS_OK:
- struct {
- fhandle file;
- fattr attributes;
- } diropok;
- default:
- void;
-};
-.DE
-.KE
-The results of a directory operation are returned in a
-.I diropres
-structure. If the call succeeded, a new file handle "file" and the
-"attributes" associated with that file are returned along with the
-"status".
-.NH 2
-\&Server Procedures
-.IX "NFS server procedures" "" "" "" PAGE MAJOR
-.LP
-The protocol definition is given as a set of procedures with
-arguments and results defined using the RPC language. A brief
-description of the function of each procedure should provide enough
-information to allow implementation.
-.LP
-All of the procedures in the NFS protocol are assumed to be
-synchronous. When a procedure returns to the client, the client
-can assume that the operation has completed and any data associated
-with the request is now on stable storage. For example, a client
-.I WRITE
-request may cause the server to update data blocks,
-filesystem information blocks (such as indirect blocks), and file
-attribute information (size and modify times). When the
-.I WRITE
-returns to the client, it can assume that the write is safe, even
-in case of a server crash, and it can discard the data written.
-This is a very important part of the statelessness of the server.
-If the server waited to flush data from remote requests, the client
-would have to save those requests so that it could resend them in
-case of a server crash.
-.ie t .DS
-.el .DS L
-
-.ft I
-/*
-* Remote file service routines
-*/
-.ft CW
-program NFS_PROGRAM {
- version NFS_VERSION {
- void NFSPROC_NULL(void) = 0;
- attrstat NFSPROC_GETATTR(fhandle) = 1;
- attrstat NFSPROC_SETATTR(sattrargs) = 2;
- void NFSPROC_ROOT(void) = 3;
- diropres NFSPROC_LOOKUP(diropargs) = 4;
- readlinkres NFSPROC_READLINK(fhandle) = 5;
- readres NFSPROC_READ(readargs) = 6;
- void NFSPROC_WRITECACHE(void) = 7;
- attrstat NFSPROC_WRITE(writeargs) = 8;
- diropres NFSPROC_CREATE(createargs) = 9;
- stat NFSPROC_REMOVE(diropargs) = 10;
- stat NFSPROC_RENAME(renameargs) = 11;
- stat NFSPROC_LINK(linkargs) = 12;
- stat NFSPROC_SYMLINK(symlinkargs) = 13;
- diropres NFSPROC_MKDIR(createargs) = 14;
- stat NFSPROC_RMDIR(diropargs) = 15;
- readdirres NFSPROC_READDIR(readdirargs) = 16;
- statfsres NFSPROC_STATFS(fhandle) = 17;
- } = 2;
-} = 100003;
-.DE
-.KS
-.NH 3
-\&Do Nothing
-.IX "NFS server procedures" NFSPROC_NULL() "" \fINFSPROC_NULL()\fP
-.DS
-void
-NFSPROC_NULL(void) = 0;
-.DE
-.KE
-This procedure does no work. It is made available in all RPC
-services to allow server response testing and timing.
-.KS
-.NH 3
-\&Get File Attributes
-.IX "NFS server procedures" NFSPROC_GETATTR() "" \fINFSPROC_GETATTR()\fP
-.DS
-attrstat
-NFSPROC_GETATTR (fhandle) = 1;
-.DE
-.KE
-If the reply status is
-.I NFS_OK ,
-then the reply attributes contains
-the attributes for the file given by the input fhandle.
-.KS
-.NH 3
-\&Set File Attributes
-.IX "NFS server procedures" NFSPROC_SETATTR() "" \fINFSPROC_SETATTR()\fP
-.DS
-struct sattrargs {
- fhandle file;
- sattr attributes;
- };
-
-attrstat
-NFSPROC_SETATTR (sattrargs) = 2;
-.DE
-.KE
-The "attributes" argument contains fields which are either -1 or
-are the new value for the attributes of "file". If the reply
-status is
-.I NFS_OK ,
-then the reply attributes have the attributes of
-the file after the "SETATTR" operation has completed.
-.LP
-Note: The use of -1 to indicate an unused field in "attributes" is
-changed in the next version of the protocol.
-.KS
-.NH 3
-\&Get Filesystem Root
-.IX "NFS server procedures" NFSPROC_ROOT "" \fINFSPROC_ROOT\fP
-.DS
-void
-NFSPROC_ROOT(void) = 3;
-.DE
-.KE
-Obsolete. This procedure is no longer used because finding the
-root file handle of a filesystem requires moving pathnames between
-client and server. To do this right we would have to define a
-network standard representation of pathnames. Instead, the
-function of looking up the root file handle is done by the
-.I MNTPROC_MNT()
-procedure. (See the
-.I "Mount Protocol Definition"
-later in this chapter for details).
-.KS
-.NH 3
-\&Look Up File Name
-.IX "NFS server procedures" NFSPROC_LOOKUP() "" \fINFSPROC_LOOKUP()\fP
-.DS
-diropres
-NFSPROC_LOOKUP(diropargs) = 4;
-.DE
-.KE
-If the reply "status" is
-.I NFS_OK ,
-then the reply "file" and reply
-"attributes" are the file handle and attributes for the file "name"
-in the directory given by "dir" in the argument.
-.KS
-.NH 3
-\&Read From Symbolic Link
-.IX "NFS server procedures" NFSPROC_READLINK() "" \fINFSPROC_READLINK()\fP
-.DS
-union readlinkres switch (stat status) {
- case NFS_OK:
- path data;
- default:
- void;
-};
-
-readlinkres
-NFSPROC_READLINK(fhandle) = 5;
-.DE
-.KE
-If "status" has the value
-.I NFS_OK ,
-then the reply "data" is the data in
-the symbolic link given by the file referred to by the fhandle argument.
-.LP
-Note: since NFS always parses pathnames on the client, the
-pathname in a symbolic link may mean something different (or be
-meaningless) on a different client or on the server if a different
-pathname syntax is used.
-.KS
-.NH 3
-\&Read From File
-.IX "NFS server procedures" NFSPROC_READ "" \fINFSPROC_READ\fP
-.DS
-struct readargs {
- fhandle file;
- unsigned offset;
- unsigned count;
- unsigned totalcount;
-};
-
-union readres switch (stat status) {
- case NFS_OK:
- fattr attributes;
- opaque data<NFS_MAXDATA>;
- default:
- void;
-};
-
-readres
-NFSPROC_READ(readargs) = 6;
-.DE
-.KE
-Returns up to "count" bytes of "data" from the file given by
-"file", starting at "offset" bytes from the beginning of the file.
-The first byte of the file is at offset zero. The file attributes
-after the read takes place are returned in "attributes".
-.LP
-Note: The argument "totalcount" is unused, and is removed in the
-next protocol revision.
-.KS
-.NH 3
-\&Write to Cache
-.IX "NFS server procedures" NFSPROC_WRITECACHE() "" \fINFSPROC_WRITECACHE()\fP
-.DS
-void
-NFSPROC_WRITECACHE(void) = 7;
-.DE
-.KE
-To be used in the next protocol revision.
-.KS
-.NH 3
-\&Write to File
-.IX "NFS server procedures" NFSPROC_WRITE() "" \fINFSPROC_WRITE()\fP
-.DS
-struct writeargs {
- fhandle file;
- unsigned beginoffset;
- unsigned offset;
- unsigned totalcount;
- opaque data<NFS_MAXDATA>;
-};
-
-attrstat
-NFSPROC_WRITE(writeargs) = 8;
-.DE
-.KE
-Writes "data" beginning "offset" bytes from the beginning of
-"file". The first byte of the file is at offset zero. If the
-reply "status" is NFS_OK, then the reply "attributes" contains the
-attributes of the file after the write has completed. The write
-operation is atomic. Data from this call to
-.I WRITE
-will not be mixed with data from another client's calls.
-.LP
-Note: The arguments "beginoffset" and "totalcount" are ignored and
-are removed in the next protocol revision.
-.KS
-.NH 3
-\&Create File
-.IX "NFS server procedures" NFSPROC_CREATE() "" \fINFSPROC_CREATE()\fP
-.DS
-struct createargs {
- diropargs where;
- sattr attributes;
-};
-
-diropres
-NFSPROC_CREATE(createargs) = 9;
-.DE
-.KE
-The file "name" is created in the directory given by "dir". The
-initial attributes of the new file are given by "attributes". A
-reply "status" of NFS_OK indicates that the file was created, and
-reply "file" and reply "attributes" are its file handle and
-attributes. Any other reply "status" means that the operation
-failed and no file was created.
-.LP
-Note: This routine should pass an exclusive create flag, meaning
-"create the file only if it is not already there".
-.KS
-.NH 3
-\&Remove File
-.IX "NFS server procedures" NFSPROC_REMOVE() "" \fINFSPROC_REMOVE()\fP
-.DS
-stat
-NFSPROC_REMOVE(diropargs) = 10;
-.DE
-.KE
-The file "name" is removed from the directory given by "dir". A
-reply of NFS_OK means the directory entry was removed.
-.LP
-Note: possibly non-idempotent operation.
-.KS
-.NH 3
-\&Rename File
-.IX "NFS server procedures" NFSPROC_RENAME() "" \fINFSPROC_RENAME()\fP
-.DS
-struct renameargs {
- diropargs from;
- diropargs to;
-};
-
-stat
-NFSPROC_RENAME(renameargs) = 11;
-.DE
-.KE
-The existing file "from.name" in the directory given by "from.dir"
-is renamed to "to.name" in the directory given by "to.dir". If the
-reply is
-.I NFS_OK ,
-the file was renamed. The
-RENAME
-operation is
-atomic on the server; it cannot be interrupted in the middle.
-.LP
-Note: possibly non-idempotent operation.
-.KS
-.NH 3
-\&Create Link to File
-.IX "NFS server procedures" NFSPROC_LINK() "" \fINFSPROC_LINK()\fP
-.DS
-struct linkargs {
- fhandle from;
- diropargs to;
-};
-
-stat
-NFSPROC_LINK(linkargs) = 12;
-.DE
-.KE
-Creates the file "to.name" in the directory given by "to.dir",
-which is a hard link to the existing file given by "from". If the
-return value is
-.I NFS_OK ,
-a link was created. Any other return value
-indicates an error, and the link was not created.
-.LP
-A hard link should have the property that changes to either of the
-linked files are reflected in both files. When a hard link is made
-to a file, the attributes for the file should have a value for
-"nlink" that is one greater than the value before the link.
-.LP
-Note: possibly non-idempotent operation.
-.KS
-.NH 3
-\&Create Symbolic Link
-.IX "NFS server procedures" NFSPROC_SYMLINK() "" \fINFSPROC_SYMLINK()\fP
-.DS
-struct symlinkargs {
- diropargs from;
- path to;
- sattr attributes;
-};
-
-stat
-NFSPROC_SYMLINK(symlinkargs) = 13;
-.DE
-.KE
-Creates the file "from.name" with ftype
-.I NFLNK
-in the directory
-given by "from.dir". The new file contains the pathname "to" and
-has initial attributes given by "attributes". If the return value
-is
-.I NFS_OK ,
-a link was created. Any other return value indicates an
-error, and the link was not created.
-.LP
-A symbolic link is a pointer to another file. The name given in
-"to" is not interpreted by the server, only stored in the newly
-created file. When the client references a file that is a symbolic
-link, the contents of the symbolic link are normally transparently
-reinterpreted as a pathname to substitute. A
-.I READLINK
-operation returns the data to the client for interpretation.
-.LP
-Note: On UNIX servers the attributes are never used, since
-symbolic links always have mode 0777.
-.KS
-.NH 3
-\&Create Directory
-.IX "NFS server procedures" NFSPROC_MKDIR() "" \fINFSPROC_MKDIR()\fP
-.DS
-diropres
-NFSPROC_MKDIR (createargs) = 14;
-.DE
-.KE
-The new directory "where.name" is created in the directory given by
-"where.dir". The initial attributes of the new directory are given
-by "attributes". A reply "status" of NFS_OK indicates that the new
-directory was created, and reply "file" and reply "attributes" are
-its file handle and attributes. Any other reply "status" means
-that the operation failed and no directory was created.
-.LP
-Note: possibly non-idempotent operation.
-.KS
-.NH 3
-\&Remove Directory
-.IX "NFS server procedures" NFSPROC_RMDIR() "" \fINFSPROC_RMDIR()\fP
-.DS
-stat
-NFSPROC_RMDIR(diropargs) = 15;
-.DE
-.KE
-The existing empty directory "name" in the directory given by "dir"
-is removed. If the reply is
-.I NFS_OK ,
-the directory was removed.
-.LP
-Note: possibly non-idempotent operation.
-.KS
-.NH 3
-\&Read From Directory
-.IX "NFS server procedures" NFSPROC_READDIR() "" \fINFSPROC_READDIR()\fP
-.DS
-struct readdirargs {
- fhandle dir;
- nfscookie cookie;
- unsigned count;
-};
-
-struct entry {
- unsigned fileid;
- filename name;
- nfscookie cookie;
- entry *nextentry;
-};
-
-union readdirres switch (stat status) {
- case NFS_OK:
- struct {
- entry *entries;
- bool eof;
- } readdirok;
- default:
- void;
-};
-
-readdirres
-NFSPROC_READDIR (readdirargs) = 16;
-.DE
-.KE
-Returns a variable number of directory entries, with a total size
-of up to "count" bytes, from the directory given by "dir". If the
-returned value of "status" is
-.I NFS_OK ,
-then it is followed by a
-variable number of "entry"s. Each "entry" contains a "fileid"
-which consists of a unique number to identify the file within a
-filesystem, the "name" of the file, and a "cookie" which is an
-opaque pointer to the next entry in the directory. The cookie is
-used in the next
-.I READDIR
-call to get more entries starting at a
-given point in the directory. The special cookie zero (all bits
-zero) can be used to get the entries starting at the beginning of
-the directory. The "fileid" field should be the same number as the
-"fileid" in the the attributes of the file. (See the
-.I "Basic Data Types"
-section.)
-The "eof" flag has a value of
-.I TRUE
-if there are no more entries in the directory.
-.KS
-.NH 3
-\&Get Filesystem Attributes
-.IX "NFS server procedures" NFSPROC_STATFS() "" \fINFSPROC_STATFS()\fP
-.DS
-union statfsres (stat status) {
- case NFS_OK:
- struct {
- unsigned tsize;
- unsigned bsize;
- unsigned blocks;
- unsigned bfree;
- unsigned bavail;
- } info;
- default:
- void;
-};
-
-statfsres
-NFSPROC_STATFS(fhandle) = 17;
-.DE
-.KE
-If the reply "status" is
-.I NFS_OK ,
-then the reply "info" gives the
-attributes for the filesystem that contains file referred to by the
-input fhandle. The attribute fields contain the following values:
-.IP tsize:
-The optimum transfer size of the server in bytes. This is
-the number of bytes the server would like to have in the
-data part of READ and WRITE requests.
-.IP bsize:
-The block size in bytes of the filesystem.
-.IP blocks:
-The total number of "bsize" blocks on the filesystem.
-.IP bfree:
-The number of free "bsize" blocks on the filesystem.
-.IP bavail:
-The number of "bsize" blocks available to non-privileged users.
-.LP
-Note: This call does not work well if a filesystem has variable
-size blocks.
-.NH 1
-\&NFS Implementation Issues
-.IX NFS implementation
-.LP
-The NFS protocol is designed to be operating system independent, but
-since this version was designed in a UNIX environment, many
-operations have semantics similar to the operations of the UNIX file
-system. This section discusses some of the implementation-specific
-semantic issues.
-.NH 2
-\&Server/Client Relationship
-.IX NFS "server/client relationship"
-.LP
-The NFS protocol is designed to allow servers to be as simple and
-general as possible. Sometimes the simplicity of the server can be a
-problem, if the client wants to implement complicated filesystem
-semantics.
-.LP
-For example, some operating systems allow removal of open files. A
-process can open a file and, while it is open, remove it from the
-directory. The file can be read and written as long as the process
-keeps it open, even though the file has no name in the filesystem.
-It is impossible for a stateless server to implement these semantics.
-The client can do some tricks such as renaming the file on remove,
-and only removing it on close. We believe that the server provides
-enough functionality to implement most file system semantics on the
-client.
-.LP
-Every NFS client can also potentially be a server, and remote and
-local mounted filesystems can be freely intermixed. This leads to
-some interesting problems when a client travels down the directory
-tree of a remote filesystem and reaches the mount point on the server
-for another remote filesystem. Allowing the server to follow the
-second remote mount would require loop detection, server lookup, and
-user revalidation. Instead, we decided not to let clients cross a
-server's mount point. When a client does a LOOKUP on a directory on
-which the server has mounted a filesystem, the client sees the
-underlying directory instead of the mounted directory. A client can
-do remote mounts that match the server's mount points to maintain the
-server's view.
-.LP
-.NH 2
-\&Pathname Interpretation
-.IX NFS "pathname interpretation"
-.LP
-There are a few complications to the rule that pathnames are always
-parsed on the client. For example, symbolic links could have
-different interpretations on different clients. Another common
-problem for non-UNIX implementations is the special interpretation of
-the pathname ".." to mean the parent of a given directory. The next
-revision of the protocol uses an explicit flag to indicate the parent
-instead.
-.NH 2
-\&Permission Issues
-.IX NFS "permission issues"
-.LP
-The NFS protocol, strictly speaking, does not define the permission
-checking used by servers. However, it is expected that a server
-will do normal operating system permission checking using
-.I AUTH_UNIX
-style authentication as the basis of its protection mechanism. The
-server gets the client's effective "uid", effective "gid", and groups
-on each call and uses them to check permission. There are various
-problems with this method that can been resolved in interesting ways.
-.LP
-Using "uid" and "gid" implies that the client and server share the
-same "uid" list. Every server and client pair must have the same
-mapping from user to "uid" and from group to "gid". Since every
-client can also be a server, this tends to imply that the whole
-network shares the same "uid/gid" space.
-.I AUTH_DES
-(and the next
-revision of the NFS protocol) uses string names instead of numbers,
-but there are still complex problems to be solved.
-.LP
-Another problem arises due to the usually stateful open operation.
-Most operating systems check permission at open time, and then check
-that the file is open on each read and write request. With stateless
-servers, the server has no idea that the file is open and must do
-permission checking on each read and write call. On a local
-filesystem, a user can open a file and then change the permissions so
-that no one is allowed to touch it, but will still be able to write
-to the file because it is open. On a remote filesystem, by contrast,
-the write would fail. To get around this problem, the server's
-permission checking algorithm should allow the owner of a file to
-access it regardless of the permission setting.
-.LP
-A similar problem has to do with paging in from a file over the
-network. The operating system usually checks for execute permission
-before opening a file for demand paging, and then reads blocks from
-the open file. The file may not have read permission, but after it
-is opened it doesn't matter. An NFS server can not tell the
-difference between a normal file read and a demand page-in read. To
-make this work, the server allows reading of files if the "uid" given
-in the call has execute or read permission on the file.
-.LP
-In most operating systems, a particular user (on the user ID zero)
-has access to all files no matter what permission and ownership they
-have. This "super-user" permission may not be allowed on the server,
-since anyone who can become super-user on their workstation could
-gain access to all remote files. The UNIX server by default maps
-user id 0 to -2 before doing its access checking. This works except
-for NFS root filesystems, where super-user access cannot be avoided.
-.NH 2
-\&Setting RPC Parameters
-.IX NFS "setting RPC parameters"
-.LP
-Various file system parameters and options should be set at mount
-time. The mount protocol is described in the appendix below. For
-example, "Soft" mounts as well as "Hard" mounts are usually both
-provided. Soft mounted file systems return errors when RPC
-operations fail (after a given number of optional retransmissions),
-while hard mounted file systems continue to retransmit forever.
-Clients and servers may need to keep caches of recent operations to
-help avoid problems with non-idempotent operations.
-.NH 1
-\&Mount Protocol Definition
-.IX "mount protocol" "" "" "" PAGE MAJOR
-.sp 1
-.NH 2
-\&Introduction
-.IX "mount protocol" introduction
-.LP
-The mount protocol is separate from, but related to, the NFS
-protocol. It provides operating system specific services to get the
-NFS off the ground -- looking up server path names, validating user
-identity, and checking access permissions. Clients use the mount
-protocol to get the first file handle, which allows them entry into a
-remote filesystem.
-.LP
-The mount protocol is kept separate from the NFS protocol to make it
-easy to plug in new access checking and validation methods without
-changing the NFS server protocol.
-.LP
-Notice that the protocol definition implies stateful servers because
-the server maintains a list of client's mount requests. The mount
-list information is not critical for the correct functioning of
-either the client or the server. It is intended for advisory use
-only, for example, to warn possible clients when a server is going
-down.
-.LP
-Version one of the mount protocol is used with version two of the NFS
-protocol. The only connecting point is the
-.I fhandle
-structure, which is the same for both protocols.
-.NH 2
-\&RPC Information
-.IX "mount protocol" "RPC information"
-.IP \fIAuthentication\fP
-The mount service uses
-.I AUTH_UNIX
-and
-.I AUTH_DES
-style authentication only.
-.IP "\fITransport Protocols\fP"
-The mount service is currently supported on UDP/IP only.
-.IP "\fIPort Number\fP"
-Consult the server's portmapper, described in the chapter
-.I "Remote Procedure Calls: Protocol Specification",
-to find the port number on which the mount service is registered.
-.NH 2
-\&Sizes of XDR Structures
-.IX "mount protocol" "XDR structure sizes"
-.LP
-These are the sizes, given in decimal bytes, of various XDR
-structures used in the protocol:
-.DS
-/* \fIThe maximum number of bytes in a pathname argument\fP */
-const MNTPATHLEN = 1024;
-
-/* \fIThe maximum number of bytes in a name argument\fP */
-const MNTNAMLEN = 255;
-
-/* \fIThe size in bytes of the opaque file handle\fP */
-const FHSIZE = 32;
-.DE
-.NH 2
-\&Basic Data Types
-.IX "mount protocol" "basic data types"
-.IX "mount data types"
-.LP
-This section presents the data types used by the mount protocol.
-In many cases they are similar to the types used in NFS.
-.KS
-.NH 3
-\&fhandle
-.IX "mount data types" fhandle "" \fIfhandle\fP
-.DS
-typedef opaque fhandle[FHSIZE];
-.DE
-.KE
-The type
-.I fhandle
-is the file handle that the server passes to the
-client. All file operations are done using file handles to refer
-to a file or directory. The file handle can contain whatever
-information the server needs to distinguish an individual file.
-.LP
-This is the same as the "fhandle" XDR definition in version 2 of
-the NFS protocol; see
-.I "Basic Data Types"
-in the definition of the NFS protocol, above.
-.KS
-.NH 3
-\&fhstatus
-.IX "mount data types" fhstatus "" \fIfhstatus\fP
-.DS
-union fhstatus switch (unsigned status) {
- case 0:
- fhandle directory;
- default:
- void;
-};
-.DE
-.KE
-The type
-.I fhstatus
-is a union. If a "status" of zero is returned,
-the call completed successfully, and a file handle for the
-"directory" follows. A non-zero status indicates some sort of
-error. In this case the status is a UNIX error number.
-.KS
-.NH 3
-\&dirpath
-.IX "mount data types" dirpath "" \fIdirpath\fP
-.DS
-typedef string dirpath<MNTPATHLEN>;
-.DE
-.KE
-The type
-.I dirpath
-is a server pathname of a directory.
-.KS
-.NH 3
-\&name
-.IX "mount data types" name "" \fIname\fP
-.DS
-typedef string name<MNTNAMLEN>;
-.DE
-.KE
-The type
-.I name
-is an arbitrary string used for various names.
-.NH 2
-\&Server Procedures
-.IX "mount server procedures"
-.LP
-The following sections define the RPC procedures supplied by a
-mount server.
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* Protocol description for the mount program
-*/
-.ft CW
-
-program MOUNTPROG {
-.ft I
-/*
-* Version 1 of the mount protocol used with
-* version 2 of the NFS protocol.
-*/
-.ft CW
- version MOUNTVERS {
- void MOUNTPROC_NULL(void) = 0;
- fhstatus MOUNTPROC_MNT(dirpath) = 1;
- mountlist MOUNTPROC_DUMP(void) = 2;
- void MOUNTPROC_UMNT(dirpath) = 3;
- void MOUNTPROC_UMNTALL(void) = 4;
- exportlist MOUNTPROC_EXPORT(void) = 5;
- } = 1;
-} = 100005;
-.DE
-.KS
-.NH 3
-\&Do Nothing
-.IX "mount server procedures" MNTPROC_NULL() "" \fIMNTPROC_NULL()\fP
-.DS
-void
-MNTPROC_NULL(void) = 0;
-.DE
-.KE
-This procedure does no work. It is made available in all RPC
-services to allow server response testing and timing.
-.KS
-.NH 3
-\&Add Mount Entry
-.IX "mount server procedures" MNTPROC_MNT() "" \fIMNTPROC_MNT()\fP
-.DS
-fhstatus
-MNTPROC_MNT(dirpath) = 1;
-.DE
-.KE
-If the reply "status" is 0, then the reply "directory" contains the
-file handle for the directory "dirname". This file handle may be
-used in the NFS protocol. This procedure also adds a new entry to
-the mount list for this client mounting "dirname".
-.KS
-.NH 3
-\&Return Mount Entries
-.IX "mount server procedures" MNTPROC_DUMP() "" \fIMNTPROC_DUMP()\fP
-.DS
-struct *mountlist {
- name hostname;
- dirpath directory;
- mountlist nextentry;
-};
-
-mountlist
-MNTPROC_DUMP(void) = 2;
-.DE
-.KE
-Returns the list of remote mounted filesystems. The "mountlist"
-contains one entry for each "hostname" and "directory" pair.
-.KS
-.NH 3
-\&Remove Mount Entry
-.IX "mount server procedures" MNTPROC_UMNT() "" \fIMNTPROC_UMNT()\fP
-.DS
-void
-MNTPROC_UMNT(dirpath) = 3;
-.DE
-.KE
-Removes the mount list entry for the input "dirpath".
-.KS
-.NH 3
-\&Remove All Mount Entries
-.IX "mount server procedures" MNTPROC_UMNTALL() "" \fIMNTPROC_UMNTALL()\fP
-.DS
-void
-MNTPROC_UMNTALL(void) = 4;
-.DE
-.KE
-Removes all of the mount list entries for this client.
-.KS
-.NH 3
-\&Return Export List
-.IX "mount server procedures" MNTPROC_EXPORT() "" \fIMNTPROC_EXPORT()\fP
-.DS
-struct *groups {
- name grname;
- groups grnext;
-};
-
-struct *exportlist {
- dirpath filesys;
- groups groups;
- exportlist next;
-};
-
-exportlist
-MNTPROC_EXPORT(void) = 5;
-.DE
-.KE
-Returns a variable number of export list entries. Each entry
-contains a filesystem name and a list of groups that are allowed to
-import it. The filesystem name is in "filesys", and the group name
-is in the list "groups".
-.LP
-Note: The exportlist should contain
-more information about the status of the filesystem, such as a
-read-only flag.
diff --git a/cpukit/librpc/src/rpc/PSD.doc/rpc.prog.ms b/cpukit/librpc/src/rpc/PSD.doc/rpc.prog.ms
deleted file mode 100644
index 3b02447fe8..0000000000
--- a/cpukit/librpc/src/rpc/PSD.doc/rpc.prog.ms
+++ /dev/null
@@ -1,2684 +0,0 @@
-.\"
-.\" Must use -- tbl and pic -- with this one
-.\"
-.\" @(#)rpc.prog.ms 2.3 88/08/11 4.0 RPCSRC
-.de BT
-.if \\n%=1 .tl ''- % -''
-..
-.IX "Network Programming" "" "" "" PAGE MAJOR
-.nr OF 0
-.ND
-.\" prevent excess underlining in nroff
-.if n .fp 2 R
-.OH 'Remote Procedure Call Programming Guide''Page %'
-.EH 'Page %''Remote Procedure Call Programming Guide'
-.SH
-\&Remote Procedure Call Programming Guide
-.nr OF 1
-.IX "RPC Programming Guide"
-.LP
-This document assumes a working knowledge of network theory. It is
-intended for programmers who wish to write network applications using
-remote procedure calls (explained below), and who want to understand
-the RPC mechanisms usually hidden by the
-.I rpcgen(1)
-protocol compiler.
-.I rpcgen
-is described in detail in the previous chapter, the
-.I "\fBrpcgen\fP \fIProgramming Guide\fP".
-.SH
-Note:
-.I
-.IX rpcgen "" \fIrpcgen\fP
-Before attempting to write a network application, or to convert an
-existing non-network application to run over the network, you may want to
-understand the material in this chapter. However, for most applications,
-you can circumvent the need to cope with the details presented here by using
-.I rpcgen .
-The
-.I "Generating XDR Routines"
-section of that chapter contains the complete source for a working RPC
-service\(ema remote directory listing service which uses
-.I rpcgen
-to generate XDR routines as well as client and server stubs.
-.LP
-.LP
-What are remote procedure calls? Simply put, they are the high-level
-communications paradigm used in the operating system.
-RPC presumes the existence of
-low-level networking mechanisms (such as TCP/IP and UDP/IP), and upon them
-it implements a logical client to server communications system designed
-specifically for the support of network applications. With RPC, the client
-makes a procedure call to send a data packet to the server. When the
-packet arrives, the server calls a dispatch routine, performs whatever
-service is requested, sends back the reply, and the procedure call returns
-to the client.
-.NH 0
-\&Layers of RPC
-.IX "layers of RPC"
-.IX "RPC" "layers"
-.LP
-The RPC interface can be seen as being divided into three layers.\**
-.FS
-For a complete specification of the routines in the remote procedure
-call Library, see the
-.I rpc(3N)
-manual page.
-.FE
-.LP
-.I "The Highest Layer:"
-.IX RPC "The Highest Layer"
-The highest layer is totally transparent to the operating system,
-machine and network upon which is is run. It's probably best to
-think of this level as a way of
-.I using
-RPC, rather than as
-a \fIpart of\fP RPC proper. Programmers who write RPC routines
-should (almost) always make this layer available to others by way
-of a simple C front end that entirely hides the networking.
-.LP
-To illustrate, at this level a program can simply make a call to
-.I rnusers (),
-a C routine which returns the number of users on a remote machine.
-The user is not explicitly aware of using RPC \(em they simply
-call a procedure, just as they would call
-.I malloc() .
-.LP
-.I "The Middle Layer:"
-.IX RPC "The Middle Layer"
-The middle layer is really \*QRPC proper.\*U Here, the user doesn't
-need to consider details about sockets, the UNIX system, or other low-level
-implementation mechanisms. They simply make remote procedure calls
-to routines on other machines. The selling point here is simplicity.
-It's this layer that allows RPC to pass the \*Qhello world\*U test \(em
-simple things should be simple. The middle-layer routines are used
-for most applications.
-.LP
-RPC calls are made with the system routines
-.I registerrpc()
-.I callrpc()
-and
-.I svc_run ().
-The first two of these are the most fundamental:
-.I registerrpc()
-obtains a unique system-wide procedure-identification number, and
-.I callrpc()
-actually executes a remote procedure call. At the middle level, a
-call to
-.I rnusers()
-is implemented by way of these two routines.
-.LP
-The middle layer is unfortunately rarely used in serious programming
-due to its inflexibility (simplicity). It does not allow timeout
-specifications or the choice of transport. It allows no UNIX
-process control or flexibility in case of errors. It doesn't support
-multiple kinds of call authentication. The programmer rarely needs
-all these kinds of control, but one or two of them is often necessary.
-.LP
-.I "The Lowest Layer:"
-.IX RPC "The Lowest Layer"
-The lowest layer does allow these details to be controlled by the
-programmer, and for that reason it is often necessary. Programs
-written at this level are also most efficient, but this is rarely a
-real issue \(em since RPC clients and servers rarely generate
-heavy network loads.
-.LP
-Although this document only discusses the interface to C,
-remote procedure calls can be made from any language.
-Even though this document discusses RPC
-when it is used to communicate
-between processes on different machines,
-it works just as well for communication
-between different processes on the same machine.
-.br
-.KS
-.NH 2
-\&The RPC Paradigm
-.IX RPC paradigm
-.LP
-Here is a diagram of the RPC paradigm:
-.LP
-\fBFigure 1-1\fI Network Communication with the Remote Reocedure Call\fR
-.LP
-.PS
-L1: arrow down 1i "client " rjust "program " rjust
-L2: line right 1.5i "\fIcallrpc\fP" "function"
-move up 1.5i; line dotted down 6i; move up 4.5i
-arrow right 1i
-L3: arrow down 1i "invoke " rjust "service " rjust
-L4: arrow right 1.5i "call" "service"
-L5: arrow down 1i " service" ljust " executes" ljust
-L6: arrow left 1.5i "\fIreturn\fP" "answer"
-L7: arrow down 1i "request " rjust "completed " rjust
-L8: line left 1i
-arrow left 1.5i "\fIreturn\fP" "reply"
-L9: arrow down 1i "program " rjust "continues " rjust
-line dashed down from L2 to L9
-line dashed down from L4 to L7
-line dashed up 1i from L3 "service " rjust "daemon " rjust
-arrow dashed down 1i from L8
-move right 1i from L3
-box invis "Machine B"
-move left 1.2i from L2; move down
-box invis "Machine A"
-.PE
-.KE
-.KS
-.NH 1
-\&Higher Layers of RPC
-.NH 2
-\&Highest Layer
-.IX "highest layer of RPC"
-.IX RPC "highest layer"
-.LP
-Imagine you're writing a program that needs to know
-how many users are logged into a remote machine.
-You can do this by calling the RPC library routine
-.I rnusers()
-as illustrated below:
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-
-main(argc, argv)
- int argc;
- char **argv;
-{
- int num;
-
- if (argc != 2) {
- fprintf(stderr, "usage: rnusers hostname\en");
- exit(1);
- }
- if ((num = rnusers(argv[1])) < 0) {
- fprintf(stderr, "error: rnusers\en");
- exit(-1);
- }
- printf("%d users on %s\en", num, argv[1]);
- exit(0);
-}
-.DE
-.KE
-RPC library routines such as
-.I rnusers()
-are in the RPC services library
-.I librpcsvc.a
-Thus, the program above should be compiled with
-.DS
-.ft CW
-% cc \fIprogram.c -lrpcsvc\fP
-.DE
-.I rnusers (),
-like the other RPC library routines, is documented in section 3R
-of the
-.I "System Interface Manual for the Sun Workstation" ,
-the same section which documents the standard Sun RPC services.
-.IX "RPC Services"
-See the
-.I intro(3R)
-manual page for an explanation of the documentation strategy
-for these services and their RPC protocols.
-.LP
-Here are some of the RPC service library routines available to the
-C programmer:
-.LP
-\fBTable 3-3\fI RPC Service Library Routines\RP
-.TS
-box tab (&) ;
-cfI cfI
-lfL l .
-Routine&Description
-_
-.sp.5
-rnusers&Return number of users on remote machine
-rusers&Return information about users on remote machine
-havedisk&Determine if remote machine has disk
-rstats&Get performance data from remote kernel
-rwall&Write to specified remote machines
-yppasswd&Update user password in Yellow Pages
-.TE
-.LP
-Other RPC services \(em for example
-.I ether()
-.I mount
-.I rquota()
-and
-.I spray
-\(em are not available to the C programmer as library routines.
-They do, however,
-have RPC program numbers so they can be invoked with
-.I callrpc()
-which will be discussed in the next section. Most of them also
-have compilable
-.I rpcgen(1)
-protocol description files. (The
-.I rpcgen
-protocol compiler radically simplifies the process of developing
-network applications.
-See the \fBrpcgen\fI Programming Guide\fR
-for detailed information about
-.I rpcgen
-and
-.I rpcgen
-protocol description files).
-.KS
-.NH 2
-\&Intermediate Layer
-.IX "intermediate layer of RPC"
-.IX "RPC" "intermediate layer"
-.LP
-The simplest interface, which explicitly makes RPC calls, uses the
-functions
-.I callrpc()
-and
-.I registerrpc()
-Using this method, the number of remote users can be gotten as follows:
-.ie t .DS
-.el .DS L
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <utmp.h>
-#include <rpcsvc/rusers.h>
-
-main(argc, argv)
- int argc;
- char **argv;
-{
- unsigned long nusers;
- int stat;
-
- if (argc != 2) {
- fprintf(stderr, "usage: nusers hostname\en");
- exit(-1);
- }
- if (stat = callrpc(argv[1],
- RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
- xdr_void, 0, xdr_u_long, &nusers) != 0) {
- clnt_perrno(stat);
- exit(1);
- }
- printf("%d users on %s\en", nusers, argv[1]);
- exit(0);
-}
-.DE
-.KE
-Each RPC procedure is uniquely defined by a program number,
-version number, and procedure number. The program number
-specifies a group of related remote procedures, each of
-which has a different procedure number. Each program also
-has a version number, so when a minor change is made to a
-remote service (adding a new procedure, for example), a new
-program number doesn't have to be assigned. When you want
-to call a procedure to find the number of remote users, you
-look up the appropriate program, version and procedure numbers
-in a manual, just as you look up the name of a memory allocator
-when you want to allocate memory.
-.LP
-The simplest way of making remote procedure calls is with the the RPC
-library routine
-.I callrpc()
-It has eight parameters. The first is the name of the remote server
-machine. The next three parameters are the program, version, and procedure
-numbers\(emtogether they identify the procedure to be called.
-The fifth and sixth parameters are an XDR filter and an argument to
-be encoded and passed to the remote procedure.
-The final two parameters are a filter for decoding the results
-returned by the remote procedure and a pointer to the place where
-the procedure's results are to be stored. Multiple arguments and
-results are handled by embedding them in structures. If
-.I callrpc()
-completes successfully, it returns zero; else it returns a nonzero
-value. The return codes (of type
-.IX "enum clnt_stat (in RPC programming)" "" "\fIenum clnt_stat\fP (in RPC programming)"
-cast into an integer) are found in
-.I <rpc/clnt.h> .
-.LP
-Since data types may be represented differently on different machines,
-.I callrpc()
-needs both the type of the RPC argument, as well as
-a pointer to the argument itself (and similarly for the result). For
-.I RUSERSPROC_NUM ,
-the return value is an
-.I "unsigned long"
-so
-.I callrpc()
-has
-.I xdr_u_long()
-as its first return parameter, which says
-that the result is of type
-.I "unsigned long"
-and
-.I &nusers
-as its second return parameter,
-which is a pointer to where the long result will be placed. Since
-.I RUSERSPROC_NUM
-takes no argument, the argument parameter of
-.I callrpc()
-is
-.I xdr_void ().
-.LP
-After trying several times to deliver a message, if
-.I callrpc()
-gets no answer, it returns with an error code.
-The delivery mechanism is UDP,
-which stands for User Datagram Protocol.
-Methods for adjusting the number of retries
-or for using a different protocol require you to use the lower
-layer of the RPC library, discussed later in this document.
-The remote server procedure
-corresponding to the above might look like this:
-.ie t .DS
-.el .DS L
-.ft CW
-.ft CW
-char *
-nuser(indata)
- char *indata;
-{
- unsigned long nusers;
-
-.ft I
- /*
- * Code here to compute the number of users
- * and place result in variable \fInusers\fP.
- */
-.ft CW
- return((char *)&nusers);
-}
-.DE
-.LP
-It takes one argument, which is a pointer to the input
-of the remote procedure call (ignored in our example),
-and it returns a pointer to the result.
-In the current version of C,
-character pointers are the generic pointers,
-so both the input argument and the return value are cast to
-.I "char *" .
-.LP
-Normally, a server registers all of the RPC calls it plans
-to handle, and then goes into an infinite loop waiting to service requests.
-In this example, there is only a single procedure
-to register, so the main body of the server would look like this:
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <utmp.h>
-#include <rpcsvc/rusers.h>
-
-char *nuser();
-
-main()
-{
- registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
- nuser, xdr_void, xdr_u_long);
- svc_run(); /* \fINever returns\fP */
- fprintf(stderr, "Error: svc_run returned!\en");
- exit(1);
-}
-.DE
-.LP
-The
-.I registerrpc()
-routine registers a C procedure as corresponding to a
-given RPC procedure number. The first three parameters,
-.I RUSERPROG ,
-.I RUSERSVERS ,
-and
-.I RUSERSPROC_NUM
-are the program, version, and procedure numbers
-of the remote procedure to be registered;
-.I nuser()
-is the name of the local procedure that implements the remote
-procedure; and
-.I xdr_void()
-and
-.I xdr_u_long()
-are the XDR filters for the remote procedure's arguments and
-results, respectively. (Multiple arguments or multiple results
-are passed as structures).
-.LP
-Only the UDP transport mechanism can use
-.I registerrpc()
-thus, it is always safe in conjunction with calls generated by
-.I callrpc() .
-.SH
-.IX "UDP 8K warning"
-Warning: the UDP transport mechanism can only deal with
-arguments and results less than 8K bytes in length.
-.LP
-.LP
-After registering the local procedure, the server program's
-main procedure calls
-.I svc_run (),
-the RPC library's remote procedure dispatcher. It is this
-function that calls the remote procedures in response to RPC
-call messages. Note that the dispatcher takes care of decoding
-remote procedure arguments and encoding results, using the XDR
-filters specified when the remote procedure was registered.
-.NH 2
-\&Assigning Program Numbers
-.IX "program number assignment"
-.IX "assigning program numbers"
-.LP
-Program numbers are assigned in groups of
-.I 0x20000000
-according to the following chart:
-.DS
-.ft CW
- 0x0 - 0x1fffffff \fRDefined by Sun\fP
-0x20000000 - 0x3fffffff \fRDefined by user\fP
-0x40000000 - 0x5fffffff \fRTransient\fP
-0x60000000 - 0x7fffffff \fRReserved\fP
-0x80000000 - 0x9fffffff \fRReserved\fP
-0xa0000000 - 0xbfffffff \fRReserved\fP
-0xc0000000 - 0xdfffffff \fRReserved\fP
-0xe0000000 - 0xffffffff \fRReserved\fP
-.ft R
-.DE
-Sun Microsystems administers the first group of numbers, which
-should be identical for all Sun customers. If a customer
-develops an application that might be of general interest, that
-application should be given an assigned number in the first
-range. The second group of numbers is reserved for specific
-customer applications. This range is intended primarily for
-debugging new programs. The third group is reserved for
-applications that generate program numbers dynamically. The
-final groups are reserved for future use, and should not be
-used.
-.LP
-To register a protocol specification, send a request by network
-mail to
-.I rpc@sun
-or write to:
-.DS
-RPC Administrator
-Sun Microsystems
-2550 Garcia Ave.
-Mountain View, CA 94043
-.DE
-Please include a compilable
-.I rpcgen
-\*Q.x\*U file describing your protocol.
-You will be given a unique program number in return.
-.IX RPC administration
-.IX administration "of RPC"
-.LP
-The RPC program numbers and protocol specifications
-of standard Sun RPC services can be
-found in the include files in
-.I "/usr/include/rpcsvc" .
-These services, however, constitute only a small subset
-of those which have been registered. The complete list of
-registered programs, as of the time when this manual was
-printed, is:
-.LP
-\fBTable 3-2\fI RPC Registered Programs\fR
-.TS H
-box tab (&) ;
-lfBI lfBI lfBI
-lfL lfL lfI .
-RPC Number&Program&Description
-_
-.TH
-.sp.5
-100000&PMAPPROG&portmapper
-100001&RSTATPROG&remote stats
-100002&RUSERSPROG&remote users
-100003&NFSPROG&nfs
-100004&YPPROG&Yellow Pages
-100005&MOUNTPROG&mount demon
-100006&DBXPROG&remote dbx
-100007&YPBINDPROG&yp binder
-100008&WALLPROG&shutdown msg
-100009&YPPASSWDPROG&yppasswd server
-100010&ETHERSTATPROG&ether stats
-100011&RQUOTAPROG&disk quotas
-100012&SPRAYPROG&spray packets
-100013&IBM3270PROG&3270 mapper
-100014&IBMRJEPROG&RJE mapper
-100015&SELNSVCPROG&selection service
-100016&RDATABASEPROG&remote database access
-100017&REXECPROG&remote execution
-100018&ALICEPROG&Alice Office Automation
-100019&SCHEDPROG&scheduling service
-100020&LOCKPROG&local lock manager
-100021&NETLOCKPROG&network lock manager
-100022&X25PROG&x.25 inr protocol
-100023&STATMON1PROG&status monitor 1
-100024&STATMON2PROG&status monitor 2
-100025&SELNLIBPROG&selection library
-100026&BOOTPARAMPROG&boot parameters service
-100027&MAZEPROG&mazewars game
-100028&YPUPDATEPROG&yp update
-100029&KEYSERVEPROG&key server
-100030&SECURECMDPROG&secure login
-100031&NETFWDIPROG&nfs net forwarder init
-100032&NETFWDTPROG&nfs net forwarder trans
-100033&SUNLINKMAP_PROG&sunlink MAP
-100034&NETMONPROG&network monitor
-100035&DBASEPROG&lightweight database
-100036&PWDAUTHPROG&password authorization
-100037&TFSPROG&translucent file svc
-100038&NSEPROG&nse server
-100039&NSE_ACTIVATE_PROG&nse activate daemon
-.sp .2i
-150001&PCNFSDPROG&pc passwd authorization
-.sp .2i
-200000&PYRAMIDLOCKINGPROG&Pyramid-locking
-200001&PYRAMIDSYS5&Pyramid-sys5
-200002&CADDS_IMAGE&CV cadds_image
-.sp .2i
-300001&ADT_RFLOCKPROG&ADT file locking
-.TE
-.NH 2
-\&Passing Arbitrary Data Types
-.IX "arbitrary data types"
-.LP
-In the previous example, the RPC call passes a single
-.I "unsigned long"
-RPC can handle arbitrary data structures, regardless of
-different machines' byte orders or structure layout conventions,
-by always converting them to a network standard called
-.I "External Data Representation"
-(XDR) before
-sending them over the wire.
-The process of converting from a particular machine representation
-to XDR format is called
-.I serializing ,
-and the reverse process is called
-.I deserializing .
-The type field parameters of
-.I callrpc()
-and
-.I registerrpc()
-can be a built-in procedure like
-.I xdr_u_long()
-in the previous example, or a user supplied one.
-XDR has these built-in type routines:
-.IX RPC "built-in routines"
-.DS
-.ft CW
-xdr_int() xdr_u_int() xdr_enum()
-xdr_long() xdr_u_long() xdr_bool()
-xdr_short() xdr_u_short() xdr_wrapstring()
-xdr_char() xdr_u_char()
-.DE
-Note that the routine
-.I xdr_string()
-exists, but cannot be used with
-.I callrpc()
-and
-.I registerrpc (),
-which only pass two parameters to their XDR routines.
-.I xdr_wrapstring()
-has only two parameters, and is thus OK. It calls
-.I xdr_string ().
-.LP
-As an example of a user-defined type routine,
-if you wanted to send the structure
-.DS
-.ft CW
-struct simple {
- int a;
- short b;
-} simple;
-.DE
-then you would call
-.I callrpc()
-as
-.DS
-.ft CW
-callrpc(hostname, PROGNUM, VERSNUM, PROCNUM,
- xdr_simple, &simple ...);
-.DE
-where
-.I xdr_simple()
-is written as:
-.ie t .DS
-.el .DS L
-.ft CW
-#include <rpc/rpc.h>
-
-xdr_simple(xdrsp, simplep)
- XDR *xdrsp;
- struct simple *simplep;
-{
- if (!xdr_int(xdrsp, &simplep->a))
- return (0);
- if (!xdr_short(xdrsp, &simplep->b))
- return (0);
- return (1);
-}
-.DE
-.LP
-An XDR routine returns nonzero (true in the sense of C) if it
-completes successfully, and zero otherwise.
-A complete description of XDR is in the
-.I "XDR Protocol Specification"
-section of this manual, only few implementation examples are
-given here.
-.LP
-In addition to the built-in primitives,
-there are also the prefabricated building blocks:
-.DS
-.ft CW
-xdr_array() xdr_bytes() xdr_reference()
-xdr_vector() xdr_union() xdr_pointer()
-xdr_string() xdr_opaque()
-.DE
-To send a variable array of integers,
-you might package them up as a structure like this
-.DS
-.ft CW
-struct varintarr {
- int *data;
- int arrlnth;
-} arr;
-.DE
-and make an RPC call such as
-.DS
-.ft CW
-callrpc(hostname, PROGNUM, VERSNUM, PROCNUM,
- xdr_varintarr, &arr...);
-.DE
-with
-.I xdr_varintarr()
-defined as:
-.ie t .DS
-.el .DS L
-.ft CW
-xdr_varintarr(xdrsp, arrp)
- XDR *xdrsp;
- struct varintarr *arrp;
-{
- return (xdr_array(xdrsp, &arrp->data, &arrp->arrlnth,
- MAXLEN, sizeof(int), xdr_int));
-}
-.DE
-This routine takes as parameters the XDR handle,
-a pointer to the array, a pointer to the size of the array,
-the maximum allowable array size,
-the size of each array element,
-and an XDR routine for handling each array element.
-.KS
-.LP
-If the size of the array is known in advance, one can use
-.I xdr_vector (),
-which serializes fixed-length arrays.
-.ie t .DS
-.el .DS L
-.ft CW
-int intarr[SIZE];
-
-xdr_intarr(xdrsp, intarr)
- XDR *xdrsp;
- int intarr[];
-{
- int i;
-
- return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int),
- xdr_int));
-}
-.DE
-.KE
-.LP
-XDR always converts quantities to 4-byte multiples when serializing.
-Thus, if either of the examples above involved characters
-instead of integers, each character would occupy 32 bits.
-That is the reason for the XDR routine
-.I xdr_bytes()
-which is like
-.I xdr_array()
-except that it packs characters;
-.I xdr_bytes()
-has four parameters, similar to the first four parameters of
-.I xdr_array ().
-For null-terminated strings, there is also the
-.I xdr_string()
-routine, which is the same as
-.I xdr_bytes()
-without the length parameter.
-On serializing it gets the string length from
-.I strlen (),
-and on deserializing it creates a null-terminated string.
-.LP
-Here is a final example that calls the previously written
-.I xdr_simple()
-as well as the built-in functions
-.I xdr_string()
-and
-.I xdr_reference (),
-which chases pointers:
-.ie t .DS
-.el .DS L
-.ft CW
-struct finalexample {
- char *string;
- struct simple *simplep;
-} finalexample;
-
-xdr_finalexample(xdrsp, finalp)
- XDR *xdrsp;
- struct finalexample *finalp;
-{
-
- if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN))
- return (0);
- if (!xdr_reference(xdrsp, &finalp->simplep,
- sizeof(struct simple), xdr_simple);
- return (0);
- return (1);
-}
-.DE
-Note that we could as easily call
-.I xdr_simple()
-here instead of
-.I xdr_reference ().
-.NH 1
-\&Lowest Layer of RPC
-.IX "lowest layer of RPC"
-.IX "RPC" "lowest layer"
-.LP
-In the examples given so far,
-RPC takes care of many details automatically for you.
-In this section, we'll show you how you can change the defaults
-by using lower layers of the RPC library.
-It is assumed that you are familiar with sockets
-and the system calls for dealing with them.
-.LP
-There are several occasions when you may need to use lower layers of
-RPC. First, you may need to use TCP, since the higher layer uses UDP,
-which restricts RPC calls to 8K bytes of data. Using TCP permits calls
-to send long streams of data.
-For an example, see the
-.I TCP
-section below. Second, you may want to allocate and free memory
-while serializing or deserializing with XDR routines.
-There is no call at the higher level to let
-you free memory explicitly.
-For more explanation, see the
-.I "Memory Allocation with XDR"
-section below.
-Third, you may need to perform authentication
-on either the client or server side, by supplying
-credentials or verifying them.
-See the explanation in the
-.I Authentication
-section below.
-.NH 2
-\&More on the Server Side
-.IX RPC "server side"
-.LP
-The server for the
-.I nusers()
-program shown below does the same thing as the one using
-.I registerrpc()
-above, but is written using a lower layer of the RPC package:
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <utmp.h>
-#include <rpcsvc/rusers.h>
-
-main()
-{
- SVCXPRT *transp;
- int nuser();
-
- transp = svcudp_create(RPC_ANYSOCK);
- if (transp == NULL){
- fprintf(stderr, "can't create an RPC server\en");
- exit(1);
- }
- pmap_unset(RUSERSPROG, RUSERSVERS);
- if (!svc_register(transp, RUSERSPROG, RUSERSVERS,
- nuser, IPPROTO_UDP)) {
- fprintf(stderr, "can't register RUSER service\en");
- exit(1);
- }
- svc_run(); /* \fINever returns\fP */
- fprintf(stderr, "should never reach this point\en");
-}
-
-nuser(rqstp, transp)
- struct svc_req *rqstp;
- SVCXPRT *transp;
-{
- unsigned long nusers;
-
- switch (rqstp->rq_proc) {
- case NULLPROC:
- if (!svc_sendreply(transp, xdr_void, 0))
- fprintf(stderr, "can't reply to RPC call\en");
- return;
- case RUSERSPROC_NUM:
-.ft I
- /*
- * Code here to compute the number of users
- * and assign it to the variable \fInusers\fP
- */
-.ft CW
- if (!svc_sendreply(transp, xdr_u_long, &nusers))
- fprintf(stderr, "can't reply to RPC call\en");
- return;
- default:
- svcerr_noproc(transp);
- return;
- }
-}
-.DE
-.LP
-First, the server gets a transport handle, which is used
-for receiving and replying to RPC messages.
-.I registerrpc()
-uses
-.I svcudp_create()
-to get a UDP handle.
-If you require a more reliable protocol, call
-.I svctcp_create()
-instead.
-If the argument to
-.I svcudp_create()
-is
-.I RPC_ANYSOCK
-the RPC library creates a socket
-on which to receive and reply to RPC calls. Otherwise,
-.I svcudp_create()
-expects its argument to be a valid socket number.
-If you specify your own socket, it can be bound or unbound.
-If it is bound to a port by the user, the port numbers of
-.I svcudp_create()
-and
-.I clnttcp_create()
-(the low-level client routine) must match.
-.LP
-If the user specifies the
-.I RPC_ANYSOCK
-argument, the RPC library routines will open sockets.
-Otherwise they will expect the user to do so. The routines
-.I svcudp_create()
-and
-.I clntudp_create()
-will cause the RPC library routines to
-.I bind()
-their socket if it is not bound already.
-.LP
-A service may choose to register its port number with the
-local portmapper service. This is done is done by specifying
-a non-zero protocol number in
-.I svc_register ().
-Incidently, a client can discover the server's port number by
-consulting the portmapper on their server's machine. This can
-be done automatically by specifying a zero port number in
-.I clntudp_create()
-or
-.I clnttcp_create ().
-.LP
-After creating an
-.I SVCXPRT ,
-the next step is to call
-.I pmap_unset()
-so that if the
-.I nusers()
-server crashed earlier,
-any previous trace of it is erased before restarting.
-More precisely,
-.I pmap_unset()
-erases the entry for
-.I RUSERSPROG
-from the port mapper's tables.
-.LP
-Finally, we associate the program number for
-.I nusers()
-with the procedure
-.I nuser ().
-The final argument to
-.I svc_register()
-is normally the protocol being used,
-which, in this case, is
-.I IPPROTO_UDP
-Notice that unlike
-.I registerrpc (),
-there are no XDR routines involved
-in the registration process.
-Also, registration is done on the program,
-rather than procedure, level.
-.LP
-The user routine
-.I nuser()
-must call and dispatch the appropriate XDR routines
-based on the procedure number.
-Note that
-two things are handled by
-.I nuser()
-that
-.I registerrpc()
-handles automatically.
-The first is that procedure
-.I NULLPROC
-(currently zero) returns with no results.
-This can be used as a simple test
-for detecting if a remote program is running.
-Second, there is a check for invalid procedure numbers.
-If one is detected,
-.I svcerr_noproc()
-is called to handle the error.
-.KS
-.LP
-The user service routine serializes the results and returns
-them to the RPC caller via
-.I svc_sendreply()
-Its first parameter is the
-.I SVCXPRT
-handle, the second is the XDR routine,
-and the third is a pointer to the data to be returned.
-Not illustrated above is how a server
-handles an RPC program that receives data.
-As an example, we can add a procedure
-.I RUSERSPROC_BOOL
-which has an argument
-.I nusers (),
-and returns
-.I TRUE
-or
-.I FALSE
-depending on whether there are nusers logged on.
-It would look like this:
-.ie t .DS
-.el .DS L
-.ft CW
-case RUSERSPROC_BOOL: {
- int bool;
- unsigned nuserquery;
-
- if (!svc_getargs(transp, xdr_u_int, &nuserquery) {
- svcerr_decode(transp);
- return;
- }
-.ft I
- /*
- * Code to set \fInusers\fP = number of users
- */
-.ft CW
- if (nuserquery == nusers)
- bool = TRUE;
- else
- bool = FALSE;
- if (!svc_sendreply(transp, xdr_bool, &bool)) {
- fprintf(stderr, "can't reply to RPC call\en");
- return (1);
- }
- return;
-}
-.DE
-.KE
-.LP
-The relevant routine is
-.I svc_getargs()
-which takes an
-.I SVCXPRT
-handle, the XDR routine,
-and a pointer to where the input is to be placed as arguments.
-.NH 2
-\&Memory Allocation with XDR
-.IX "memory allocation with XDR"
-.IX XDR "memory allocation"
-.LP
-XDR routines not only do input and output,
-they also do memory allocation.
-This is why the second parameter of
-.I xdr_array()
-is a pointer to an array, rather than the array itself.
-If it is
-.I NULL ,
-then
-.I xdr_array()
-allocates space for the array and returns a pointer to it,
-putting the size of the array in the third argument.
-As an example, consider the following XDR routine
-.I xdr_chararr1()
-which deals with a fixed array of bytes with length
-.I SIZE .
-.ie t .DS
-.el .DS L
-.ft CW
-xdr_chararr1(xdrsp, chararr)
- XDR *xdrsp;
- char chararr[];
-{
- char *p;
- int len;
-
- p = chararr;
- len = SIZE;
- return (xdr_bytes(xdrsp, &p, &len, SIZE));
-}
-.DE
-If space has already been allocated in
-.I chararr ,
-it can be called from a server like this:
-.ie t .DS
-.el .DS L
-.ft CW
-char chararr[SIZE];
-
-svc_getargs(transp, xdr_chararr1, chararr);
-.DE
-If you want XDR to do the allocation,
-you would have to rewrite this routine in the following way:
-.ie t .DS
-.el .DS L
-.ft CW
-xdr_chararr2(xdrsp, chararrp)
- XDR *xdrsp;
- char **chararrp;
-{
- int len;
-
- len = SIZE;
- return (xdr_bytes(xdrsp, charrarrp, &len, SIZE));
-}
-.DE
-Then the RPC call might look like this:
-.ie t .DS
-.el .DS L
-.ft CW
-char *arrptr;
-
-arrptr = NULL;
-svc_getargs(transp, xdr_chararr2, &arrptr);
-.ft I
-/*
- * Use the result here
- */
-.ft CW
-svc_freeargs(transp, xdr_chararr2, &arrptr);
-.DE
-Note that, after being used, the character array can be freed with
-.I svc_freeargs()
-.I svc_freeargs()
-will not attempt to free any memory if the variable indicating it
-is NULL. For example, in the the routine
-.I xdr_finalexample (),
-given earlier, if
-.I finalp->string
-was NULL, then it would not be freed. The same is true for
-.I finalp->simplep .
-.LP
-To summarize, each XDR routine is responsible
-for serializing, deserializing, and freeing memory.
-When an XDR routine is called from
-.I callrpc()
-the serializing part is used.
-When called from
-.I svc_getargs()
-the deserializer is used.
-And when called from
-.I svc_freeargs()
-the memory deallocator is used. When building simple examples like those
-in this section, a user doesn't have to worry
-about the three modes.
-See the
-.I "External Data Representation: Sun Technical Notes"
-for examples of more sophisticated XDR routines that determine
-which of the three modes they are in and adjust their behavior accordingly.
-.KS
-.NH 2
-\&The Calling Side
-.IX RPC "calling side"
-.LP
-When you use
-.I callrpc()
-you have no control over the RPC delivery
-mechanism or the socket used to transport the data.
-To illustrate the layer of RPC that lets you adjust these
-parameters, consider the following code to call the
-.I nusers
-service:
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <utmp.h>
-#include <rpcsvc/rusers.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <netdb.h>
-
-main(argc, argv)
- int argc;
- char **argv;
-{
- struct hostent *hp;
- struct timeval pertry_timeout, total_timeout;
- struct sockaddr_in server_addr;
- int sock = RPC_ANYSOCK;
- register CLIENT *client;
- enum clnt_stat clnt_stat;
- unsigned long nusers;
-
- if (argc != 2) {
- fprintf(stderr, "usage: nusers hostname\en");
- exit(-1);
- }
- if ((hp = gethostbyname(argv[1])) == NULL) {
- fprintf(stderr, "can't get addr for %s\en",argv[1]);
- exit(-1);
- }
- pertry_timeout.tv_sec = 3;
- pertry_timeout.tv_usec = 0;
- bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
- hp->h_length);
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = 0;
- if ((client = clntudp_create(&server_addr, RUSERSPROG,
- RUSERSVERS, pertry_timeout, &sock)) == NULL) {
- clnt_pcreateerror("clntudp_create");
- exit(-1);
- }
- total_timeout.tv_sec = 20;
- total_timeout.tv_usec = 0;
- clnt_stat = clnt_call(client, RUSERSPROC_NUM, xdr_void,
- 0, xdr_u_long, &nusers, total_timeout);
- if (clnt_stat != RPC_SUCCESS) {
- clnt_perror(client, "rpc");
- exit(-1);
- }
- clnt_destroy(client);
- close(sock);
- exit(0);
-}
-.vs
-.DE
-.KE
-The low-level version of
-.I callrpc()
-is
-.I clnt_call()
-which takes a
-.I CLIENT
-pointer rather than a host name. The parameters to
-.I clnt_call()
-are a
-.I CLIENT
-pointer, the procedure number,
-the XDR routine for serializing the argument,
-a pointer to the argument,
-the XDR routine for deserializing the return value,
-a pointer to where the return value will be placed,
-and the time in seconds to wait for a reply.
-.LP
-The
-.I CLIENT
-pointer is encoded with the transport mechanism.
-.I callrpc()
-uses UDP, thus it calls
-.I clntudp_create()
-to get a
-.I CLIENT
-pointer. To get TCP (Transmission Control Protocol), you would use
-.I clnttcp_create() .
-.LP
-The parameters to
-.I clntudp_create()
-are the server address, the program number, the version number,
-a timeout value (between tries), and a pointer to a socket.
-The final argument to
-.I clnt_call()
-is the total time to wait for a response.
-Thus, the number of tries is the
-.I clnt_call()
-timeout divided by the
-.I clntudp_create()
-timeout.
-.LP
-Note that the
-.I clnt_destroy()
-call
-always deallocates the space associated with the
-.I CLIENT
-handle. It closes the socket associated with the
-.I CLIENT
-handle, however, only if the RPC library opened it. It the
-socket was opened by the user, it stays open. This makes it
-possible, in cases where there are multiple client handles
-using the same socket, to destroy one handle without closing
-the socket that other handles are using.
-.LP
-To make a stream connection, the call to
-.I clntudp_create()
-is replaced with a call to
-.I clnttcp_create() .
-.DS
-.ft CW
-clnttcp_create(&server_addr, prognum, versnum, &sock,
- inputsize, outputsize);
-.DE
-There is no timeout argument; instead, the receive and send buffer
-sizes must be specified. When the
-.I clnttcp_create()
-call is made, a TCP connection is established.
-All RPC calls using that
-.I CLIENT
-handle would use this connection.
-The server side of an RPC call using TCP has
-.I svcudp_create()
-replaced by
-.I svctcp_create() .
-.DS
-.ft CW
-transp = svctcp_create(RPC_ANYSOCK, 0, 0);
-.DE
-The last two arguments to
-.I svctcp_create()
-are send and receive sizes respectively. If `0' is specified for
-either of these, the system chooses a reasonable default.
-.KS
-.NH 1
-\&Other RPC Features
-.IX "RPC" "miscellaneous features"
-.IX "miscellaneous RPC features"
-.LP
-This section discusses some other aspects of RPC
-that are occasionally useful.
-.NH 2
-\&Select on the Server Side
-.IX RPC select() RPC \fIselect()\fP
-.IX select() "" \fIselect()\fP "on the server side"
-.LP
-Suppose a process is processing RPC requests
-while performing some other activity.
-If the other activity involves periodically updating a data structure,
-the process can set an alarm signal before calling
-.I svc_run()
-But if the other activity
-involves waiting on a a file descriptor, the
-.I svc_run()
-call won't work.
-The code for
-.I svc_run()
-is as follows:
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-void
-svc_run()
-{
- fd_set readfds;
- int dtbsz = getdtablesize();
-
- for (;;) {
- readfds = svc_fds;
- switch (select(dtbsz, &readfds, NULL,NULL,NULL)) {
-
- case -1:
- if (errno == EINTR)
- continue;
- perror("select");
- return;
- case 0:
- break;
- default:
- svc_getreqset(&readfds);
- }
- }
-}
-.vs
-.DE
-.KE
-.LP
-You can bypass
-.I svc_run()
-and call
-.I svc_getreqset()
-yourself.
-All you need to know are the file descriptors
-of the socket(s) associated with the programs you are waiting on.
-Thus you can have your own
-.I select()
-.IX select() "" \fIselect()\fP
-that waits on both the RPC socket,
-and your own descriptors. Note that
-.I svc_fds()
-is a bit mask of all the file descriptors that RPC is using for
-services. It can change everytime that
-.I any
-RPC library routine is called, because descriptors are constantly
-being opened and closed, for example for TCP connections.
-.NH 2
-\&Broadcast RPC
-.IX "broadcast RPC"
-.IX RPC "broadcast"
-.LP
-The
-.I portmapper
-is a daemon that converts RPC program numbers
-into DARPA protocol port numbers; see the
-.I portmap
-man page. You can't do broadcast RPC without the portmapper.
-Here are the main differences between
-broadcast RPC and normal RPC calls:
-.IP 1.
-Normal RPC expects one answer, whereas
-broadcast RPC expects many answers
-(one or more answer from each responding machine).
-.IP 2.
-Broadcast RPC can only be supported by packet-oriented (connectionless)
-transport protocols like UPD/IP.
-.IP 3.
-The implementation of broadcast RPC
-treats all unsuccessful responses as garbage by filtering them out.
-Thus, if there is a version mismatch between the
-broadcaster and a remote service,
-the user of broadcast RPC never knows.
-.IP 4.
-All broadcast messages are sent to the portmap port.
-Thus, only services that register themselves with their portmapper
-are accessible via the broadcast RPC mechanism.
-.IP 5.
-Broadcast requests are limited in size to the MTU (Maximum Transfer
-Unit) of the local network. For Ethernet, the MTU is 1500 bytes.
-.KS
-.NH 3
-\&Broadcast RPC Synopsis
-.IX "broadcast RPC" synopsis
-.IX "RPC" "broadcast synopsis"
-.ie t .DS
-.el .DS L
-.ft CW
-#include <rpc/pmap_clnt.h>
- . . .
-enum clnt_stat clnt_stat;
- . . .
-clnt_stat = clnt_broadcast(prognum, versnum, procnum,
- inproc, in, outproc, out, eachresult)
- u_long prognum; /* \fIprogram number\fP */
- u_long versnum; /* \fIversion number\fP */
- u_long procnum; /* \fIprocedure number\fP */
- xdrproc_t inproc; /* \fIxdr routine for args\fP */
- caddr_t in; /* \fIpointer to args\fP */
- xdrproc_t outproc; /* \fIxdr routine for results\fP */
- caddr_t out; /* \fIpointer to results\fP */
- bool_t (*eachresult)();/* \fIcall with each result gotten\fP */
-.DE
-.KE
-The procedure
-.I eachresult()
-is called each time a valid result is obtained.
-It returns a boolean that indicates
-whether or not the user wants more responses.
-.ie t .DS
-.el .DS L
-.ft CW
-bool_t done;
- . . .
-done = eachresult(resultsp, raddr)
- caddr_t resultsp;
- struct sockaddr_in *raddr; /* \fIAddr of responding machine\fP */
-.DE
-If
-.I done
-is
-.I TRUE ,
-then broadcasting stops and
-.I clnt_broadcast()
-returns successfully.
-Otherwise, the routine waits for another response.
-The request is rebroadcast
-after a few seconds of waiting.
-If no responses come back,
-the routine returns with
-.I RPC_TIMEDOUT .
-.NH 2
-\&Batching
-.IX "batching"
-.IX RPC "batching"
-.LP
-The RPC architecture is designed so that clients send a call message,
-and wait for servers to reply that the call succeeded.
-This implies that clients do not compute
-while servers are processing a call.
-This is inefficient if the client does not want or need
-an acknowledgement for every message sent.
-It is possible for clients to continue computing
-while waiting for a response,
-using RPC batch facilities.
-.LP
-RPC messages can be placed in a \*Qpipeline\*U of calls
-to a desired server; this is called batching.
-Batching assumes that:
-1) each RPC call in the pipeline requires no response from the server,
-and the server does not send a response message; and
-2) the pipeline of calls is transported on a reliable
-byte stream transport such as TCP/IP.
-Since the server does not respond to every call,
-the client can generate new calls in parallel
-with the server executing previous calls.
-Furthermore, the TCP/IP implementation can buffer up
-many call messages, and send them to the server in one
-.I write()
-system call. This overlapped execution
-greatly decreases the interprocess communication overhead of
-the client and server processes,
-and the total elapsed time of a series of calls.
-.LP
-Since the batched calls are buffered,
-the client should eventually do a nonbatched call
-in order to flush the pipeline.
-.LP
-A contrived example of batching follows.
-Assume a string rendering service (like a window system)
-has two similar calls: one renders a string and returns void results,
-while the other renders a string and remains silent.
-The service (using the TCP/IP transport) may look like:
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <suntool/windows.h>
-
-void windowdispatch();
-
-main()
-{
- SVCXPRT *transp;
-
- transp = svctcp_create(RPC_ANYSOCK, 0, 0);
- if (transp == NULL){
- fprintf(stderr, "can't create an RPC server\en");
- exit(1);
- }
- pmap_unset(WINDOWPROG, WINDOWVERS);
- if (!svc_register(transp, WINDOWPROG, WINDOWVERS,
- windowdispatch, IPPROTO_TCP)) {
- fprintf(stderr, "can't register WINDOW service\en");
- exit(1);
- }
- svc_run(); /* \fINever returns\fP */
- fprintf(stderr, "should never reach this point\en");
-}
-
-void
-windowdispatch(rqstp, transp)
- struct svc_req *rqstp;
- SVCXPRT *transp;
-{
- char *s = NULL;
-
- switch (rqstp->rq_proc) {
- case NULLPROC:
- if (!svc_sendreply(transp, xdr_void, 0))
- fprintf(stderr, "can't reply to RPC call\en");
- return;
- case RENDERSTRING:
- if (!svc_getargs(transp, xdr_wrapstring, &s)) {
- fprintf(stderr, "can't decode arguments\en");
-.ft I
- /*
- * Tell caller he screwed up
- */
-.ft CW
- svcerr_decode(transp);
- break;
- }
-.ft I
- /*
- * Code here to render the string \fIs\fP
- */
-.ft CW
- if (!svc_sendreply(transp, xdr_void, NULL))
- fprintf(stderr, "can't reply to RPC call\en");
- break;
- case RENDERSTRING_BATCHED:
- if (!svc_getargs(transp, xdr_wrapstring, &s)) {
- fprintf(stderr, "can't decode arguments\en");
-.ft I
- /*
- * We are silent in the face of protocol errors
- */
-.ft CW
- break;
- }
-.ft I
- /*
- * Code here to render string s, but send no reply!
- */
-.ft CW
- break;
- default:
- svcerr_noproc(transp);
- return;
- }
-.ft I
- /*
- * Now free string allocated while decoding arguments
- */
-.ft CW
- svc_freeargs(transp, xdr_wrapstring, &s);
-}
-.DE
-Of course the service could have one procedure
-that takes the string and a boolean
-to indicate whether or not the procedure should respond.
-.LP
-In order for a client to take advantage of batching,
-the client must perform RPC calls on a TCP-based transport
-and the actual calls must have the following attributes:
-1) the result's XDR routine must be zero
-.I NULL ),
-and 2) the RPC call's timeout must be zero.
-.KS
-.LP
-Here is an example of a client that uses batching to render a
-bunch of strings; the batching is flushed when the client gets
-a null string (EOF):
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <netdb.h>
-#include <suntool/windows.h>
-
-main(argc, argv)
- int argc;
- char **argv;
-{
- struct hostent *hp;
- struct timeval pertry_timeout, total_timeout;
- struct sockaddr_in server_addr;
- int sock = RPC_ANYSOCK;
- register CLIENT *client;
- enum clnt_stat clnt_stat;
- char buf[1000], *s = buf;
-
- if ((client = clnttcp_create(&server_addr,
- WINDOWPROG, WINDOWVERS, &sock, 0, 0)) == NULL) {
- perror("clnttcp_create");
- exit(-1);
- }
- total_timeout.tv_sec = 0;
- total_timeout.tv_usec = 0;
- while (scanf("%s", s) != EOF) {
- clnt_stat = clnt_call(client, RENDERSTRING_BATCHED,
- xdr_wrapstring, &s, NULL, NULL, total_timeout);
- if (clnt_stat != RPC_SUCCESS) {
- clnt_perror(client, "batched rpc");
- exit(-1);
- }
- }
-
- /* \fINow flush the pipeline\fP */
-
- total_timeout.tv_sec = 20;
- clnt_stat = clnt_call(client, NULLPROC, xdr_void, NULL,
- xdr_void, NULL, total_timeout);
- if (clnt_stat != RPC_SUCCESS) {
- clnt_perror(client, "rpc");
- exit(-1);
- }
- clnt_destroy(client);
- exit(0);
-}
-.vs
-.DE
-.KE
-Since the server sends no message,
-the clients cannot be notified of any of the failures that may occur.
-Therefore, clients are on their own when it comes to handling errors.
-.LP
-The above example was completed to render
-all of the (2000) lines in the file
-.I /etc/termcap .
-The rendering service did nothing but throw the lines away.
-The example was run in the following four configurations:
-1) machine to itself, regular RPC;
-2) machine to itself, batched RPC;
-3) machine to another, regular RPC; and
-4) machine to another, batched RPC.
-The results are as follows:
-1) 50 seconds;
-2) 16 seconds;
-3) 52 seconds;
-4) 10 seconds.
-Running
-.I fscanf()
-on
-.I /etc/termcap
-only requires six seconds.
-These timings show the advantage of protocols
-that allow for overlapped execution,
-though these protocols are often hard to design.
-.NH 2
-\&Authentication
-.IX "authentication"
-.IX "RPC" "authentication"
-.LP
-In the examples presented so far,
-the caller never identified itself to the server,
-and the server never required an ID from the caller.
-Clearly, some network services, such as a network filesystem,
-require stronger security than what has been presented so far.
-.LP
-In reality, every RPC call is authenticated by
-the RPC package on the server, and similarly,
-the RPC client package generates and sends authentication parameters.
-Just as different transports (TCP/IP or UDP/IP)
-can be used when creating RPC clients and servers,
-different forms of authentication can be associated with RPC clients;
-the default authentication type used as a default is type
-.I none .
-.LP
-The authentication subsystem of the RPC package is open ended.
-That is, numerous types of authentication are easy to support.
-.NH 3
-\&UNIX Authentication
-.IX "UNIX Authentication"
-.IP "\fIThe Client Side\fP"
-.LP
-When a caller creates a new RPC client handle as in:
-.DS
-.ft CW
-clnt = clntudp_create(address, prognum, versnum,
- wait, sockp)
-.DE
-the appropriate transport instance defaults
-the associate authentication handle to be
-.DS
-.ft CW
-clnt->cl_auth = authnone_create();
-.DE
-The RPC client can choose to use
-.I UNIX
-style authentication by setting
-.I clnt\->cl_auth
-after creating the RPC client handle:
-.DS
-.ft CW
-clnt->cl_auth = authunix_create_default();
-.DE
-This causes each RPC call associated with
-.I clnt
-to carry with it the following authentication credentials structure:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * UNIX style credentials.
- */
-.ft CW
-struct authunix_parms {
- u_long aup_time; /* \fIcredentials creation time\fP */
- char *aup_machname; /* \fIhost name where client is\fP */
- int aup_uid; /* \fIclient's UNIX effective uid\fP */
- int aup_gid; /* \fIclient's current group id\fP */
- u_int aup_len; /* \fIelement length of aup_gids\fP */
- int *aup_gids; /* \fIarray of groups user is in\fP */
-};
-.DE
-These fields are set by
-.I authunix_create_default()
-by invoking the appropriate system calls.
-Since the RPC user created this new style of authentication,
-the user is responsible for destroying it with:
-.DS
-.ft CW
-auth_destroy(clnt->cl_auth);
-.DE
-This should be done in all cases, to conserve memory.
-.sp
-.IP "\fIThe Server Side\fP"
-.LP
-Service implementors have a harder time dealing with authentication issues
-since the RPC package passes the service dispatch routine a request
-that has an arbitrary authentication style associated with it.
-Consider the fields of a request handle passed to a service dispatch routine:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * An RPC Service request
- */
-.ft CW
-struct svc_req {
- u_long rq_prog; /* \fIservice program number\fP */
- u_long rq_vers; /* \fIservice protocol vers num\fP */
- u_long rq_proc; /* \fIdesired procedure number\fP */
- struct opaque_auth rq_cred; /* \fIraw credentials from wire\fP */
- caddr_t rq_clntcred; /* \fIcredentials (read only)\fP */
-};
-.DE
-The
-.I rq_cred
-is mostly opaque, except for one field of interest:
-the style or flavor of authentication credentials:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * Authentication info. Mostly opaque to the programmer.
- */
-.ft CW
-struct opaque_auth {
- enum_t oa_flavor; /* \fIstyle of credentials\fP */
- caddr_t oa_base; /* \fIaddress of more auth stuff\fP */
- u_int oa_length; /* \fInot to exceed \fIMAX_AUTH_BYTES */
-};
-.DE
-.IX RPC guarantees
-The RPC package guarantees the following
-to the service dispatch routine:
-.IP 1.
-That the request's
-.I rq_cred
-is well formed. Thus the service implementor may inspect the request's
-.I rq_cred.oa_flavor
-to determine which style of authentication the caller used.
-The service implementor may also wish to inspect the other fields of
-.I rq_cred
-if the style is not one of the styles supported by the RPC package.
-.IP 2.
-That the request's
-.I rq_clntcred
-field is either
-.I NULL
-or points to a well formed structure
-that corresponds to a supported style of authentication credentials.
-Remember that only
-.I unix
-style is currently supported, so (currently)
-.I rq_clntcred
-could be cast to a pointer to an
-.I authunix_parms
-structure. If
-.I rq_clntcred
-is
-.I NULL ,
-the service implementor may wish to inspect the other (opaque) fields of
-.I rq_cred
-in case the service knows about a new type of authentication
-that the RPC package does not know about.
-.LP
-Our remote users service example can be extended so that
-it computes results for all users except UID 16:
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-nuser(rqstp, transp)
- struct svc_req *rqstp;
- SVCXPRT *transp;
-{
- struct authunix_parms *unix_cred;
- int uid;
- unsigned long nusers;
-
-.ft I
- /*
- * we don't care about authentication for null proc
- */
-.ft CW
- if (rqstp->rq_proc == NULLPROC) {
- if (!svc_sendreply(transp, xdr_void, 0)) {
- fprintf(stderr, "can't reply to RPC call\en");
- return (1);
- }
- return;
- }
-.ft I
- /*
- * now get the uid
- */
-.ft CW
- switch (rqstp->rq_cred.oa_flavor) {
- case AUTH_UNIX:
- unix_cred =
- (struct authunix_parms *)rqstp->rq_clntcred;
- uid = unix_cred->aup_uid;
- break;
- case AUTH_NULL:
- default:
- svcerr_weakauth(transp);
- return;
- }
- switch (rqstp->rq_proc) {
- case RUSERSPROC_NUM:
-.ft I
- /*
- * make sure caller is allowed to call this proc
- */
-.ft CW
- if (uid == 16) {
- svcerr_systemerr(transp);
- return;
- }
-.ft I
- /*
- * Code here to compute the number of users
- * and assign it to the variable \fInusers\fP
- */
-.ft CW
- if (!svc_sendreply(transp, xdr_u_long, &nusers)) {
- fprintf(stderr, "can't reply to RPC call\en");
- return (1);
- }
- return;
- default:
- svcerr_noproc(transp);
- return;
- }
-}
-.vs
-.DE
-A few things should be noted here.
-First, it is customary not to check
-the authentication parameters associated with the
-.I NULLPROC
-(procedure number zero).
-Second, if the authentication parameter's type is not suitable
-for your service, you should call
-.I svcerr_weakauth() .
-And finally, the service protocol itself should return status
-for access denied; in the case of our example, the protocol
-does not have such a status, so we call the service primitive
-.I svcerr_systemerr()
-instead.
-.LP
-The last point underscores the relation between
-the RPC authentication package and the services;
-RPC deals only with
-.I authentication
-and not with individual services'
-.I "access control" .
-The services themselves must implement their own access control policies
-and reflect these policies as return statuses in their protocols.
-.NH 2
-\&DES Authentication
-.IX RPC DES
-.IX RPC authentication
-.LP
-UNIX authentication is quite easy to defeat. Instead of using
-.I authunix_create_default (),
-one can call
-.I authunix_create()
-and then modify the RPC authentication handle it returns by filling in
-whatever user ID and hostname they wish the server to think they have.
-DES authentication is thus recommended for people who want more security
-than UNIX authentication offers.
-.LP
-The details of the DES authentication protocol are complicated and
-are not explained here.
-See
-.I "Remote Procedure Calls: Protocol Specification"
-for the details.
-.LP
-In order for DES authentication to work, the
-.I keyserv(8c)
-daemon must be running on both the server and client machines. The
-users on these machines need public keys assigned by the network
-administrator in the
-.I publickey(5)
-database. And, they need to have decrypted their secret keys
-using their login password. This automatically happens when one
-logs in using
-.I login(1) ,
-or can be done manually using
-.I keylogin(1) .
-The
-.I "Network Services"
-chapter
-./" XXX
-explains more how to setup secure networking.
-.sp
-.IP "\fIClient Side\fP"
-.LP
-If a client wishes to use DES authentication, it must set its
-authentication handle appropriately. Here is an example:
-.DS
-cl->cl_auth =
- authdes_create(servername, 60, &server_addr, NULL);
-.DE
-The first argument is the network name or \*Qnetname\*U of the owner of
-the server process. Typically, server processes are root processes
-and their netname can be derived using the following call:
-.DS
-char servername[MAXNETNAMELEN];
-
-host2netname(servername, rhostname, NULL);
-.DE
-Here,
-.I rhostname
-is the hostname of the machine the server process is running on.
-.I host2netname()
-fills in
-.I servername
-to contain this root process's netname. If the
-server process was run by a regular user, one could use the call
-.I user2netname()
-instead. Here is an example for a server process with the same user
-ID as the client:
-.DS
-char servername[MAXNETNAMELEN];
-
-user2netname(servername, getuid(), NULL);
-.DE
-The last argument to both of these calls,
-.I user2netname()
-and
-.I host2netname (),
-is the name of the naming domain where the server is located. The
-.I NULL
-used here means \*Quse the local domain name.\*U
-.LP
-The second argument to
-.I authdes_create()
-is a lifetime for the credential. Here it is set to sixty
-seconds. What that means is that the credential will expire 60
-seconds from now. If some mischievous user tries to reuse the
-credential, the server RPC subsystem will recognize that it has
-expired and not grant any requests. If the same mischievous user
-tries to reuse the credential within the sixty second lifetime,
-he will still be rejected because the server RPC subsystem
-remembers which credentials it has already seen in the near past,
-and will not grant requests to duplicates.
-.LP
-The third argument to
-.I authdes_create()
-is the address of the host to synchronize with. In order for DES
-authentication to work, the server and client must agree upon the
-time. Here we pass the address of the server itself, so the
-client and server will both be using the same time: the server's
-time. The argument can be
-.I NULL ,
-which means \*Qdon't bother synchronizing.\*U You should only do this
-if you are sure the client and server are already synchronized.
-.LP
-The final argument to
-.I authdes_create()
-is the address of a DES encryption key to use for encrypting
-timestamps and data. If this argument is
-.I NULL ,
-as it is in this example, a random key will be chosen. The client
-may find out the encryption key being used by consulting the
-.I ah_key
-field of the authentication handle.
-.sp
-.IP "\fIServer Side\fP"
-.LP
-The server side is a lot simpler than the client side. Here is the
-previous example rewritten to use
-.I AUTH_DES
-instead of
-.I AUTH_UNIX :
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-#include <sys/time.h>
-#include <rpc/auth_des.h>
- . . .
- . . .
-nuser(rqstp, transp)
- struct svc_req *rqstp;
- SVCXPRT *transp;
-{
- struct authdes_cred *des_cred;
- int uid;
- int gid;
- int gidlen;
- int gidlist[10];
-.ft I
- /*
- * we don't care about authentication for null proc
- */
-.ft CW
-
- if (rqstp->rq_proc == NULLPROC) {
- /* \fIsame as before\fP */
- }
-
-.ft I
- /*
- * now get the uid
- */
-.ft CW
- switch (rqstp->rq_cred.oa_flavor) {
- case AUTH_DES:
- des_cred =
- (struct authdes_cred *) rqstp->rq_clntcred;
- if (! netname2user(des_cred->adc_fullname.name,
- &uid, &gid, &gidlen, gidlist))
- {
- fprintf(stderr, "unknown user: %s\n",
- des_cred->adc_fullname.name);
- svcerr_systemerr(transp);
- return;
- }
- break;
- case AUTH_NULL:
- default:
- svcerr_weakauth(transp);
- return;
- }
-
-.ft I
- /*
- * The rest is the same as before
- */
-.ft CW
-.vs
-.DE
-Note the use of the routine
-.I netname2user (),
-the inverse of
-.I user2netname ():
-it takes a network ID and converts to a unix ID.
-.I netname2user ()
-also supplies the group IDs which we don't use in this example,
-but which may be useful to other UNIX programs.
-.NH 2
-\&Using Inetd
-.IX inetd "" "using \fIinetd\fP"
-.LP
-An RPC server can be started from
-.I inetd
-The only difference from the usual code is that the service
-creation routine should be called in the following form:
-.ie t .DS
-.el .DS L
-.ft CW
-transp = svcudp_create(0); /* \fIFor UDP\fP */
-transp = svctcp_create(0,0,0); /* \fIFor listener TCP sockets\fP */
-transp = svcfd_create(0,0,0); /* \fIFor connected TCP sockets\fP */
-.DE
-since
-.I inet
-passes a socket as file descriptor 0.
-Also,
-.I svc_register()
-should be called as
-.ie t .DS
-.el .DS L
-.ft CW
-svc_register(transp, PROGNUM, VERSNUM, service, 0);
-.DE
-with the final flag as 0,
-since the program would already be registered by
-.I inetd
-Remember that if you want to exit
-from the server process and return control to
-.I inet
-you need to explicitly exit, since
-.I svc_run()
-never returns.
-.LP
-The format of entries in
-.I /etc/inetd.conf
-for RPC services is in one of the following two forms:
-.ie t .DS
-.el .DS L
-.ft CW
-p_name/version dgram rpc/udp wait/nowait user server args
-p_name/version stream rpc/tcp wait/nowait user server args
-.DE
-where
-.I p_name
-is the symbolic name of the program as it appears in
-.I rpc(5) ,
-.I server
-is the program implementing the server,
-and
-.I program
-and
-.I version
-are the program and version numbers of the service.
-For more information, see
-.I inetd.conf(5) .
-.LP
-If the same program handles multiple versions,
-then the version number can be a range,
-as in this example:
-.ie t .DS
-.el .DS L
-.ft CW
-rstatd/1-2 dgram rpc/udp wait root /usr/etc/rpc.rstatd
-.DE
-.NH 1
-\&More Examples
-.sp 1
-.NH 2
-\&Versions
-.IX "versions"
-.IX "RPC" "versions"
-.LP
-By convention, the first version number of program
-.I PROG
-is
-.I PROGVERS_ORIG
-and the most recent version is
-.I PROGVERS
-Suppose there is a new version of the
-.I user
-program that returns an
-.I "unsigned short"
-rather than a
-.I long .
-If we name this version
-.I RUSERSVERS_SHORT
-then a server that wants to support both versions
-would do a double register.
-.ie t .DS
-.el .DS L
-.ft CW
-if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG,
- nuser, IPPROTO_TCP)) {
- fprintf(stderr, "can't register RUSER service\en");
- exit(1);
-}
-if (!svc_register(transp, RUSERSPROG, RUSERSVERS_SHORT,
- nuser, IPPROTO_TCP)) {
- fprintf(stderr, "can't register RUSER service\en");
- exit(1);
-}
-.DE
-Both versions can be handled by the same C procedure:
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-nuser(rqstp, transp)
- struct svc_req *rqstp;
- SVCXPRT *transp;
-{
- unsigned long nusers;
- unsigned short nusers2;
-
- switch (rqstp->rq_proc) {
- case NULLPROC:
- if (!svc_sendreply(transp, xdr_void, 0)) {
- fprintf(stderr, "can't reply to RPC call\en");
- return (1);
- }
- return;
- case RUSERSPROC_NUM:
-.ft I
- /*
- * Code here to compute the number of users
- * and assign it to the variable \fInusers\fP
- */
-.ft CW
- nusers2 = nusers;
- switch (rqstp->rq_vers) {
- case RUSERSVERS_ORIG:
- if (!svc_sendreply(transp, xdr_u_long,
- &nusers)) {
- fprintf(stderr,"can't reply to RPC call\en");
- }
- break;
- case RUSERSVERS_SHORT:
- if (!svc_sendreply(transp, xdr_u_short,
- &nusers2)) {
- fprintf(stderr,"can't reply to RPC call\en");
- }
- break;
- }
- default:
- svcerr_noproc(transp);
- return;
- }
-}
-.vs
-.DE
-.KS
-.NH 2
-\&TCP
-.IX "TCP"
-.LP
-Here is an example that is essentially
-.I rcp.
-The initiator of the RPC
-.I snd
-call takes its standard input and sends it to the server
-.I rcv
-which prints it on standard output.
-The RPC call uses TCP.
-This also illustrates an XDR procedure that behaves differently
-on serialization than on deserialization.
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * The xdr routine:
- * on decode, read from wire, write onto fp
- * on encode, read from fp, write onto wire
- */
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-
-xdr_rcp(xdrs, fp)
- XDR *xdrs;
- FILE *fp;
-{
- unsigned long size;
- char buf[BUFSIZ], *p;
-
- if (xdrs->x_op == XDR_FREE)/* nothing to free */
- return 1;
- while (1) {
- if (xdrs->x_op == XDR_ENCODE) {
- if ((size = fread(buf, sizeof(char), BUFSIZ,
- fp)) == 0 && ferror(fp)) {
- fprintf(stderr, "can't fread\en");
- return (1);
- }
- }
- p = buf;
- if (!xdr_bytes(xdrs, &p, &size, BUFSIZ))
- return 0;
- if (size == 0)
- return 1;
- if (xdrs->x_op == XDR_DECODE) {
- if (fwrite(buf, sizeof(char), size,
- fp) != size) {
- fprintf(stderr, "can't fwrite\en");
- return (1);
- }
- }
- }
-}
-.vs
-.DE
-.KE
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * The sender routines
- */
-.ft CW
-#include <stdio.h>
-#include <netdb.h>
-#include <rpc/rpc.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-
-main(argc, argv)
- int argc;
- char **argv;
-{
- int xdr_rcp();
- int err;
-
- if (argc < 2) {
- fprintf(stderr, "usage: %s servername\en", argv[0]);
- exit(-1);
- }
- if ((err = callrpctcp(argv[1], RCPPROG, RCPPROC,
- RCPVERS, xdr_rcp, stdin, xdr_void, 0) != 0)) {
- clnt_perrno(err);
- fprintf(stderr, "can't make RPC call\en");
- exit(1);
- }
- exit(0);
-}
-
-callrpctcp(host, prognum, procnum, versnum,
- inproc, in, outproc, out)
- char *host, *in, *out;
- xdrproc_t inproc, outproc;
-{
- struct sockaddr_in server_addr;
- int socket = RPC_ANYSOCK;
- enum clnt_stat clnt_stat;
- struct hostent *hp;
- register CLIENT *client;
- struct timeval total_timeout;
-
- if ((hp = gethostbyname(host)) == NULL) {
- fprintf(stderr, "can't get addr for '%s'\en", host);
- return (-1);
- }
- bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
- hp->h_length);
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = 0;
- if ((client = clnttcp_create(&server_addr, prognum,
- versnum, &socket, BUFSIZ, BUFSIZ)) == NULL) {
- perror("rpctcp_create");
- return (-1);
- }
- total_timeout.tv_sec = 20;
- total_timeout.tv_usec = 0;
- clnt_stat = clnt_call(client, procnum,
- inproc, in, outproc, out, total_timeout);
- clnt_destroy(client);
- return (int)clnt_stat;
-}
-.vs
-.DE
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * The receiving routines
- */
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-
-main()
-{
- register SVCXPRT *transp;
- int rcp_service(), xdr_rcp();
-
- if ((transp = svctcp_create(RPC_ANYSOCK,
- BUFSIZ, BUFSIZ)) == NULL) {
- fprintf("svctcp_create: error\en");
- exit(1);
- }
- pmap_unset(RCPPROG, RCPVERS);
- if (!svc_register(transp,
- RCPPROG, RCPVERS, rcp_service, IPPROTO_TCP)) {
- fprintf(stderr, "svc_register: error\en");
- exit(1);
- }
- svc_run(); /* \fInever returns\fP */
- fprintf(stderr, "svc_run should never return\en");
-}
-
-rcp_service(rqstp, transp)
- register struct svc_req *rqstp;
- register SVCXPRT *transp;
-{
- switch (rqstp->rq_proc) {
- case NULLPROC:
- if (svc_sendreply(transp, xdr_void, 0) == 0) {
- fprintf(stderr, "err: rcp_service");
- return (1);
- }
- return;
- case RCPPROC_FP:
- if (!svc_getargs(transp, xdr_rcp, stdout)) {
- svcerr_decode(transp);
- return;
- }
- if (!svc_sendreply(transp, xdr_void, 0)) {
- fprintf(stderr, "can't reply\en");
- return;
- }
- return (0);
- default:
- svcerr_noproc(transp);
- return;
- }
-}
-.vs
-.DE
-.NH 2
-\&Callback Procedures
-.IX RPC "callback procedures"
-.LP
-Occasionally, it is useful to have a server become a client,
-and make an RPC call back to the process which is its client.
-An example is remote debugging,
-where the client is a window system program,
-and the server is a debugger running on the remote machine.
-Most of the time,
-the user clicks a mouse button at the debugging window,
-which converts this to a debugger command,
-and then makes an RPC call to the server
-(where the debugger is actually running),
-telling it to execute that command.
-However, when the debugger hits a breakpoint, the roles are reversed,
-and the debugger wants to make an rpc call to the window program,
-so that it can inform the user that a breakpoint has been reached.
-.LP
-In order to do an RPC callback,
-you need a program number to make the RPC call on.
-Since this will be a dynamically generated program number,
-it should be in the transient range,
-.I "0x40000000 - 0x5fffffff" .
-The routine
-.I gettransient()
-returns a valid program number in the transient range,
-and registers it with the portmapper.
-It only talks to the portmapper running on the same machine as the
-.I gettransient()
-routine itself. The call to
-.I pmap_set()
-is a test and set operation,
-in that it indivisibly tests whether a program number
-has already been registered,
-and if it has not, then reserves it. On return, the
-.I sockp
-argument will contain a socket that can be used
-as the argument to an
-.I svcudp_create()
-or
-.I svctcp_create()
-call.
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <sys/socket.h>
-
-gettransient(proto, vers, sockp)
- int proto, vers, *sockp;
-{
- static int prognum = 0x40000000;
- int s, len, socktype;
- struct sockaddr_in addr;
-
- switch(proto) {
- case IPPROTO_UDP:
- socktype = SOCK_DGRAM;
- break;
- case IPPROTO_TCP:
- socktype = SOCK_STREAM;
- break;
- default:
- fprintf(stderr, "unknown protocol type\en");
- return 0;
- }
- if (*sockp == RPC_ANYSOCK) {
- if ((s = socket(AF_INET, socktype, 0)) < 0) {
- perror("socket");
- return (0);
- }
- *sockp = s;
- }
- else
- s = *sockp;
- addr.sin_addr.s_addr = 0;
- addr.sin_family = AF_INET;
- addr.sin_port = 0;
- len = sizeof(addr);
-.ft I
- /*
- * may be already bound, so don't check for error
- */
-.ft CW
- bind(s, &addr, len);
- if (getsockname(s, &addr, &len)< 0) {
- perror("getsockname");
- return (0);
- }
- while (!pmap_set(prognum++, vers, proto,
- ntohs(addr.sin_port))) continue;
- return (prognum-1);
-}
-.vs
-.DE
-.SH
-Note:
-.I
-The call to
-.I ntohs()
-is necessary to ensure that the port number in
-.I "addr.sin_port" ,
-which is in
-.I network
-byte order, is passed in
-.I host
-byte order (as
-.I pmap_set()
-expects). See the
-.I byteorder(3N)
-man page for more details on the conversion of network
-addresses from network to host byte order.
-.KS
-.LP
-The following pair of programs illustrate how to use the
-.I gettransient()
-routine.
-The client makes an RPC call to the server,
-passing it a transient program number.
-Then the client waits around to receive a callback
-from the server at that program number.
-The server registers the program
-.I EXAMPLEPROG
-so that it can receive the RPC call
-informing it of the callback program number.
-Then at some random time (on receiving an
-.I ALRM
-signal in this example), it sends a callback RPC call,
-using the program number it received earlier.
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * client
- */
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-
-int callback();
-char hostname[256];
-
-main()
-{
- int x, ans, s;
- SVCXPRT *xprt;
-
- gethostname(hostname, sizeof(hostname));
- s = RPC_ANYSOCK;
- x = gettransient(IPPROTO_UDP, 1, &s);
- fprintf(stderr, "client gets prognum %d\en", x);
- if ((xprt = svcudp_create(s)) == NULL) {
- fprintf(stderr, "rpc_server: svcudp_create\en");
- exit(1);
- }
-.ft I
- /* protocol is 0 - gettransient does registering
- */
-.ft CW
- (void)svc_register(xprt, x, 1, callback, 0);
- ans = callrpc(hostname, EXAMPLEPROG, EXAMPLEVERS,
- EXAMPLEPROC_CALLBACK, xdr_int, &x, xdr_void, 0);
- if ((enum clnt_stat) ans != RPC_SUCCESS) {
- fprintf(stderr, "call: ");
- clnt_perrno(ans);
- fprintf(stderr, "\en");
- }
- svc_run();
- fprintf(stderr, "Error: svc_run shouldn't return\en");
-}
-
-callback(rqstp, transp)
- register struct svc_req *rqstp;
- register SVCXPRT *transp;
-{
- switch (rqstp->rq_proc) {
- case 0:
- if (!svc_sendreply(transp, xdr_void, 0)) {
- fprintf(stderr, "err: exampleprog\en");
- return (1);
- }
- return (0);
- case 1:
- if (!svc_getargs(transp, xdr_void, 0)) {
- svcerr_decode(transp);
- return (1);
- }
- fprintf(stderr, "client got callback\en");
- if (!svc_sendreply(transp, xdr_void, 0)) {
- fprintf(stderr, "err: exampleprog");
- return (1);
- }
- }
-}
-.vs
-.DE
-.KE
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * server
- */
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <sys/signal.h>
-
-char *getnewprog();
-char hostname[256];
-int docallback();
-int pnum; /* \fIprogram number for callback routine\fP */
-
-main()
-{
- gethostname(hostname, sizeof(hostname));
- registerrpc(EXAMPLEPROG, EXAMPLEVERS,
- EXAMPLEPROC_CALLBACK, getnewprog, xdr_int, xdr_void);
- fprintf(stderr, "server going into svc_run\en");
- signal(SIGALRM, docallback);
- alarm(10);
- svc_run();
- fprintf(stderr, "Error: svc_run shouldn't return\en");
-}
-
-char *
-getnewprog(pnump)
- char *pnump;
-{
- pnum = *(int *)pnump;
- return NULL;
-}
-
-docallback()
-{
- int ans;
-
- ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0,
- xdr_void, 0);
- if (ans != 0) {
- fprintf(stderr, "server: ");
- clnt_perrno(ans);
- fprintf(stderr, "\en");
- }
-}
-.vs
-.DE
diff --git a/cpukit/librpc/src/rpc/PSD.doc/rpc.rfc.ms b/cpukit/librpc/src/rpc/PSD.doc/rpc.rfc.ms
deleted file mode 100644
index af9c2df2ed..0000000000
--- a/cpukit/librpc/src/rpc/PSD.doc/rpc.rfc.ms
+++ /dev/null
@@ -1,1302 +0,0 @@
-.\"
-.\" Must use -- tbl -- with this one
-.\"
-.\" @(#)rpc.rfc.ms 2.2 88/08/05 4.0 RPCSRC
-.de BT
-.if \\n%=1 .tl ''- % -''
-..
-.ND
-.\" prevent excess underlining in nroff
-.if n .fp 2 R
-.OH 'Remote Procedure Calls: Protocol Specification''Page %'
-.EH 'Page %''Remote Procedure Calls: Protocol Specification'
-.if \\n%=1 .bp
-.SH
-\&Remote Procedure Calls: Protocol Specification
-.LP
-.NH 0
-\&Status of this Memo
-.LP
-Note: This chapter specifies a protocol that Sun Microsystems, Inc.,
-and others are using.
-It has been designated RFC1050 by the ARPA Network
-Information Center.
-.LP
-.NH 1
-\&Introduction
-.LP
-This chapter specifies a message protocol used in implementing
-Sun's Remote Procedure Call (RPC) package. (The message protocol is
-specified with the External Data Representation (XDR) language.
-See the
-.I "External Data Representation Standard: Protocol Specification"
-for the details. Here, we assume that the reader is familiar
-with XDR and do not attempt to justify it or its uses). The paper
-by Birrell and Nelson [1] is recommended as an excellent background
-to and justification of RPC.
-.NH 2
-\&Terminology
-.LP
-This chapter discusses servers, services, programs, procedures,
-clients, and versions. A server is a piece of software where network
-services are implemented. A network service is a collection of one
-or more remote programs. A remote program implements one or more
-remote procedures; the procedures, their parameters, and results are
-documented in the specific program's protocol specification (see the
-\fIPort Mapper Program Protocol\fP\, below, for an example). Network
-clients are pieces of software that initiate remote procedure calls
-to services. A server may support more than one version of a remote
-program in order to be forward compatible with changing protocols.
-.LP
-For example, a network file service may be composed of two programs.
-One program may deal with high-level applications such as file system
-access control and locking. The other may deal with low-level file
-IO and have procedures like "read" and "write". A client machine of
-the network file service would call the procedures associated with
-the two programs of the service on behalf of some user on the client
-machine.
-.NH 2
-\&The RPC Model
-.LP
-The remote procedure call model is similar to the local procedure
-call model. In the local case, the caller places arguments to a
-procedure in some well-specified location (such as a result
-register). It then transfers control to the procedure, and
-eventually gains back control. At that point, the results of the
-procedure are extracted from the well-specified location, and the
-caller continues execution.
-.LP
-The remote procedure call is similar, in that one thread of control
-logically winds through two processes\(emone is the caller's process,
-the other is a server's process. That is, the caller process sends a
-call message to the server process and waits (blocks) for a reply
-message. The call message contains the procedure's parameters, among
-other things. The reply message contains the procedure's results,
-among other things. Once the reply message is received, the results
-of the procedure are extracted, and caller's execution is resumed.
-.LP
-On the server side, a process is dormant awaiting the arrival of a
-call message. When one arrives, the server process extracts the
-procedure's parameters, computes the results, sends a reply message,
-and then awaits the next call message.
-.LP
-Note that in this model, only one of the two processes is active at
-any given time. However, this model is only given as an example.
-The RPC protocol makes no restrictions on the concurrency model
-implemented, and others are possible. For example, an implementation
-may choose to have RPC calls be asynchronous, so that the client may
-do useful work while waiting for the reply from the server. Another
-possibility is to have the server create a task to process an
-incoming request, so that the server can be free to receive other
-requests.
-.NH 2
-\&Transports and Semantics
-.LP
-The RPC protocol is independent of transport protocols. That is, RPC
-does not care how a message is passed from one process to another.
-The protocol deals only with specification and interpretation of
-messages.
-.LP
-It is important to point out that RPC does not try to implement any
-kind of reliability and that the application must be aware of the
-type of transport protocol underneath RPC. If it knows it is running
-on top of a reliable transport such as TCP/IP[6], then most of the
-work is already done for it. On the other hand, if it is running on
-top of an unreliable transport such as UDP/IP[7], it must implement
-is own retransmission and time-out policy as the RPC layer does not
-provide this service.
-.LP
-Because of transport independence, the RPC protocol does not attach
-specific semantics to the remote procedures or their execution.
-Semantics can be inferred from (but should be explicitly specified
-by) the underlying transport protocol. For example, consider RPC
-running on top of an unreliable transport such as UDP/IP. If an
-application retransmits RPC messages after short time-outs, the only
-thing it can infer if it receives no reply is that the procedure was
-executed zero or more times. If it does receive a reply, then it can
-infer that the procedure was executed at least once.
-.LP
-A server may wish to remember previously granted requests from a
-client and not regrant them in order to insure some degree of
-execute-at-most-once semantics. A server can do this by taking
-advantage of the transaction ID that is packaged with every RPC
-request. The main use of this transaction is by the client RPC layer
-in matching replies to requests. However, a client application may
-choose to reuse its previous transaction ID when retransmitting a
-request. The server application, knowing this fact, may choose to
-remember this ID after granting a request and not regrant requests
-with the same ID in order to achieve some degree of
-execute-at-most-once semantics. The server is not allowed to examine
-this ID in any other way except as a test for equality.
-.LP
-On the other hand, if using a reliable transport such as TCP/IP, the
-application can infer from a reply message that the procedure was
-executed exactly once, but if it receives no reply message, it cannot
-assume the remote procedure was not executed. Note that even if a
-connection-oriented protocol like TCP is used, an application still
-needs time-outs and reconnection to handle server crashes.
-.LP
-There are other possibilities for transports besides datagram- or
-connection-oriented protocols. For example, a request-reply protocol
-such as VMTP[2] is perhaps the most natural transport for RPC.
-.SH
-.I
-NOTE: At Sun, RPC is currently implemented on top of both TCP/IP
-and UDP/IP transports.
-.LP
-.NH 2
-\&Binding and Rendezvous Independence
-.LP
-The act of binding a client to a service is NOT part of the remote
-procedure call specification. This important and necessary function
-is left up to some higher-level software. (The software may use RPC
-itself\(emsee the \fIPort Mapper Program Protocol\fP\, below).
-.LP
-Implementors should think of the RPC protocol as the jump-subroutine
-instruction ("JSR") of a network; the loader (binder) makes JSR
-useful, and the loader itself uses JSR to accomplish its task.
-Likewise, the network makes RPC useful, using RPC to accomplish this
-task.
-.NH 2
-\&Authentication
-.LP
-The RPC protocol provides the fields necessary for a client to
-identify itself to a service and vice-versa. Security and access
-control mechanisms can be built on top of the message authentication.
-Several different authentication protocols can be supported. A field
-in the RPC header indicates which protocol is being used. More
-information on specific authentication protocols can be found in the
-\fIAuthentication Protocols\fP\,
-below.
-.KS
-.NH 1
-\&RPC Protocol Requirements
-.LP
-The RPC protocol must provide for the following:
-.IP 1.
-Unique specification of a procedure to be called.
-.IP 2.
-Provisions for matching response messages to request messages.
-.KE
-.IP 3.
-Provisions for authenticating the caller to service and vice-versa.
-.LP
-Besides these requirements, features that detect the following are
-worth supporting because of protocol roll-over errors, implementation
-bugs, user error, and network administration:
-.IP 1.
-RPC protocol mismatches.
-.IP 2.
-Remote program protocol version mismatches.
-.IP 3.
-Protocol errors (such as misspecification of a procedure's parameters).
-.IP 4.
-Reasons why remote authentication failed.
-.IP 5.
-Any other reasons why the desired procedure was not called.
-.NH 2
-\&Programs and Procedures
-.LP
-The RPC call message has three unsigned fields: remote program
-number, remote program version number, and remote procedure number.
-The three fields uniquely identify the procedure to be called.
-Program numbers are administered by some central authority (like
-Sun). Once an implementor has a program number, he can implement his
-remote program; the first implementation would most likely have the
-version number of 1. Because most new protocols evolve into better,
-stable, and mature protocols, a version field of the call message
-identifies which version of the protocol the caller is using.
-Version numbers make speaking old and new protocols through the same
-server process possible.
-.LP
-The procedure number identifies the procedure to be called. These
-numbers are documented in the specific program's protocol
-specification. For example, a file service's protocol specification
-may state that its procedure number 5 is "read" and procedure number
-12 is "write".
-.LP
-Just as remote program protocols may change over several versions,
-the actual RPC message protocol could also change. Therefore, the
-call message also has in it the RPC version number, which is always
-equal to two for the version of RPC described here.
-.LP
-The reply message to a request message has enough information to
-distinguish the following error conditions:
-.IP 1.
-The remote implementation of RPC does speak protocol version 2.
-The lowest and highest supported RPC version numbers are returned.
-.IP 2.
-The remote program is not available on the remote system.
-.IP 3.
-The remote program does not support the requested version number.
-The lowest and highest supported remote program version numbers are
-returned.
-.IP 4.
-The requested procedure number does not exist. (This is usually a
-caller side protocol or programming error.)
-.IP 5.
-The parameters to the remote procedure appear to be garbage from the
-server's point of view. (Again, this is usually caused by a
-disagreement about the protocol between client and service.)
-.NH 2
-\&Authentication
-.LP
-Provisions for authentication of caller to service and vice-versa are
-provided as a part of the RPC protocol. The call message has two
-authentication fields, the credentials and verifier. The reply
-message has one authentication field, the response verifier. The RPC
-protocol specification defines all three fields to be the following
-opaque type:
-.DS
-.ft CW
-.vs 11
-enum auth_flavor {
- AUTH_NULL = 0,
- AUTH_UNIX = 1,
- AUTH_SHORT = 2,
- AUTH_DES = 3
- /* \fIand more to be defined\fP */
-};
-
-struct opaque_auth {
- auth_flavor flavor;
- opaque body<400>;
-};
-.DE
-.LP
-In simple English, any
-.I opaque_auth
-structure is an
-.I auth_flavor
-enumeration followed by bytes which are opaque to the RPC protocol
-implementation.
-.LP
-The interpretation and semantics of the data contained within the
-authentication fields is specified by individual, independent
-authentication protocol specifications. (See
-\fIAuthentication Protocols\fP\,
-below, for definitions of the various authentication protocols.)
-.LP
-If authentication parameters were rejected, the response message
-contains information stating why they were rejected.
-.NH 2
-\&Program Number Assignment
-.LP
-Program numbers are given out in groups of
-.I 0x20000000
-(decimal 536870912) according to the following chart:
-.TS
-box tab (&) ;
-lfI lfI
-rfL cfI .
-Program Numbers&Description
-_
-.sp .5
-0 - 1fffffff&Defined by Sun
-20000000 - 3fffffff&Defined by user
-40000000 - 5fffffff&Transient
-60000000 - 7fffffff&Reserved
-80000000 - 9fffffff&Reserved
-a0000000 - bfffffff&Reserved
-c0000000 - dfffffff&Reserved
-e0000000 - ffffffff&Reserved
-.TE
-.LP
-The first group is a range of numbers administered by Sun
-Microsystems and should be identical for all sites. The second range
-is for applications peculiar to a particular site. This range is
-intended primarily for debugging new programs. When a site develops
-an application that might be of general interest, that application
-should be given an assigned number in the first range. The third
-group is for applications that generate program numbers dynamically.
-The final groups are reserved for future use, and should not be used.
-.NH 2
-\&Other Uses of the RPC Protocol
-.LP
-The intended use of this protocol is for calling remote procedures.
-That is, each call message is matched with a response message.
-However, the protocol itself is a message-passing protocol with which
-other (non-RPC) protocols can be implemented. Sun currently uses, or
-perhaps abuses, the RPC message protocol for the following two
-(non-RPC) protocols: batching (or pipelining) and broadcast RPC.
-These two protocols are discussed but not defined below.
-.NH 3
-\&Batching
-.LP
-Batching allows a client to send an arbitrarily large sequence of
-call messages to a server; batching typically uses reliable byte
-stream protocols (like TCP/IP) for its transport. In the case of
-batching, the client never waits for a reply from the server, and the
-server does not send replies to batch requests. A sequence of batch
-calls is usually terminated by a legitimate RPC in order to flush the
-pipeline (with positive acknowledgement).
-.NH 3
-\&Broadcast RPC
-.LP
-In broadcast RPC-based protocols, the client sends a broadcast packet
-to the network and waits for numerous replies. Broadcast RPC uses
-unreliable, packet-based protocols (like UDP/IP) as its transports.
-Servers that support broadcast protocols only respond when the
-request is successfully processed, and are silent in the face of
-errors. Broadcast RPC uses the Port Mapper RPC service to achieve
-its semantics. See the \fIPort Mapper Program Protocol\fP\, below,
-for more information.
-.KS
-.NH 1
-\&The RPC Message Protocol
-.LP
-This section defines the RPC message protocol in the XDR data
-description language. The message is defined in a top-down style.
-.ie t .DS
-.el .DS L
-.ft CW
-enum msg_type {
- CALL = 0,
- REPLY = 1
-};
-
-.ft I
-/*
-* A reply to a call message can take on two forms:
-* The message was either accepted or rejected.
-*/
-.ft CW
-enum reply_stat {
- MSG_ACCEPTED = 0,
- MSG_DENIED = 1
-};
-
-.ft I
-/*
-* Given that a call message was accepted, the following is the
-* status of an attempt to call a remote procedure.
-*/
-.ft CW
-enum accept_stat {
- SUCCESS = 0, /* \fIRPC executed successfully \fP*/
- PROG_UNAVAIL = 1, /* \fIremote hasn't exported program \fP*/
- PROG_MISMATCH = 2, /* \fIremote can't support version # \fP*/
- PROC_UNAVAIL = 3, /* \fIprogram can't support procedure \fP*/
- GARBAGE_ARGS = 4 /* \fIprocedure can't decode params \fP*/
-};
-.DE
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* Reasons why a call message was rejected:
-*/
-.ft CW
-enum reject_stat {
- RPC_MISMATCH = 0, /* \fIRPC version number != 2 \fP*/
- AUTH_ERROR = 1 /* \fIremote can't authenticate caller \fP*/
-};
-
-.ft I
-/*
-* Why authentication failed:
-*/
-.ft CW
-enum auth_stat {
- AUTH_BADCRED = 1, /* \fIbad credentials \fP*/
- AUTH_REJECTEDCRED = 2, /* \fIclient must begin new session \fP*/
- AUTH_BADVERF = 3, /* \fIbad verifier \fP*/
- AUTH_REJECTEDVERF = 4, /* \fIverifier expired or replayed \fP*/
- AUTH_TOOWEAK = 5 /* \fIrejected for security reasons \fP*/
-};
-.DE
-.KE
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* The RPC message:
-* All messages start with a transaction identifier, xid,
-* followed by a two-armed discriminated union. The union's
-* discriminant is a msg_type which switches to one of the two
-* types of the message. The xid of a \fIREPLY\fP message always
-* matches that of the initiating \fICALL\fP message. NB: The xid
-* field is only used for clients matching reply messages with
-* call messages or for servers detecting retransmissions; the
-* service side cannot treat this id as any type of sequence
-* number.
-*/
-.ft CW
-struct rpc_msg {
- unsigned int xid;
- union switch (msg_type mtype) {
- case CALL:
- call_body cbody;
- case REPLY:
- reply_body rbody;
- } body;
-};
-.DE
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* Body of an RPC request call:
-* In version 2 of the RPC protocol specification, rpcvers must
-* be equal to 2. The fields prog, vers, and proc specify the
-* remote program, its version number, and the procedure within
-* the remote program to be called. After these fields are two
-* authentication parameters: cred (authentication credentials)
-* and verf (authentication verifier). The two authentication
-* parameters are followed by the parameters to the remote
-* procedure, which are specified by the specific program
-* protocol.
-*/
-.ft CW
-struct call_body {
- unsigned int rpcvers; /* \fImust be equal to two (2) \fP*/
- unsigned int prog;
- unsigned int vers;
- unsigned int proc;
- opaque_auth cred;
- opaque_auth verf;
- /* \fIprocedure specific parameters start here \fP*/
-};
-.DE
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* Body of a reply to an RPC request:
-* The call message was either accepted or rejected.
-*/
-.ft CW
-union reply_body switch (reply_stat stat) {
- case MSG_ACCEPTED:
- accepted_reply areply;
- case MSG_DENIED:
- rejected_reply rreply;
-} reply;
-.DE
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* Reply to an RPC request that was accepted by the server:
-* there could be an error even though the request was accepted.
-* The first field is an authentication verifier that the server
-* generates in order to validate itself to the caller. It is
-* followed by a union whose discriminant is an enum
-* accept_stat. The \fISUCCESS\fP arm of the union is protocol
-* specific. The \fIPROG_UNAVAIL\fP, \fIPROC_UNAVAIL\fP, and \fIGARBAGE_ARGP\fP
-* arms of the union are void. The \fIPROG_MISMATCH\fP arm specifies
-* the lowest and highest version numbers of the remote program
-* supported by the server.
-*/
-.ft CW
-struct accepted_reply {
- opaque_auth verf;
- union switch (accept_stat stat) {
- case SUCCESS:
- opaque results[0];
- /* \fIprocedure-specific results start here\fP */
- case PROG_MISMATCH:
- struct {
- unsigned int low;
- unsigned int high;
- } mismatch_info;
- default:
-.ft I
- /*
- * Void. Cases include \fIPROG_UNAVAIL, PROC_UNAVAIL\fP,
- * and \fIGARBAGE_ARGS\fP.
- */
-.ft CW
- void;
- } reply_data;
-};
-.DE
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* Reply to an RPC request that was rejected by the server:
-* The request can be rejected for two reasons: either the
-* server is not running a compatible version of the RPC
-* protocol (\fIRPC_MISMATCH\fP), or the server refuses to
-* authenticate the caller (\fIAUTH_ERROR\fP). In case of an RPC
-* version mismatch, the server returns the lowest and highest
-* supported RPC version numbers. In case of refused
-* authentication, failure status is returned.
-*/
-.ft CW
-union rejected_reply switch (reject_stat stat) {
- case RPC_MISMATCH:
- struct {
- unsigned int low;
- unsigned int high;
- } mismatch_info;
- case AUTH_ERROR:
- auth_stat stat;
-};
-.DE
-.NH 1
-\&Authentication Protocols
-.LP
-As previously stated, authentication parameters are opaque, but
-open-ended to the rest of the RPC protocol. This section defines
-some "flavors" of authentication implemented at (and supported by)
-Sun. Other sites are free to invent new authentication types, with
-the same rules of flavor number assignment as there is for program
-number assignment.
-.NH 2
-\&Null Authentication
-.LP
-Often calls must be made where the caller does not know who he is or
-the server does not care who the caller is. In this case, the flavor
-value (the discriminant of the \fIopaque_auth\fP's union) of the RPC
-message's credentials, verifier, and response verifier is
-.I AUTH_NULL .
-The bytes of the opaque_auth's body are undefined.
-It is recommended that the opaque length be zero.
-.NH 2
-\&UNIX Authentication
-.LP
-The caller of a remote procedure may wish to identify himself as he
-is identified on a UNIX system. The value of the credential's
-discriminant of an RPC call message is
-.I AUTH_UNIX .
-The bytes of
-the credential's opaque body encode the following structure:
-.DS
-.ft CW
-struct auth_unix {
- unsigned int stamp;
- string machinename<255>;
- unsigned int uid;
- unsigned int gid;
- unsigned int gids<10>;
-};
-.DE
-The
-.I stamp
-is an arbitrary ID which the caller machine may
-generate. The
-.I machinename
-is the name of the caller's machine (like "krypton"). The
-.I uid
-is the caller's effective user ID. The
-.I gid
-is the caller's effective group ID. The
-.I gids
-is a
-counted array of groups which contain the caller as a member. The
-verifier accompanying the credentials should be of
-.I AUTH_NULL
-(defined above).
-.LP
-The value of the discriminant of the response verifier received in
-the reply message from the server may be
-.I AUTH_NULL
-or
-.I AUTH_SHORT .
-In the case of
-.I AUTH_SHORT ,
-the bytes of the response verifier's string encode an opaque
-structure. This new opaque structure may now be passed to the server
-instead of the original
-.I AUTH_UNIX
-flavor credentials. The server keeps a cache which maps shorthand
-opaque structures (passed back by way of an
-.I AUTH_SHORT
-style response verifier) to the original credentials of the caller.
-The caller can save network bandwidth and server cpu cycles by using
-the new credentials.
-.LP
-The server may flush the shorthand opaque structure at any time. If
-this happens, the remote procedure call message will be rejected due
-to an authentication error. The reason for the failure will be
-.I AUTH_REJECTEDCRED .
-At this point, the caller may wish to try the original
-.I AUTH_UNIX
-style of credentials.
-.KS
-.NH 2
-\&DES Authentication
-.LP
-UNIX authentication suffers from two major problems:
-.IP 1.
-The naming is too UNIX-system oriented.
-.IP 2.
-There is no verifier, so credentials can easily be faked.
-.LP
-DES authentication attempts to fix these two problems.
-.KE
-.NH 3
-\&Naming
-.LP
-The first problem is handled by addressing the caller by a simple
-string of characters instead of by an operating system specific
-integer. This string of characters is known as the "netname" or
-network name of the caller. The server is not allowed to interpret
-the contents of the caller's name in any other way except to
-identify the caller. Thus, netnames should be unique for every
-caller in the internet.
-.LP
-It is up to each operating system's implementation of DES
-authentication to generate netnames for its users that insure this
-uniqueness when they call upon remote servers. Operating systems
-already know how to distinguish users local to their systems. It is
-usually a simple matter to extend this mechanism to the network.
-For example, a UNIX user at Sun with a user ID of 515 might be
-assigned the following netname: "unix.515@sun.com". This netname
-contains three items that serve to insure it is unique. Going
-backwards, there is only one naming domain called "sun.com" in the
-internet. Within this domain, there is only one UNIX user with
-user ID 515. However, there may be another user on another
-operating system, for example VMS, within the same naming domain
-that, by coincidence, happens to have the same user ID. To insure
-that these two users can be distinguished we add the operating
-system name. So one user is "unix.515@sun.com" and the other is
-"vms.515@sun.com".
-.LP
-The first field is actually a naming method rather than an
-operating system name. It just happens that today there is almost
-a one-to-one correspondence between naming methods and operating
-systems. If the world could agree on a naming standard, the first
-field could be the name of that standard, instead of an operating
-system name.
-.LP
-.NH 3
-\&DES Authentication Verifiers
-.LP
-Unlike UNIX authentication, DES authentication does have a verifier
-so the server can validate the client's credential (and
-vice-versa). The contents of this verifier is primarily an
-encrypted timestamp. The server can decrypt this timestamp, and if
-it is close to what the real time is, then the client must have
-encrypted it correctly. The only way the client could encrypt it
-correctly is to know the "conversation key" of the RPC session. And
-if the client knows the conversation key, then it must be the real
-client.
-.LP
-The conversation key is a DES [5] key which the client generates
-and notifies the server of in its first RPC call. The conversation
-key is encrypted using a public key scheme in this first
-transaction. The particular public key scheme used in DES
-authentication is Diffie-Hellman [3] with 192-bit keys. The
-details of this encryption method are described later.
-.LP
-The client and the server need the same notion of the current time
-in order for all of this to work. If network time synchronization
-cannot be guaranteed, then client can synchronize with the server
-before beginning the conversation, perhaps by consulting the
-Internet Time Server (TIME[4]).
-.LP
-The way a server determines if a client timestamp is valid is
-somewhat complicated. For any other transaction but the first, the
-server just checks for two things:
-.IP 1.
-the timestamp is greater than the one previously seen from the
-same client.
-.IP 2.
-the timestamp has not expired.
-.LP
-A timestamp is expired if the server's time is later than the sum
-of the client's timestamp plus what is known as the client's
-"window". The "window" is a number the client passes (encrypted)
-to the server in its first transaction. You can think of it as a
-lifetime for the credential.
-.LP
-This explains everything but the first transaction. In the first
-transaction, the server checks only that the timestamp has not
-expired. If this was all that was done though, then it would be
-quite easy for the client to send random data in place of the
-timestamp with a fairly good chance of succeeding. As an added
-check, the client sends an encrypted item in the first transaction
-known as the "window verifier" which must be equal to the window
-minus 1, or the server will reject the credential.
-.LP
-The client too must check the verifier returned from the server to
-be sure it is legitimate. The server sends back to the client the
-encrypted timestamp it received from the client, minus one second.
-If the client gets anything different than this, it will reject it.
-.LP
-.NH 3
-\&Nicknames and Clock Synchronization
-.LP
-After the first transaction, the server's DES authentication
-subsystem returns in its verifier to the client an integer
-"nickname" which the client may use in its further transactions
-instead of passing its netname, encrypted DES key and window every
-time. The nickname is most likely an index into a table on the
-server which stores for each client its netname, decrypted DES key
-and window.
-.LP
-Though they originally were synchronized, the client's and server's
-clocks can get out of sync again. When this happens the client RPC
-subsystem most likely will get back
-.I RPC_AUTHERROR
-at which point it should resynchronize.
-.LP
-A client may still get the
-.I RPC_AUTHERROR
-error even though it is
-synchronized with the server. The reason is that the server's
-nickname table is a limited size, and it may flush entries whenever
-it wants. A client should resend its original credential in this
-case and the server will give it a new nickname. If a server
-crashes, the entire nickname table gets flushed, and all clients
-will have to resend their original credentials.
-.KS
-.NH 3
-\&DES Authentication Protocol (in XDR language)
-.ie t .DS
-.el .DS L
-.ft I
-/*
-* There are two kinds of credentials: one in which the client uses
-* its full network name, and one in which it uses its "nickname"
-* (just an unsigned integer) given to it by the server. The
-* client must use its fullname in its first transaction with the
-* server, in which the server will return to the client its
-* nickname. The client may use its nickname in all further
-* transactions with the server. There is no requirement to use the
-* nickname, but it is wise to use it for performance reasons.
-*/
-.ft CW
-enum authdes_namekind {
- ADN_FULLNAME = 0,
- ADN_NICKNAME = 1
-};
-
-.ft I
-/*
-* A 64-bit block of encrypted DES data
-*/
-.ft CW
-typedef opaque des_block[8];
-
-.ft I
-/*
-* Maximum length of a network user's name
-*/
-.ft CW
-const MAXNETNAMELEN = 255;
-
-.ft I
-/*
-* A fullname contains the network name of the client, an encrypted
-* conversation key and the window. The window is actually a
-* lifetime for the credential. If the time indicated in the
-* verifier timestamp plus the window has past, then the server
-* should expire the request and not grant it. To insure that
-* requests are not replayed, the server should insist that
-* timestamps are greater than the previous one seen, unless it is
-* the first transaction. In the first transaction, the server
-* checks instead that the window verifier is one less than the
-* window.
-*/
-.ft CW
-struct authdes_fullname {
-string name<MAXNETNAMELEN>; /* \fIname of client \f(CW*/
-des_block key; /* \fIPK encrypted conversation key \f(CW*/
-unsigned int window; /* \fIencrypted window \f(CW*/
-};
-
-.ft I
-/*
-* A credential is either a fullname or a nickname
-*/
-.ft CW
-union authdes_cred switch (authdes_namekind adc_namekind) {
- case ADN_FULLNAME:
- authdes_fullname adc_fullname;
- case ADN_NICKNAME:
- unsigned int adc_nickname;
-};
-
-.ft I
-/*
-* A timestamp encodes the time since midnight, January 1, 1970.
-*/
-.ft CW
-struct timestamp {
- unsigned int seconds; /* \fIseconds \fP*/
- unsigned int useconds; /* \fIand microseconds \fP*/
-};
-
-.ft I
-/*
-* Verifier: client variety
-* The window verifier is only used in the first transaction. In
-* conjunction with a fullname credential, these items are packed
-* into the following structure before being encrypted:
-*
-* \f(CWstruct {\fP
-* \f(CWadv_timestamp; \fP-- one DES block
-* \f(CWadc_fullname.window; \fP-- one half DES block
-* \f(CWadv_winverf; \fP-- one half DES block
-* \f(CW}\fP
-* This structure is encrypted using CBC mode encryption with an
-* input vector of zero. All other encryptions of timestamps use
-* ECB mode encryption.
-*/
-.ft CW
-struct authdes_verf_clnt {
- timestamp adv_timestamp; /* \fIencrypted timestamp \fP*/
- unsigned int adv_winverf; /* \fIencrypted window verifier \fP*/
-};
-
-.ft I
-/*
-* Verifier: server variety
-* The server returns (encrypted) the same timestamp the client
-* gave it minus one second. It also tells the client its nickname
-* to be used in future transactions (unencrypted).
-*/
-.ft CW
-struct authdes_verf_svr {
-timestamp adv_timeverf; /* \fIencrypted verifier \fP*/
-unsigned int adv_nickname; /* \fInew nickname for client \fP*/
-};
-.DE
-.KE
-.NH 3
-\&Diffie-Hellman Encryption
-.LP
-In this scheme, there are two constants,
-.I BASE
-and
-.I MODULUS .
-The
-particular values Sun has chosen for these for the DES
-authentication protocol are:
-.ie t .DS
-.el .DS L
-.ft CW
-const BASE = 3;
-const MODULUS =
- "d4a0ba0250b6fd2ec626e7efd637df76c716e22d0944b88b"; /* \fIhex \fP*/
-.DE
-.ft R
-The way this scheme works is best explained by an example. Suppose
-there are two people "A" and "B" who want to send encrypted
-messages to each other. So, A and B both generate "secret" keys at
-random which they do not reveal to anyone. Let these keys be
-represented as SK(A) and SK(B). They also publish in a public
-directory their "public" keys. These keys are computed as follows:
-.ie t .DS
-.el .DS L
-.ft CW
-PK(A) = ( BASE ** SK(A) ) mod MODULUS
-PK(B) = ( BASE ** SK(B) ) mod MODULUS
-.DE
-.ft R
-The "**" notation is used here to represent exponentiation. Now,
-both A and B can arrive at the "common" key between them,
-represented here as CK(A, B), without revealing their secret keys.
-.LP
-A computes:
-.ie t .DS
-.el .DS L
-.ft CW
-CK(A, B) = ( PK(B) ** SK(A)) mod MODULUS
-.DE
-.ft R
-while B computes:
-.ie t .DS
-.el .DS L
-.ft CW
-CK(A, B) = ( PK(A) ** SK(B)) mod MODULUS
-.DE
-.ft R
-These two can be shown to be equivalent:
-.ie t .DS
-.el .DS L
-.ft CW
-(PK(B) ** SK(A)) mod MODULUS = (PK(A) ** SK(B)) mod MODULUS
-.DE
-.ft R
-We drop the "mod MODULUS" parts and assume modulo arithmetic to
-simplify things:
-.ie t .DS
-.el .DS L
-.ft CW
-PK(B) ** SK(A) = PK(A) ** SK(B)
-.DE
-.ft R
-Then, replace PK(B) by what B computed earlier and likewise for
-PK(A).
-.ie t .DS
-.el .DS L
-.ft CW
-((BASE ** SK(B)) ** SK(A) = (BASE ** SK(A)) ** SK(B)
-.DE
-.ft R
-which leads to:
-.ie t .DS
-.el .DS L
-.ft CW
-BASE ** (SK(A) * SK(B)) = BASE ** (SK(A) * SK(B))
-.DE
-.ft R
-This common key CK(A, B) is not used to encrypt the timestamps used
-in the protocol. Rather, it is used only to encrypt a conversation
-key which is then used to encrypt the timestamps. The reason for
-doing this is to use the common key as little as possible, for fear
-that it could be broken. Breaking the conversation key is a far
-less serious offense, since conversations are relatively
-short-lived.
-.LP
-The conversation key is encrypted using 56-bit DES keys, yet the
-common key is 192 bits. To reduce the number of bits, 56 bits are
-selected from the common key as follows. The middle-most 8-bytes
-are selected from the common key, and then parity is added to the
-lower order bit of each byte, producing a 56-bit key with 8 bits of
-parity.
-.KS
-.NH 1
-\&Record Marking Standard
-.LP
-When RPC messages are passed on top of a byte stream protocol (like
-TCP/IP), it is necessary, or at least desirable, to delimit one
-message from another in order to detect and possibly recover from
-user protocol errors. This is called record marking (RM). Sun uses
-this RM/TCP/IP transport for passing RPC messages on TCP streams.
-One RPC message fits into one RM record.
-.LP
-A record is composed of one or more record fragments. A record
-fragment is a four-byte header followed by 0 to (2**31) - 1 bytes of
-fragment data. The bytes encode an unsigned binary number; as with
-XDR integers, the byte order is from highest to lowest. The number
-encodes two values\(ema boolean which indicates whether the fragment
-is the last fragment of the record (bit value 1 implies the fragment
-is the last fragment) and a 31-bit unsigned binary value which is the
-length in bytes of the fragment's data. The boolean value is the
-highest-order bit of the header; the length is the 31 low-order bits.
-(Note that this record specification is NOT in XDR standard form!)
-.KE
-.KS
-.NH 1
-\&The RPC Language
-.LP
-Just as there was a need to describe the XDR data-types in a formal
-language, there is also need to describe the procedures that operate
-on these XDR data-types in a formal language as well. We use the RPC
-Language for this purpose. It is an extension to the XDR language.
-The following example is used to describe the essence of the
-language.
-.NH 2
-\&An Example Service Described in the RPC Language
-.LP
-Here is an example of the specification of a simple ping program.
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
-* Simple ping program
-*/
-.ft CW
-program PING_PROG {
- /* \fILatest and greatest version\fP */
- version PING_VERS_PINGBACK {
- void
- PINGPROC_NULL(void) = 0;
-
-.ft I
- /*
- * Ping the caller, return the round-trip time
- * (in microseconds). Returns -1 if the operation
- * timed out.
- */
-.ft CW
- int
- PINGPROC_PINGBACK(void) = 1;
-} = 2;
-
-.ft I
-/*
-* Original version
-*/
-.ft CW
-version PING_VERS_ORIG {
- void
- PINGPROC_NULL(void) = 0;
- } = 1;
-} = 1;
-
-const PING_VERS = 2; /* \fIlatest version \fP*/
-.vs
-.DE
-.KE
-.LP
-The first version described is
-.I PING_VERS_PINGBACK
-with two procedures,
-.I PINGPROC_NULL
-and
-.I PINGPROC_PINGBACK .
-.I PINGPROC_NULL
-takes no arguments and returns no results, but it is useful for
-computing round-trip times from the client to the server and back
-again. By convention, procedure 0 of any RPC protocol should have
-the same semantics, and never require any kind of authentication.
-The second procedure is used for the client to have the server do a
-reverse ping operation back to the client, and it returns the amount
-of time (in microseconds) that the operation used. The next version,
-.I PING_VERS_ORIG ,
-is the original version of the protocol
-and it does not contain
-.I PINGPROC_PINGBACK
-procedure. It is useful
-for compatibility with old client programs, and as this program
-matures it may be dropped from the protocol entirely.
-.KS
-.NH 2
-\&The RPC Language Specification
-.LP
-The RPC language is identical to the XDR language, except for the
-added definition of a
-.I program-def
-described below.
-.DS
-.ft CW
-program-def:
- "program" identifier "{"
- version-def
- version-def *
- "}" "=" constant ";"
-
-version-def:
- "version" identifier "{"
- procedure-def
- procedure-def *
- "}" "=" constant ";"
-
-procedure-def:
- type-specifier identifier "(" type-specifier ")"
- "=" constant ";"
-.DE
-.KE
-.NH 2
-\&Syntax Notes
-.IP 1.
-The following keywords are added and cannot be used as
-identifiers: "program" and "version";
-.IP 2.
-A version name cannot occur more than once within the scope of
-a program definition. Nor can a version number occur more than once
-within the scope of a program definition.
-.IP 3.
-A procedure name cannot occur more than once within the scope
-of a version definition. Nor can a procedure number occur more than
-once within the scope of version definition.
-.IP 4.
-Program identifiers are in the same name space as constant and
-type identifiers.
-.IP 5.
-Only unsigned constants can be assigned to programs, versions
-and procedures.
-.NH 1
-\&Port Mapper Program Protocol
-.LP
-The port mapper program maps RPC program and version numbers to
-transport-specific port numbers. This program makes dynamic binding
-of remote programs possible.
-.LP
-This is desirable because the range of reserved port numbers is very
-small and the number of potential remote programs is very large. By
-running only the port mapper on a reserved port, the port numbers of
-other remote programs can be ascertained by querying the port mapper.
-.LP
-The port mapper also aids in broadcast RPC. A given RPC program will
-usually have different port number bindings on different machines, so
-there is no way to directly broadcast to all of these programs. The
-port mapper, however, does have a fixed port number. So, to
-broadcast to a given program, the client actually sends its message
-to the port mapper located at the broadcast address. Each port
-mapper that picks up the broadcast then calls the local service
-specified by the client. When the port mapper gets the reply from
-the local service, it sends the reply on back to the client.
-.KS
-.NH 2
-\&Port Mapper Protocol Specification (in RPC Language)
-.ie t .DS
-.el .DS L
-.ft CW
-.vs 11
-const PMAP_PORT = 111; /* \fIportmapper port number \fP*/
-
-.ft I
-/*
-* A mapping of (program, version, protocol) to port number
-*/
-.ft CW
-struct mapping {
- unsigned int prog;
- unsigned int vers;
- unsigned int prot;
- unsigned int port;
-};
-
-.ft I
-/*
-* Supported values for the "prot" field
-*/
-.ft CW
-const IPPROTO_TCP = 6; /* \fIprotocol number for TCP/IP \fP*/
-const IPPROTO_UDP = 17; /* \fIprotocol number for UDP/IP \fP*/
-
-.ft I
-/*
-* A list of mappings
-*/
-.ft CW
-struct *pmaplist {
- mapping map;
- pmaplist next;
-};
-.vs
-.DE
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
-* Arguments to callit
-*/
-.ft CW
-struct call_args {
- unsigned int prog;
- unsigned int vers;
- unsigned int proc;
- opaque args<>;
-};
-
-.ft I
-/*
-* Results of callit
-*/
-.ft CW
-struct call_result {
- unsigned int port;
- opaque res<>;
-};
-.vs
-.DE
-.KE
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
-* Port mapper procedures
-*/
-.ft CW
-program PMAP_PROG {
- version PMAP_VERS {
- void
- PMAPPROC_NULL(void) = 0;
-
- bool
- PMAPPROC_SET(mapping) = 1;
-
- bool
- PMAPPROC_UNSET(mapping) = 2;
-
- unsigned int
- PMAPPROC_GETPORT(mapping) = 3;
-
- pmaplist
- PMAPPROC_DUMP(void) = 4;
-
- call_result
- PMAPPROC_CALLIT(call_args) = 5;
- } = 2;
-} = 100000;
-.vs
-.DE
-.NH 2
-\&Port Mapper Operation
-.LP
-The portmapper program currently supports two protocols (UDP/IP and
-TCP/IP). The portmapper is contacted by talking to it on assigned
-port number 111 (SUNRPC [8]) on either of these protocols. The
-following is a description of each of the portmapper procedures:
-.IP \fBPMAPPROC_NULL:\fP
-This procedure does no work. By convention, procedure zero of any
-protocol takes no parameters and returns no results.
-.IP \fBPMAPPROC_SET:\fP
-When a program first becomes available on a machine, it registers
-itself with the port mapper program on the same machine. The program
-passes its program number "prog", version number "vers", transport
-protocol number "prot", and the port "port" on which it awaits
-service request. The procedure returns a boolean response whose
-value is
-.I TRUE
-if the procedure successfully established the mapping and
-.I FALSE
-otherwise. The procedure refuses to establish
-a mapping if one already exists for the tuple "(prog, vers, prot)".
-.IP \fBPMAPPROC_UNSET:\fP
-When a program becomes unavailable, it should unregister itself with
-the port mapper program on the same machine. The parameters and
-results have meanings identical to those of
-.I PMAPPROC_SET .
-The protocol and port number fields of the argument are ignored.
-.IP \fBPMAPPROC_GETPORT:\fP
-Given a program number "prog", version number "vers", and transport
-protocol number "prot", this procedure returns the port number on
-which the program is awaiting call requests. A port value of zeros
-means the program has not been registered. The "port" field of the
-argument is ignored.
-.IP \fBPMAPPROC_DUMP:\fP
-This procedure enumerates all entries in the port mapper's database.
-The procedure takes no parameters and returns a list of program,
-version, protocol, and port values.
-.IP \fBPMAPPROC_CALLIT:\fP
-This procedure allows a caller to call another remote procedure on
-the same machine without knowing the remote procedure's port number.
-It is intended for supporting broadcasts to arbitrary remote programs
-via the well-known port mapper's port. The parameters "prog",
-"vers", "proc", and the bytes of "args" are the program number,
-version number, procedure number, and parameters of the remote
-procedure.
-.LP
-.B Note:
-.RS
-.IP 1.
-This procedure only sends a response if the procedure was
-successfully executed and is silent (no response) otherwise.
-.IP 2.
-The port mapper communicates with the remote program using UDP/IP
-only.
-.RE
-.LP
-The procedure returns the remote program's port number, and the bytes
-of results are the results of the remote procedure.
-.bp
-.NH 1
-\&References
-.LP
-[1] Birrell, Andrew D. & Nelson, Bruce Jay; "Implementing Remote
-Procedure Calls"; XEROX CSL-83-7, October 1983.
-.LP
-[2] Cheriton, D.; "VMTP: Versatile Message Transaction Protocol",
-Preliminary Version 0.3; Stanford University, January 1987.
-.LP
-[3] Diffie & Hellman; "New Directions in Cryptography"; IEEE
-Transactions on Information Theory IT-22, November 1976.
-.LP
-[4] Harrenstien, K.; "Time Server", RFC 738; Information Sciences
-Institute, October 1977.
-.LP
-[5] National Bureau of Standards; "Data Encryption Standard"; Federal
-Information Processing Standards Publication 46, January 1977.
-.LP
-[6] Postel, J.; "Transmission Control Protocol - DARPA Internet
-Program Protocol Specification", RFC 793; Information Sciences
-Institute, September 1981.
-.LP
-[7] Postel, J.; "User Datagram Protocol", RFC 768; Information Sciences
-Institute, August 1980.
-.LP
-[8] Reynolds, J. & Postel, J.; "Assigned Numbers", RFC 923; Information
-Sciences Institute, October 1984.
diff --git a/cpukit/librpc/src/rpc/PSD.doc/rpcgen.ms b/cpukit/librpc/src/rpc/PSD.doc/rpcgen.ms
deleted file mode 100644
index b4e50e5d6f..0000000000
--- a/cpukit/librpc/src/rpc/PSD.doc/rpcgen.ms
+++ /dev/null
@@ -1,1299 +0,0 @@
-.\"
-.\" Must use -- tbl -- for this one
-.\"
-.\" @(#)rpcgen.ms 2.2 88/08/04 4.0 RPCSRC
-.de BT
-.if \\n%=1 .tl ''- % -''
-..
-.ND
-.\" prevent excess underlining in nroff
-.if n .fp 2 R
-.OH '\fBrpcgen\fP Programming Guide''Page %'
-.EH 'Page %''\fBrpcgen\fP Programming Guide'
-.if \\n%=1 .bp
-.SH
-\&\fBrpcgen\fP Programming Guide
-.NH 0
-\&The \fBrpcgen\fP Protocol Compiler
-.IX rpcgen "" \fIrpcgen\fP "" PAGE MAJOR
-.LP
-.IX RPC "" "" \fIrpcgen\fP
-The details of programming applications to use Remote Procedure Calls
-can be overwhelming. Perhaps most daunting is the writing of the XDR
-routines necessary to convert procedure arguments and results into
-their network format and vice-versa.
-.LP
-Fortunately,
-.I rpcgen(1)
-exists to help programmers write RPC applications simply and directly.
-.I rpcgen
-does most of the dirty work, allowing programmers to debug
-the main features of their application, instead of requiring them to
-spend most of their time debugging their network interface code.
-.LP
-.I rpcgen
-is a compiler. It accepts a remote program interface definition written
-in a language, called RPC Language, which is similar to C. It produces a C
-language output which includes stub versions of the client routines, a
-server skeleton, XDR filter routines for both parameters and results, and a
-header file that contains common definitions. The client stubs interface
-with the RPC library and effectively hide the network from their callers.
-The server stub similarly hides the network from the server procedures that
-are to be invoked by remote clients.
-.I rpcgen 's
-output files can be compiled and linked in the usual way. The developer
-writes server procedures\(emin any language that observes Sun calling
-conventions\(emand links them with the server skeleton produced by
-.I rpcgen
-to get an executable server program. To use a remote program, a programmer
-writes an ordinary main program that makes local procedure calls to the
-client stubs produced by
-.I rpcgen .
-Linking this program with
-.I rpcgen 's
-stubs creates an executable program. (At present the main program must be
-written in C).
-.I rpcgen
-options can be used to suppress stub generation and to specify the transport
-to be used by the server stub.
-.LP
-Like all compilers,
-.I rpcgen
-reduces development time
-that would otherwise be spent coding and debugging low-level routines.
-All compilers, including
-.I rpcgen ,
-do this at a small cost in efficiency
-and flexibility. However, many compilers allow escape hatches for
-programmers to mix low-level code with high-level code.
-.I rpcgen
-is no exception. In speed-critical applications, hand-written routines
-can be linked with the
-.I rpcgen
-output without any difficulty. Also, one may proceed by using
-.I rpcgen
-output as a starting point, and then rewriting it as necessary.
-(If you need a discussion of RPC programming without
-.I rpcgen ,
-see the
-.I "Remote Procedure Call Programming Guide)\.
-.NH 1
-\&Converting Local Procedures into Remote Procedures
-.IX rpcgen "local procedures" \fIrpcgen\fP
-.IX rpcgen "remote procedures" \fIrpcgen\fP
-.LP
-Assume an application that runs on a single machine, one which we want
-to convert to run over the network. Here we will demonstrate such a
-conversion by way of a simple example\(ema program that prints a
-message to the console:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * printmsg.c: print a message on the console
- */
-.ft CW
-#include <stdio.h>
-
-main(argc, argv)
- int argc;
- char *argv[];
-{
- char *message;
-
- if (argc < 2) {
- fprintf(stderr, "usage: %s <message>\en", argv[0]);
- exit(1);
- }
- message = argv[1];
-
- if (!printmessage(message)) {
- fprintf(stderr, "%s: couldn't print your message\en",
- argv[0]);
- exit(1);
- }
- printf("Message Delivered!\en");
- exit(0);
-}
-.ft I
-/*
- * Print a message to the console.
- * Return a boolean indicating whether the message was actually printed.
- */
-.ft CW
-printmessage(msg)
- char *msg;
-{
- FILE *f;
-
- f = fopen("/dev/console", "w");
- if (f == NULL) {
- return (0);
- }
- fprintf(f, "%s\en", msg);
- fclose(f);
- return(1);
-}
-.DE
-.LP
-And then, of course:
-.ie t .DS
-.el .DS L
-.ft CW
-example% \fBcc printmsg.c -o printmsg\fP
-example% \fBprintmsg "Hello, there."\fP
-Message delivered!
-example%
-.DE
-.LP
-If
-.I printmessage()
-was turned into a remote procedure,
-then it could be called from anywhere in the network.
-Ideally, one would just like to stick a keyword like
-.I remote
-in front of a
-procedure to turn it into a remote procedure. Unfortunately,
-we have to live within the constraints of the C language, since
-it existed long before RPC did. But even without language
-support, it's not very difficult to make a procedure remote.
-.LP
-In general, it's necessary to figure out what the types are for
-all procedure inputs and outputs. In this case, we have a
-procedure
-.I printmessage()
-which takes a string as input, and returns an integer
-as output. Knowing this, we can write a protocol specification in RPC
-language that describes the remote version of
-.I printmessage ().
-Here it is:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * msg.x: Remote message printing protocol
- */
-.ft CW
-
-program MESSAGEPROG {
- version MESSAGEVERS {
- int PRINTMESSAGE(string) = 1;
- } = 1;
-} = 99;
-.DE
-.LP
-Remote procedures are part of remote programs, so we actually declared
-an entire remote program here which contains the single procedure
-.I PRINTMESSAGE .
-This procedure was declared to be in version 1 of the
-remote program. No null procedure (procedure 0) is necessary because
-.I rpcgen
-generates it automatically.
-.LP
-Notice that everything is declared with all capital letters. This is
-not required, but is a good convention to follow.
-.LP
-Notice also that the argument type is \*Qstring\*U and not \*Qchar *\*U. This
-is because a \*Qchar *\*U in C is ambiguous. Programmers usually intend it
-to mean a null-terminated string of characters, but it could also
-represent a pointer to a single character or a pointer to an array of
-characters. In RPC language, a null-terminated string is
-unambiguously called a \*Qstring\*U.
-.LP
-There are just two more things to write. First, there is the remote
-procedure itself. Here's the definition of a remote procedure
-to implement the
-.I PRINTMESSAGE
-procedure we declared above:
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * msg_proc.c: implementation of the remote procedure "printmessage"
- */
-.ft CW
-
-#include <stdio.h>
-#include <rpc/rpc.h> /* \fIalways needed\fP */
-#include "msg.h" /* \fIneed this too: msg.h will be generated by rpcgen\fP */
-
-.ft I
-/*
- * Remote verson of "printmessage"
- */
-.ft CW
-int *
-printmessage_1(msg)
- char **msg;
-{
- static int result; /* \fImust be static!\fP */
- FILE *f;
-
- f = fopen("/dev/console", "w");
- if (f == NULL) {
- result = 0;
- return (&result);
- }
- fprintf(f, "%s\en", *msg);
- fclose(f);
- result = 1;
- return (&result);
-}
-.vs
-.DE
-.LP
-Notice here that the declaration of the remote procedure
-.I printmessage_1()
-differs from that of the local procedure
-.I printmessage()
-in three ways:
-.IP 1.
-It takes a pointer to a string instead of a string itself. This
-is true of all remote procedures: they always take pointers to their
-arguments rather than the arguments themselves.
-.IP 2.
-It returns a pointer to an integer instead of an integer itself. This is
-also generally true of remote procedures: they always return a pointer
-to their results.
-.IP 3.
-It has an \*Q_1\*U appended to its name. In general, all remote
-procedures called by
-.I rpcgen
-are named by the following rule: the name in the program definition
-(here
-.I PRINTMESSAGE )
-is converted to all
-lower-case letters, an underbar (\*Q_\*U) is appended to it, and
-finally the version number (here 1) is appended.
-.LP
-The last thing to do is declare the main client program that will call
-the remote procedure. Here it is:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * rprintmsg.c: remote version of "printmsg.c"
- */
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h> /* \fIalways needed\fP */
-#include "msg.h" /* \fIneed this too: msg.h will be generated by rpcgen\fP */
-
-main(argc, argv)
- int argc;
- char *argv[];
-{
- CLIENT *cl;
- int *result;
- char *server;
- char *message;
-
- if (argc < 3) {
- fprintf(stderr, "usage: %s host message\en", argv[0]);
- exit(1);
- }
-
-.ft I
- /*
- * Save values of command line arguments
- */
-.ft CW
- server = argv[1];
- message = argv[2];
-
-.ft I
- /*
- * Create client "handle" used for calling \fIMESSAGEPROG\fP on the
- * server designated on the command line. We tell the RPC package
- * to use the "tcp" protocol when contacting the server.
- */
-.ft CW
- cl = clnt_create(server, MESSAGEPROG, MESSAGEVERS, "tcp");
- if (cl == NULL) {
-.ft I
- /*
- * Couldn't establish connection with server.
- * Print error message and die.
- */
-.ft CW
- clnt_pcreateerror(server);
- exit(1);
- }
-
-.ft I
- /*
- * Call the remote procedure "printmessage" on the server
- */
-.ft CW
- result = printmessage_1(&message, cl);
- if (result == NULL) {
-.ft I
- /*
- * An error occurred while calling the server.
- * Print error message and die.
- */
-.ft CW
- clnt_perror(cl, server);
- exit(1);
- }
-
-.ft I
- /*
- * Okay, we successfully called the remote procedure.
- */
-.ft CW
- if (*result == 0) {
-.ft I
- /*
- * Server was unable to print our message.
- * Print error message and die.
- */
-.ft CW
- fprintf(stderr, "%s: %s couldn't print your message\en",
- argv[0], server);
- exit(1);
- }
-
-.ft I
- /*
- * The message got printed on the server's console
- */
-.ft CW
- printf("Message delivered to %s!\en", server);
-}
-.DE
-There are two things to note here:
-.IP 1.
-.IX "client handle, used by rpcgen" "" "client handle, used by \fIrpcgen\fP"
-First a client \*Qhandle\*U is created using the RPC library routine
-.I clnt_create ().
-This client handle will be passed to the stub routines
-which call the remote procedure.
-.IP 2.
-The remote procedure
-.I printmessage_1()
-is called exactly the same way as it is declared in
-.I msg_proc.c
-except for the inserted client handle as the first argument.
-.LP
-Here's how to put all of the pieces together:
-.ie t .DS
-.el .DS L
-.ft CW
-example% \fBrpcgen msg.x\fP
-example% \fBcc rprintmsg.c msg_clnt.c -o rprintmsg\fP
-example% \fBcc msg_proc.c msg_svc.c -o msg_server\fP
-.DE
-Two programs were compiled here: the client program
-.I rprintmsg
-and the server program
-.I msg_server .
-Before doing this though,
-.I rpcgen
-was used to fill in the missing pieces.
-.LP
-Here is what
-.I rpcgen
-did with the input file
-.I msg.x :
-.IP 1.
-It created a header file called
-.I msg.h
-that contained
-.I #define 's
-for
-.I MESSAGEPROG ,
-.I MESSAGEVERS
-and
-.I PRINTMESSAGE
-for use in the other modules.
-.IP 2.
-It created client \*Qstub\*U routines in the
-.I msg_clnt.c
-file. In this case there is only one, the
-.I printmessage_1()
-that was referred to from the
-.I printmsg
-client program. The name of the output file for
-client stub routines is always formed in this way: if the name of the
-input file is
-.I FOO.x ,
-the client stubs output file is called
-.I FOO_clnt.c .
-.IP 3.
-It created the server program which calls
-.I printmessage_1()
-in
-.I msg_proc.c .
-This server program is named
-.I msg_svc.c .
-The rule for naming the server output file is similar to the
-previous one: for an input file called
-.I FOO.x ,
-the output server file is named
-.I FOO_svc.c .
-.LP
-Now we're ready to have some fun. First, copy the server to a
-remote machine and run it. For this example, the
-machine is called \*Qmoon\*U. Server processes are run in the
-background, because they never exit.
-.ie t .DS
-.el .DS L
-.ft CW
-moon% \fBmsg_server &\fP
-.DE
-Then on our local machine (\*Qsun\*U) we can print a message on \*Qmoon\*Us
-console.
-.ie t .DS
-.el .DS L
-.ft CW
-sun% \fBprintmsg moon "Hello, moon."\fP
-.DE
-The message will get printed to \*Qmoon\*Us console. You can print a
-message on anybody's console (including your own) with this program if
-you are able to copy the server to their machine and run it.
-.NH 1
-\&Generating XDR Routines
-.IX RPC "generating XDR routines"
-.LP
-The previous example only demonstrated the automatic generation of
-client and server RPC code.
-.I rpcgen
-may also be used to generate XDR routines, that is, the routines
-necessary to convert local data
-structures into network format and vice-versa. This example presents
-a complete RPC service\(ema remote directory listing service, which uses
-.I rpcgen
-not only to generate stub routines, but also to generate the XDR
-routines. Here is the protocol description file:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * dir.x: Remote directory listing protocol
- */
-.ft CW
-const MAXNAMELEN = 255; /* \fImaximum length of a directory entry\fP */
-
-typedef string nametype<MAXNAMELEN>; /* \fIa directory entry\fP */
-
-typedef struct namenode *namelist; /* \fIa link in the listing\fP */
-
-.ft I
-/*
- * A node in the directory listing
- */
-.ft CW
-struct namenode {
- nametype name; /* \fIname of directory entry\fP */
- namelist next; /* \fInext entry\fP */
-};
-
-.ft I
-/*
- * The result of a READDIR operation.
- */
-.ft CW
-union readdir_res switch (int errno) {
-case 0:
- namelist list; /* \fIno error: return directory listing\fP */
-default:
- void; /* \fIerror occurred: nothing else to return\fP */
-};
-
-.ft I
-/*
- * The directory program definition
- */
-.ft CW
-program DIRPROG {
- version DIRVERS {
- readdir_res
- READDIR(nametype) = 1;
- } = 1;
-} = 76;
-.DE
-.SH
-Note:
-.I
-Types (like
-.I readdir_res
-in the example above) can be defined using
-the \*Qstruct\*U, \*Qunion\*U and \*Qenum\*U keywords, but those keywords
-should not be used in subsequent declarations of variables of those types.
-For example, if you define a union \*Qfoo\*U, you should declare using
-only \*Qfoo\*U and not \*Qunion foo\*U. In fact,
-.I rpcgen
-compiles
-RPC unions into C structures and it is an error to declare them using the
-\*Qunion\*U keyword.
-.LP
-Running
-.I rpcgen
-on
-.I dir.x
-creates four output files. Three are the same as before: header file,
-client stub routines and server skeleton. The fourth are the XDR routines
-necessary for converting the data types we declared into XDR format and
-vice-versa. These are output in the file
-.I dir_xdr.c .
-.LP
-Here is the implementation of the
-.I READDIR
-procedure.
-.ie t .DS
-.el .DS L
-.vs 11
-.ft I
-/*
- * dir_proc.c: remote readdir implementation
- */
-.ft CW
-#include <rpc/rpc.h>
-#include <sys/dir.h>
-#include "dir.h"
-
-extern int errno;
-extern char *malloc();
-extern char *strdup();
-
-readdir_res *
-readdir_1(dirname)
- nametype *dirname;
-{
- DIR *dirp;
- struct direct *d;
- namelist nl;
- namelist *nlp;
- static readdir_res res; /* \fImust be static\fP! */
-
-.ft I
- /*
- * Open directory
- */
-.ft CW
- dirp = opendir(*dirname);
- if (dirp == NULL) {
- res.errno = errno;
- return (&res);
- }
-
-.ft I
- /*
- * Free previous result
- */
-.ft CW
- xdr_free(xdr_readdir_res, &res);
-
-.ft I
- /*
- * Collect directory entries.
- * Memory allocated here will be freed by \fIxdr_free\fP
- * next time \fIreaddir_1\fP is called
- */
-.ft CW
- nlp = &res.readdir_res_u.list;
- while (d = readdir(dirp)) {
- nl = *nlp = (namenode *) malloc(sizeof(namenode));
- nl->name = strdup(d->d_name);
- nlp = &nl->next;
- }
- *nlp = NULL;
-
-.ft I
- /*
- * Return the result
- */
-.ft CW
- res.errno = 0;
- closedir(dirp);
- return (&res);
-}
-.vs
-.DE
-Finally, there is the client side program to call the server:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * rls.c: Remote directory listing client
- */
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h> /* \fIalways need this\fP */
-#include "dir.h" /* \fIwill be generated by rpcgen\fI */
-
-extern int errno;
-
-main(argc, argv)
- int argc;
- char *argv[];
-{
- CLIENT *cl;
- char *server;
- char *dir;
- readdir_res *result;
- namelist nl;
-
-
- if (argc != 3) {
- fprintf(stderr, "usage: %s host directory\en",
- argv[0]);
- exit(1);
- }
-
-.ft I
- /*
- * Remember what our command line arguments refer to
- */
-.ft CW
- server = argv[1];
- dir = argv[2];
-
-.ft I
- /*
- * Create client "handle" used for calling \fIMESSAGEPROG\fP on the
- * server designated on the command line. We tell the RPC package
- * to use the "tcp" protocol when contacting the server.
- */
-.ft CW
- cl = clnt_create(server, DIRPROG, DIRVERS, "tcp");
- if (cl == NULL) {
-.ft I
- /*
- * Couldn't establish connection with server.
- * Print error message and die.
- */
-.ft CW
- clnt_pcreateerror(server);
- exit(1);
- }
-
-.ft I
- /*
- * Call the remote procedure \fIreaddir\fP on the server
- */
-.ft CW
- result = readdir_1(&dir, cl);
- if (result == NULL) {
-.ft I
- /*
- * An error occurred while calling the server.
- * Print error message and die.
- */
-.ft CW
- clnt_perror(cl, server);
- exit(1);
- }
-
-.ft I
- /*
- * Okay, we successfully called the remote procedure.
- */
-.ft CW
- if (result->errno != 0) {
-.ft I
- /*
- * A remote system error occurred.
- * Print error message and die.
- */
-.ft CW
- errno = result->errno;
- perror(dir);
- exit(1);
- }
-
-.ft I
- /*
- * Successfully got a directory listing.
- * Print it out.
- */
-.ft CW
- for (nl = result->readdir_res_u.list; nl != NULL;
- nl = nl->next) {
- printf("%s\en", nl->name);
- }
- exit(0);
-}
-.DE
-Compile everything, and run.
-.DS
-.ft CW
-sun% \fBrpcgen dir.x\fP
-sun% \fBcc rls.c dir_clnt.c dir_xdr.c -o rls\fP
-sun% \fBcc dir_svc.c dir_proc.c dir_xdr.c -o dir_svc\fP
-
-sun% \fBdir_svc &\fP
-
-moon% \fBrls sun /usr/pub\fP
-\&.
-\&..
-ascii
-eqnchar
-greek
-kbd
-marg8
-tabclr
-tabs
-tabs4
-moon%
-.DE
-.LP
-.IX "debugging with rpcgen" "" "debugging with \fIrpcgen\fP"
-A final note about
-.I rpcgen :
-The client program and the server procedure can be tested together
-as a single program by simply linking them with each other rather
-than with the client and server stubs. The procedure calls will be
-executed as ordinary local procedure calls and the program can be
-debugged with a local debugger such as
-.I dbx .
-When the program is working, the client program can be linked to
-the client stub produced by
-.I rpcgen
-and the server procedures can be linked to the server stub produced
-by
-.I rpcgen .
-.SH
-.I NOTE :
-\fIIf you do this, you may want to comment out calls to RPC library
-routines, and have client-side routines call server routines
-directly.\fP
-.LP
-.NH 1
-\&The C-Preprocessor
-.IX rpcgen "C-preprocessor" \fIrpcgen\fP
-.LP
-The C-preprocessor is run on all input files before they are
-compiled, so all the preprocessor directives are legal within a \*Q.x\*U
-file. Four symbols may be defined, depending upon which output file is
-getting generated. The symbols are:
-.TS
-box tab (&);
-lfI lfI
-lfL l .
-Symbol&Usage
-_
-RPC_HDR&for header-file output
-RPC_XDR&for XDR routine output
-RPC_SVC&for server-skeleton output
-RPC_CLNT&for client stub output
-.TE
-.LP
-Also,
-.I rpcgen
-does a little preprocessing of its own. Any line that
-begins with a percent sign is passed directly into the output file,
-without any interpretation of the line. Here is a simple example that
-demonstrates the preprocessing features.
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * time.x: Remote time protocol
- */
-.ft CW
-program TIMEPROG {
- version TIMEVERS {
- unsigned int TIMEGET(void) = 1;
- } = 1;
-} = 44;
-
-#ifdef RPC_SVC
-%int *
-%timeget_1()
-%{
-% static int thetime;
-%
-% thetime = time(0);
-% return (&thetime);
-%}
-#endif
-.DE
-The '%' feature is not generally recommended, as there is no guarantee
-that the compiler will stick the output where you intended.
-.NH 1
-\&\fBrpcgen\fP Programming Notes
-.IX rpcgen "other operations" \fIrpcgen\fP
-.sp
-.NH 2
-\&Timeout Changes
-.IX rpcgen "timeout changes" \fIrpcgen\fP
-.LP
-RPC sets a default timeout of 25 seconds for RPC calls when
-.I clnt_create()
-is used. This timeout may be changed using
-.I clnt_control()
-Here is a small code fragment to demonstrate use of
-.I clnt_control ():
-.ID
-struct timeval tv;
-CLIENT *cl;
-.sp .5
-cl = clnt_create("somehost", SOMEPROG, SOMEVERS, "tcp");
-if (cl == NULL) {
- exit(1);
-}
-tv.tv_sec = 60; /* \fIchange timeout to 1 minute\fP */
-tv.tv_usec = 0;
-clnt_control(cl, CLSET_TIMEOUT, &tv);
-.DE
-.NH 2
-\&Handling Broadcast on the Server Side
-.IX "broadcast RPC"
-.IX rpcgen "broadcast RPC" \fIrpcgen\fP
-.LP
-When a procedure is known to be called via broadcast RPC,
-it is usually wise for the server to not reply unless it can provide
-some useful information to the client. This prevents the network
-from getting flooded by useless replies.
-.LP
-To prevent the server from replying, a remote procedure can
-return NULL as its result, and the server code generated by
-.I rpcgen
-will detect this and not send out a reply.
-.LP
-Here is an example of a procedure that replies only if it
-thinks it is an NFS server:
-.ID
-void *
-reply_if_nfsserver()
-{
- char notnull; /* \fIjust here so we can use its address\fP */
-.sp .5
- if (access("/etc/exports", F_OK) < 0) {
- return (NULL); /* \fIprevent RPC from replying\fP */
- }
-.ft I
- /*
- * return non-null pointer so RPC will send out a reply
- */
-.ft L
- return ((void *)&notnull);
-}
-.DE
-Note that if procedure returns type \*Qvoid *\*U, they must return a non-NULL
-pointer if they want RPC to reply for them.
-.NH 2
-\&Other Information Passed to Server Procedures
-.LP
-Server procedures will often want to know more about an RPC call
-than just its arguments. For example, getting authentication information
-is important to procedures that want to implement some level of security.
-This extra information is actually supplied to the server procedure as a
-second argument. Here is an example to demonstrate its use. What we've
-done here is rewrite the previous
-.I printmessage_1()
-procedure to only allow root users to print a message to the console.
-.ID
-int *
-printmessage_1(msg, rq)
- char **msg;
- struct svc_req *rq;
-{
- static in result; /* \fIMust be static\fP */
- FILE *f;
- struct suthunix_parms *aup;
-.sp .5
- aup = (struct authunix_parms *)rq->rq_clntcred;
- if (aup->aup_uid != 0) {
- result = 0;
- return (&result);
- }
-.sp
-.ft I
- /*
- * Same code as before.
- */
-.ft L
-}
-.DE
-.NH 1
-\&RPC Language
-.IX RPCL
-.IX rpcgen "RPC Language" \fIrpcgen\fP
-.LP
-RPC language is an extension of XDR language. The sole extension is
-the addition of the
-.I program
-type. For a complete description of the XDR language syntax, see the
-.I "External Data Representation Standard: Protocol Specification"
-chapter. For a description of the RPC extensions to the XDR language,
-see the
-.I "Remote Procedure Calls: Protocol Specification"
-chapter.
-.LP
-However, XDR language is so close to C that if you know C, you know most
-of it already. We describe here the syntax of the RPC language,
-showing a few examples along the way. We also show how the various
-RPC and XDR type definitions get compiled into C type definitions in
-the output header file.
-.KS
-.NH 2
-Definitions
-\&
-.IX rpcgen definitions \fIrpcgen\fP
-.LP
-An RPC language file consists of a series of definitions.
-.DS L
-.ft CW
- definition-list:
- definition ";"
- definition ";" definition-list
-.DE
-.KE
-It recognizes five types of definitions.
-.DS L
-.ft CW
- definition:
- enum-definition
- struct-definition
- union-definition
- typedef-definition
- const-definition
- program-definition
-.DE
-.NH 2
-Structures
-\&
-.IX rpcgen structures \fIrpcgen\fP
-.LP
-An XDR struct is declared almost exactly like its C counterpart. It
-looks like the following:
-.DS L
-.ft CW
- struct-definition:
- "struct" struct-ident "{"
- declaration-list
- "}"
-
- declaration-list:
- declaration ";"
- declaration ";" declaration-list
-.DE
-As an example, here is an XDR structure to a two-dimensional
-coordinate, and the C structure that it gets compiled into in the
-output header file.
-.DS
-.ft CW
- struct coord { struct coord {
- int x; --> int x;
- int y; int y;
- }; };
- typedef struct coord coord;
-.DE
-The output is identical to the input, except for the added
-.I typedef
-at the end of the output. This allows one to use \*Qcoord\*U instead of
-\*Qstruct coord\*U when declaring items.
-.NH 2
-Unions
-\&
-.IX rpcgen unions \fIrpcgen\fP
-.LP
-XDR unions are discriminated unions, and look quite different from C
-unions. They are more analogous to Pascal variant records than they
-are to C unions.
-.DS L
-.ft CW
- union-definition:
- "union" union-ident "switch" "(" declaration ")" "{"
- case-list
- "}"
-
- case-list:
- "case" value ":" declaration ";"
- "default" ":" declaration ";"
- "case" value ":" declaration ";" case-list
-.DE
-Here is an example of a type that might be returned as the result of a
-\*Qread data\*U operation. If there is no error, return a block of data.
-Otherwise, don't return anything.
-.DS L
-.ft CW
- union read_result switch (int errno) {
- case 0:
- opaque data[1024];
- default:
- void;
- };
-.DE
-It gets compiled into the following:
-.DS L
-.ft CW
- struct read_result {
- int errno;
- union {
- char data[1024];
- } read_result_u;
- };
- typedef struct read_result read_result;
-.DE
-Notice that the union component of the output struct has the name as
-the type name, except for the trailing \*Q_u\*U.
-.NH 2
-Enumerations
-\&
-.IX rpcgen enumerations \fIrpcgen\fP
-.LP
-XDR enumerations have the same syntax as C enumerations.
-.DS L
-.ft CW
- enum-definition:
- "enum" enum-ident "{"
- enum-value-list
- "}"
-
- enum-value-list:
- enum-value
- enum-value "," enum-value-list
-
- enum-value:
- enum-value-ident
- enum-value-ident "=" value
-.DE
-Here is a short example of an XDR enum, and the C enum that it gets
-compiled into.
-.DS L
-.ft CW
- enum colortype { enum colortype {
- RED = 0, RED = 0,
- GREEN = 1, --> GREEN = 1,
- BLUE = 2 BLUE = 2,
- }; };
- typedef enum colortype colortype;
-.DE
-.NH 2
-Typedef
-\&
-.IX rpcgen typedef \fIrpcgen\fP
-.LP
-XDR typedefs have the same syntax as C typedefs.
-.DS L
-.ft CW
- typedef-definition:
- "typedef" declaration
-.DE
-Here is an example that defines a
-.I fname_type
-used for declaring
-file name strings that have a maximum length of 255 characters.
-.DS L
-.ft CW
-typedef string fname_type<255>; --> typedef char *fname_type;
-.DE
-.NH 2
-Constants
-\&
-.IX rpcgen constants \fIrpcgen\fP
-.LP
-XDR constants symbolic constants that may be used wherever a
-integer constant is used, for example, in array size specifications.
-.DS L
-.ft CW
- const-definition:
- "const" const-ident "=" integer
-.DE
-For example, the following defines a constant
-.I DOZEN
-equal to 12.
-.DS L
-.ft CW
- const DOZEN = 12; --> #define DOZEN 12
-.DE
-.NH 2
-Programs
-\&
-.IX rpcgen programs \fIrpcgen\fP
-.LP
-RPC programs are declared using the following syntax:
-.DS L
-.ft CW
- program-definition:
- "program" program-ident "{"
- version-list
- "}" "=" value
-
- version-list:
- version ";"
- version ";" version-list
-
- version:
- "version" version-ident "{"
- procedure-list
- "}" "=" value
-
- procedure-list:
- procedure ";"
- procedure ";" procedure-list
-
- procedure:
- type-ident procedure-ident "(" type-ident ")" "=" value
-.DE
-For example, here is the time protocol, revisited:
-.ie t .DS
-.el .DS L
-.ft I
-/*
- * time.x: Get or set the time. Time is represented as number of seconds
- * since 0:00, January 1, 1970.
- */
-.ft CW
-program TIMEPROG {
- version TIMEVERS {
- unsigned int TIMEGET(void) = 1;
- void TIMESET(unsigned) = 2;
- } = 1;
-} = 44;
-.DE
-This file compiles into #defines in the output header file:
-.ie t .DS
-.el .DS L
-.ft CW
-#define TIMEPROG 44
-#define TIMEVERS 1
-#define TIMEGET 1
-#define TIMESET 2
-.DE
-.NH 2
-Declarations
-\&
-.IX rpcgen declarations \fIrpcgen\fP
-.LP
-In XDR, there are only four kinds of declarations.
-.DS L
-.ft CW
- declaration:
- simple-declaration
- fixed-array-declaration
- variable-array-declaration
- pointer-declaration
-.DE
-\fB1) Simple declarations\fP are just like simple C declarations.
-.DS L
-.ft CW
- simple-declaration:
- type-ident variable-ident
-.DE
-Example:
-.DS L
-.ft CW
- colortype color; --> colortype color;
-.DE
-\fB2) Fixed-length Array Declarations\fP are just like C array declarations:
-.DS L
-.ft CW
- fixed-array-declaration:
- type-ident variable-ident "[" value "]"
-.DE
-Example:
-.DS L
-.ft CW
- colortype palette[8]; --> colortype palette[8];
-.DE
-\fB3) Variable-Length Array Declarations\fP have no explicit syntax
-in C, so XDR invents its own using angle-brackets.
-.DS L
-.ft CW
-variable-array-declaration:
- type-ident variable-ident "<" value ">"
- type-ident variable-ident "<" ">"
-.DE
-The maximum size is specified between the angle brackets. The size may
-be omitted, indicating that the array may be of any size.
-.DS L
-.ft CW
- int heights<12>; /* \fIat most 12 items\fP */
- int widths<>; /* \fIany number of items\fP */
-.DE
-Since variable-length arrays have no explicit syntax in C, these
-declarations are actually compiled into \*Qstruct\*Us. For example, the
-\*Qheights\*U declaration gets compiled into the following struct:
-.DS L
-.ft CW
- struct {
- u_int heights_len; /* \fI# of items in array\fP */
- int *heights_val; /* \fIpointer to array\fP */
- } heights;
-.DE
-Note that the number of items in the array is stored in the \*Q_len\*U
-component and the pointer to the array is stored in the \*Q_val\*U
-component. The first part of each of these component's names is the
-same as the name of the declared XDR variable.
-.LP
-\fB4) Pointer Declarations\fP are made in
-XDR exactly as they are in C. You can't
-really send pointers over the network, but you can use XDR pointers
-for sending recursive data types such as lists and trees. The type is
-actually called \*Qoptional-data\*U, not \*Qpointer\*U, in XDR language.
-.DS L
-.ft CW
- pointer-declaration:
- type-ident "*" variable-ident
-.DE
-Example:
-.DS L
-.ft CW
- listitem *next; --> listitem *next;
-.DE
-.NH 2
-\&Special Cases
-.IX rpcgen "special cases" \fIrpcgen\fP
-.LP
-There are a few exceptions to the rules described above.
-.LP
-.B Booleans:
-C has no built-in boolean type. However, the RPC library does a
-boolean type called
-.I bool_t
-that is either
-.I TRUE
-or
-.I FALSE .
-Things declared as type
-.I bool
-in XDR language are compiled into
-.I bool_t
-in the output header file.
-.LP
-Example:
-.DS L
-.ft CW
- bool married; --> bool_t married;
-.DE
-.B Strings:
-C has no built-in string type, but instead uses the null-terminated
-\*Qchar *\*U convention. In XDR language, strings are declared using the
-\*Qstring\*U keyword, and compiled into \*Qchar *\*Us in the output header
-file. The maximum size contained in the angle brackets specifies the
-maximum number of characters allowed in the strings (not counting the
-.I NULL
-character). The maximum size may be left off, indicating a string
-of arbitrary length.
-.LP
-Examples:
-.DS L
-.ft CW
- string name<32>; --> char *name;
- string longname<>; --> char *longname;
-.DE
-.B "Opaque Data:"
-Opaque data is used in RPC and XDR to describe untyped data, that is,
-just sequences of arbitrary bytes. It may be declared either as a
-fixed or variable length array.
-.DS L
-Examples:
-.ft CW
- opaque diskblock[512]; --> char diskblock[512];
-
- opaque filedata<1024>; --> struct {
- u_int filedata_len;
- char *filedata_val;
- } filedata;
-.DE
-.B Voids:
-In a void declaration, the variable is not named. The declaration is
-just \*Qvoid\*U and nothing else. Void declarations can only occur in two
-places: union definitions and program definitions (as the argument or
-result of a remote procedure).
diff --git a/cpukit/librpc/src/rpc/PSD.doc/xdr.nts.ms b/cpukit/librpc/src/rpc/PSD.doc/xdr.nts.ms
deleted file mode 100644
index 6c2d482dea..0000000000
--- a/cpukit/librpc/src/rpc/PSD.doc/xdr.nts.ms
+++ /dev/null
@@ -1,1966 +0,0 @@
-.\"
-.\" Must use -- eqn -- with this one
-.\"
-.\" @(#)xdr.nts.ms 2.2 88/08/05 4.0 RPCSRC
-.EQ
-delim $$
-.EN
-.de BT
-.if \\n%=1 .tl ''- % -''
-..
-.ND
-.\" prevent excess underlining in nroff
-.if n .fp 2 R
-.OH 'External Data Representation: Sun Technical Notes''Page %'
-.EH 'Page %''External Data Representation: Sun Technical Notes'
-.if \\n%=1 .bp
-.SH
-\&External Data Representation: Sun Technical Notes
-.IX XDR "Sun technical notes"
-.LP
-This chapter contains technical notes on Sun's implementation of the
-External Data Representation (XDR) standard, a set of library routines
-that allow a C programmer to describe arbitrary data structures in a
-machinex-independent fashion.
-For a formal specification of the XDR
-standard, see the
-.I "External Data Representation Standard: Protocol Specification".
-XDR is the backbone of Sun's Remote Procedure Call package, in the
-sense that data for remote procedure calls is transmitted using the
-standard. XDR library routines should be used to transmit data
-that is accessed (read or written) by more than one type of machine.\**
-.FS
-.IX XDR "system routines"
-For a compete specification of the system External Data Representation
-routines, see the
-.I xdr(3N)
-manual page.
-.FE
-.LP
-This chapter contains a short tutorial overview of the XDR library
-routines, a guide to accessing currently available XDR streams, and
-information on defining new streams and data types. XDR was designed
-to work across different languages, operating systems, and machine
-architectures. Most users (particularly RPC users) will only need
-the information in the
-.I "Number Filters",
-.I "Floating Point Filters",
-and
-.I "Enumeration Filters"
-sections.
-Programmers wishing to implement RPC and XDR on new machines
-will be interested in the rest of the chapter, as well as the
-.I "External Data Representaiton Standard: Protocol Specification",
-which will be their primary reference.
-.SH
-Note:
-.I
-.I rpcgen
-can be used to write XDR routines even in cases where no RPC calls are
-being made.
-.LP
-On Sun systems,
-C programs that want to use XDR routines
-must include the file
-.I <rpc/rpc.h> ,
-which contains all the necessary interfaces to the XDR system.
-Since the C library
-.I libc.a
-contains all the XDR routines,
-compile as normal.
-.DS
-example% \fBcc\0\fIprogram\fP.c\fI
-.DE
-.ne 3i
-.NH 0
-\&Justification
-.IX XDR justification
-.LP
-Consider the following two programs,
-.I writer :
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-.sp.5
-main() /* \fIwriter.c\fP */
-{
- long i;
-.sp.5
- for (i = 0; i < 8; i++) {
- if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- }
- exit(0);
-}
-.DE
-and
-.I reader :
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-.sp.5
-main() /* \fIreader.c\fP */
-{
- long i, j;
-.sp.5
- for (j = 0; j < 8; j++) {
- if (fread((char *)&i, sizeof (i), 1, stdin) != 1) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- printf("%ld ", i);
- }
- printf("\en");
- exit(0);
-}
-.DE
-The two programs appear to be portable, because (a) they pass
-.I lint
-checking, and (b) they exhibit the same behavior when executed
-on two different hardware architectures, a Sun and a VAX.
-.LP
-Piping the output of the
-.I writer
-program to the
-.I reader
-program gives identical results on a Sun or a VAX.
-.DS
-.ft CW
-sun% \fBwriter | reader\fP
-0 1 2 3 4 5 6 7
-sun%
-
-
-vax% \fBwriter | reader\fP
-0 1 2 3 4 5 6 7
-vax%
-.DE
-With the advent of local area networks and 4.2BSD came the concept
-of \*Qnetwork pipes\*U \(em a process produces data on one machine,
-and a second process consumes data on another machine.
-A network pipe can be constructed with
-.I writer
-and
-.I reader .
-Here are the results if the first produces data on a Sun,
-and the second consumes data on a VAX.
-.DS
-.ft CW
-sun% \fBwriter | rsh vax reader\fP
-0 16777216 33554432 50331648 67108864 83886080 100663296
-117440512
-sun%
-.DE
-Identical results can be obtained by executing
-.I writer
-on the VAX and
-.I reader
-on the Sun. These results occur because the byte ordering
-of long integers differs between the VAX and the Sun,
-even though word size is the same.
-Note that $16777216$ is $2 sup 24$ \(em
-when four bytes are reversed, the 1 winds up in the 24th bit.
-.LP
-Whenever data is shared by two or more machine types, there is
-a need for portable data. Programs can be made data-portable by
-replacing the
-.I read()
-and
-.I write()
-calls with calls to an XDR library routine
-.I xdr_long() ,
-a filter that knows the standard representation
-of a long integer in its external form.
-Here are the revised versions of
-.I writer :
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */
-.sp.5
-main() /* \fIwriter.c\fP */
-{
- XDR xdrs;
- long i;
-.sp.5
- xdrstdio_create(&xdrs, stdout, XDR_ENCODE);
- for (i = 0; i < 8; i++) {
- if (!xdr_long(&xdrs, &i)) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- }
- exit(0);
-}
-.DE
-and
-.I reader :
-.ie t .DS
-.el .DS L
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */
-.sp.5
-main() /* \fIreader.c\fP */
-{
- XDR xdrs;
- long i, j;
-.sp.5
- xdrstdio_create(&xdrs, stdin, XDR_DECODE);
- for (j = 0; j < 8; j++) {
- if (!xdr_long(&xdrs, &i)) {
- fprintf(stderr, "failed!\en");
- exit(1);
- }
- printf("%ld ", i);
- }
- printf("\en");
- exit(0);
-}
-.DE
-The new programs were executed on a Sun,
-on a VAX, and from a Sun to a VAX;
-the results are shown below.
-.DS
-.ft CW
-sun% \fBwriter | reader\fP
-0 1 2 3 4 5 6 7
-sun%
-
-vax% \fBwriter | reader\fP
-0 1 2 3 4 5 6 7
-vax%
-
-sun% \fBwriter | rsh vax reader\fP
-0 1 2 3 4 5 6 7
-sun%
-.DE
-.SH
-Note:
-.I
-.IX XDR "portable data"
-Integers are just the tip of the portable-data iceberg. Arbitrary
-data structures present portability problems, particularly with
-respect to alignment and pointers. Alignment on word boundaries
-may cause the size of a structure to vary from machine to machine.
-And pointers, which are very convenient to use, have no meaning
-outside the machine where they are defined.
-.LP
-.NH 1
-\&A Canonical Standard
-.IX XDR "canonical standard"
-.LP
-XDR's approach to standardizing data representations is
-.I canonical .
-That is, XDR defines a single byte order (Big Endian), a single
-floating-point representation (IEEE), and so on. Any program running on
-any machine can use XDR to create portable data by translating its
-local representation to the XDR standard representations; similarly, any
-program running on any machine can read portable data by translating the
-XDR standard representaions to its local equivalents. The single standard
-completely decouples programs that create or send portable data from those
-that use or receive portable data. The advent of a new machine or a new
-language has no effect upon the community of existing portable data creators
-and users. A new machine joins this community by being \*Qtaught\*U how to
-convert the standard representations and its local representations; the
-local representations of other machines are irrelevant. Conversely, to
-existing programs running on other machines, the local representations of
-the new machine are also irrelevant; such programs can immediately read
-portable data produced by the new machine because such data conforms to the
-canonical standards that they already understand.
-.LP
-There are strong precedents for XDR's canonical approach. For example,
-TCP/IP, UDP/IP, XNS, Ethernet, and, indeed, all protocols below layer five
-of the ISO model, are canonical protocols. The advantage of any canonical
-approach is simplicity; in the case of XDR, a single set of conversion
-routines is written once and is never touched again. The canonical approach
-has a disadvantage, but it is unimportant in real-world data transfer
-applications. Suppose two Little-Endian machines are transferring integers
-according to the XDR standard. The sending machine converts the integers
-from Little-Endian byte order to XDR (Big-Endian) byte order; the receiving
-machine performs the reverse conversion. Because both machines observe the
-same byte order, their conversions are unnecessary. The point, however, is
-not necessity, but cost as compared to the alternative.
-.LP
-The time spent converting to and from a canonical representation is
-insignificant, especially in networking applications. Most of the time
-required to prepare a data structure for transfer is not spent in conversion
-but in traversing the elements of the data structure. To transmit a tree,
-for example, each leaf must be visited and each element in a leaf record must
-be copied to a buffer and aligned there; storage for the leaf may have to be
-deallocated as well. Similarly, to receive a tree, storage must be
-allocated for each leaf, data must be moved from the buffer to the leaf and
-properly aligned, and pointers must be constructed to link the leaves
-together. Every machine pays the cost of traversing and copying data
-structures whether or not conversion is required. In networking
-applications, communications overhead\(emthe time required to move the data
-down through the sender's protocol layers, across the network and up through
-the receiver's protocol layers\(emdwarfs conversion overhead.
-.NH 1
-\&The XDR Library
-.IX "XDR" "library"
-.LP
-The XDR library not only solves data portability problems, it also
-allows you to write and read arbitrary C constructs in a consistent,
-specified, well-documented manner. Thus, it can make sense to use the
-library even when the data is not shared among machines on a network.
-.LP
-The XDR library has filter routines for
-strings (null-terminated arrays of bytes),
-structures, unions, and arrays, to name a few.
-Using more primitive routines,
-you can write your own specific XDR routines
-to describe arbitrary data structures,
-including elements of arrays, arms of unions,
-or objects pointed at from other structures.
-The structures themselves may contain arrays of arbitrary elements,
-or pointers to other structures.
-.LP
-Let's examine the two programs more closely.
-There is a family of XDR stream creation routines
-in which each member treats the stream of bits differently.
-In our example, data is manipulated using standard I/O routines,
-so we use
-.I xdrstdio_create ().
-.IX xdrstdio_create() "" "\fIxdrstdio_create()\fP"
-The parameters to XDR stream creation routines
-vary according to their function.
-In our example,
-.I xdrstdio_create()
-takes a pointer to an XDR structure that it initializes,
-a pointer to a
-.I FILE
-that the input or output is performed on, and the operation.
-The operation may be
-.I XDR_ENCODE
-for serializing in the
-.I writer
-program, or
-.I XDR_DECODE
-for deserializing in the
-.I reader
-program.
-.LP
-Note: RPC users never need to create XDR streams;
-the RPC system itself creates these streams,
-which are then passed to the users.
-.LP
-The
-.I xdr_long()
-.IX xdr_long() "" "\fIxdr_long()\fP"
-primitive is characteristic of most XDR library
-primitives and all client XDR routines.
-First, the routine returns
-.I FALSE
-(0) if it fails, and
-.I TRUE
-(1) if it succeeds.
-Second, for each data type,
-.I xxx ,
-there is an associated XDR routine of the form:
-.DS
-.ft CW
-xdr_xxx(xdrs, xp)
- XDR *xdrs;
- xxx *xp;
-{
-}
-.DE
-In our case,
-.I xxx
-is long, and the corresponding XDR routine is
-a primitive,
-.I xdr_long() .
-The client could also define an arbitrary structure
-.I xxx
-in which case the client would also supply the routine
-.I xdr_xxx (),
-describing each field by calling XDR routines
-of the appropriate type.
-In all cases the first parameter,
-.I xdrs
-can be treated as an opaque handle,
-and passed to the primitive routines.
-.LP
-XDR routines are direction independent;
-that is, the same routines are called to serialize or deserialize data.
-This feature is critical to software engineering of portable data.
-The idea is to call the same routine for either operation \(em
-this almost guarantees that serialized data can also be deserialized.
-One routine is used by both producer and consumer of networked data.
-This is implemented by always passing the address
-of an object rather than the object itself \(em
-only in the case of deserialization is the object modified.
-This feature is not shown in our trivial example,
-but its value becomes obvious when nontrivial data structures
-are passed among machines.
-If needed, the user can obtain the
-direction of the XDR operation.
-See the
-.I "XDR Operation Directions"
-section below for details.
-.LP
-Let's look at a slightly more complicated example.
-Assume that a person's gross assets and liabilities
-are to be exchanged among processes.
-Also assume that these values are important enough
-to warrant their own data type:
-.ie t .DS
-.el .DS L
-.ft CW
-struct gnumbers {
- long g_assets;
- long g_liabilities;
-};
-.DE
-The corresponding XDR routine describing this structure would be:
-.ie t .DS
-.el .DS L
-.ft CW
-bool_t /* \fITRUE is success, FALSE is failure\fP */
-xdr_gnumbers(xdrs, gp)
- XDR *xdrs;
- struct gnumbers *gp;
-{
- if (xdr_long(xdrs, &gp->g_assets) &&
- xdr_long(xdrs, &gp->g_liabilities))
- return(TRUE);
- return(FALSE);
-}
-.DE
-Note that the parameter
-.I xdrs
-is never inspected or modified;
-it is only passed on to the subcomponent routines.
-It is imperative to inspect the return value of each XDR routine call,
-and to give up immediately and return
-.I FALSE
-if the subroutine fails.
-.LP
-This example also shows that the type
-.I bool_t
-is declared as an integer whose only values are
-.I TRUE
-(1) and
-.I FALSE
-(0). This document uses the following definitions:
-.ie t .DS
-.el .DS L
-.ft CW
-#define bool_t int
-#define TRUE 1
-#define FALSE 0
-.DE
-.LP
-Keeping these conventions in mind,
-.I xdr_gnumbers()
-can be rewritten as follows:
-.ie t .DS
-.el .DS L
-.ft CW
-xdr_gnumbers(xdrs, gp)
- XDR *xdrs;
- struct gnumbers *gp;
-{
- return(xdr_long(xdrs, &gp->g_assets) &&
- xdr_long(xdrs, &gp->g_liabilities));
-}
-.DE
-This document uses both coding styles.
-.NH 1
-\&XDR Library Primitives
-.IX "library primitives for XDR"
-.IX XDR "library primitives"
-.LP
-This section gives a synopsis of each XDR primitive.
-It starts with basic data types and moves on to constructed data types.
-Finally, XDR utilities are discussed.
-The interface to these primitives
-and utilities is defined in the include file
-.I <rpc/xdr.h> ,
-automatically included by
-.I <rpc/rpc.h> .
-.NH 2
-\&Number Filters
-.IX "XDR library" "number filters"
-.LP
-The XDR library provides primitives to translate between numbers
-and their corresponding external representations.
-Primitives cover the set of numbers in:
-.DS
-.ft CW
-[signed, unsigned] * [short, int, long]
-.DE
-.ne 2i
-Specifically, the eight primitives are:
-.DS
-.ft CW
-bool_t xdr_char(xdrs, cp)
- XDR *xdrs;
- char *cp;
-.sp.5
-bool_t xdr_u_char(xdrs, ucp)
- XDR *xdrs;
- unsigned char *ucp;
-.sp.5
-bool_t xdr_int(xdrs, ip)
- XDR *xdrs;
- int *ip;
-.sp.5
-bool_t xdr_u_int(xdrs, up)
- XDR *xdrs;
- unsigned *up;
-.sp.5
-bool_t xdr_long(xdrs, lip)
- XDR *xdrs;
- long *lip;
-.sp.5
-bool_t xdr_u_long(xdrs, lup)
- XDR *xdrs;
- u_long *lup;
-.sp.5
-bool_t xdr_short(xdrs, sip)
- XDR *xdrs;
- short *sip;
-.sp.5
-bool_t xdr_u_short(xdrs, sup)
- XDR *xdrs;
- u_short *sup;
-.DE
-The first parameter,
-.I xdrs ,
-is an XDR stream handle.
-The second parameter is the address of the number
-that provides data to the stream or receives data from it.
-All routines return
-.I TRUE
-if they complete successfully, and
-.I FALSE
-otherwise.
-.NH 2
-\&Floating Point Filters
-.IX "XDR library" "floating point filters"
-.LP
-The XDR library also provides primitive routines
-for C's floating point types:
-.DS
-.ft CW
-bool_t xdr_float(xdrs, fp)
- XDR *xdrs;
- float *fp;
-.sp.5
-bool_t xdr_double(xdrs, dp)
- XDR *xdrs;
- double *dp;
-.DE
-The first parameter,
-.I xdrs
-is an XDR stream handle.
-The second parameter is the address
-of the floating point number that provides data to the stream
-or receives data from it.
-Both routines return
-.I TRUE
-if they complete successfully, and
-.I FALSE
-otherwise.
-.LP
-Note: Since the numbers are represented in IEEE floating point,
-routines may fail when decoding a valid IEEE representation
-into a machine-specific representation, or vice-versa.
-.NH 2
-\&Enumeration Filters
-.IX "XDR library" "enumeration filters"
-.LP
-The XDR library provides a primitive for generic enumerations.
-The primitive assumes that a C
-.I enum
-has the same representation inside the machine as a C integer.
-The boolean type is an important instance of the
-.I enum .
-The external representation of a boolean is always
-.I TRUE
-(1) or
-.I FALSE
-(0).
-.DS
-.ft CW
-#define bool_t int
-#define FALSE 0
-#define TRUE 1
-.sp.5
-#define enum_t int
-.sp.5
-bool_t xdr_enum(xdrs, ep)
- XDR *xdrs;
- enum_t *ep;
-.sp.5
-bool_t xdr_bool(xdrs, bp)
- XDR *xdrs;
- bool_t *bp;
-.DE
-The second parameters
-.I ep
-and
-.I bp
-are addresses of the associated type that provides data to, or
-receives data from, the stream
-.I xdrs .
-.NH 2
-\&No Data
-.IX "XDR library" "no data"
-.LP
-Occasionally, an XDR routine must be supplied to the RPC system,
-even when no data is passed or required.
-The library provides such a routine:
-.DS
-.ft CW
-bool_t xdr_void(); /* \fIalways returns TRUE\fP */
-.DE
-.NH 2
-\&Constructed Data Type Filters
-.IX "XDR library" "constructed data type filters"
-.LP
-Constructed or compound data type primitives
-require more parameters and perform more complicated functions
-then the primitives discussed above.
-This section includes primitives for
-strings, arrays, unions, and pointers to structures.
-.LP
-Constructed data type primitives may use memory management.
-In many cases, memory is allocated when deserializing data with
-.I XDR_DECODE
-Therefore, the XDR package must provide means to deallocate memory.
-This is done by an XDR operation,
-.I XDR_FREE
-To review, the three XDR directional operations are
-.I XDR_ENCODE ,
-.I XDR_DECODE
-and
-.I XDR_FREE .
-.NH 3
-\&Strings
-.IX "XDR library" "strings"
-.LP
-In C, a string is defined as a sequence of bytes
-terminated by a null byte,
-which is not considered when calculating string length.
-However, when a string is passed or manipulated,
-a pointer to it is employed.
-Therefore, the XDR library defines a string to be a
-.I "char *"
-and not a sequence of characters.
-The external representation of a string is drastically different
-from its internal representation.
-Externally, strings are represented as
-sequences of ASCII characters,
-while internally, they are represented with character pointers.
-Conversion between the two representations
-is accomplished with the routine
-.I xdr_string ():
-.IX xdr_string() "" \fIxdr_string()\fP
-.DS
-.ft CW
-bool_t xdr_string(xdrs, sp, maxlength)
- XDR *xdrs;
- char **sp;
- u_int maxlength;
-.DE
-The first parameter
-.I xdrs
-is the XDR stream handle.
-The second parameter
-.I sp
-is a pointer to a string (type
-.I "char **" .
-The third parameter
-.I maxlength
-specifies the maximum number of bytes allowed during encoding or decoding.
-its value is usually specified by a protocol. For example, a protocol
-specification may say that a file name may be no longer than 255 characters.
-.LP
-The routine returns
-.I FALSE
-if the number of characters exceeds
-.I maxlength ,
-and
-.I TRUE
-if it doesn't.
-.SH
-Keep
-.I maxlength
-small. If it is too big you can blow the heap, since
-.I xdr_string()
-will call
-.I malloc()
-for space.
-.LP
-The behavior of
-.I xdr_string()
-.IX xdr_string() "" \fIxdr_string()\fP
-is similar to the behavior of other routines
-discussed in this section. The direction
-.I XDR_ENCODE
-is easiest to understand. The parameter
-.I sp
-points to a string of a certain length;
-if the string does not exceed
-.I maxlength ,
-the bytes are serialized.
-.LP
-The effect of deserializing a string is subtle.
-First the length of the incoming string is determined;
-it must not exceed
-.I maxlength .
-Next
-.I sp
-is dereferenced; if the the value is
-.I NULL ,
-then a string of the appropriate length is allocated and
-.I *sp
-is set to this string.
-If the original value of
-.I *sp
-is non-null, then the XDR package assumes
-that a target area has been allocated,
-which can hold strings no longer than
-.I maxlength .
-In either case, the string is decoded into the target area.
-The routine then appends a null character to the string.
-.LP
-In the
-.I XDR_FREE
-operation, the string is obtained by dereferencing
-.I sp .
-If the string is not
-.I NULL ,
-it is freed and
-.I *sp
-is set to
-.I NULL .
-In this operation,
-.I xdr_string()
-ignores the
-.I maxlength
-parameter.
-.NH 3
-\&Byte Arrays
-.IX "XDR library" "byte arrays"
-.LP
-Often variable-length arrays of bytes are preferable to strings.
-Byte arrays differ from strings in the following three ways:
-1) the length of the array (the byte count) is explicitly
-located in an unsigned integer,
-2) the byte sequence is not terminated by a null character, and
-3) the external representation of the bytes is the same as their
-internal representation.
-The primitive
-.I xdr_bytes()
-.IX xdr_bytes() "" \fIxdr_bytes()\fP
-converts between the internal and external
-representations of byte arrays:
-.DS
-.ft CW
-bool_t xdr_bytes(xdrs, bpp, lp, maxlength)
- XDR *xdrs;
- char **bpp;
- u_int *lp;
- u_int maxlength;
-.DE
-The usage of the first, second and fourth parameters
-are identical to the first, second and third parameters of
-.I xdr_string (),
-respectively.
-The length of the byte area is obtained by dereferencing
-.I lp
-when serializing;
-.I *lp
-is set to the byte length when deserializing.
-.NH 3
-\&Arrays
-.IX "XDR library" "arrays"
-.LP
-The XDR library package provides a primitive
-for handling arrays of arbitrary elements.
-The
-.I xdr_bytes()
-routine treats a subset of generic arrays,
-in which the size of array elements is known to be 1,
-and the external description of each element is built-in.
-The generic array primitive,
-.I xdr_array() ,
-.IX xdr_array() "" \fIxdr_array()\fP
-requires parameters identical to those of
-.I xdr_bytes()
-plus two more:
-the size of array elements,
-and an XDR routine to handle each of the elements.
-This routine is called to encode or decode
-each element of the array.
-.DS
-.ft CW
-bool_t
-xdr_array(xdrs, ap, lp, maxlength, elementsiz, xdr_element)
- XDR *xdrs;
- char **ap;
- u_int *lp;
- u_int maxlength;
- u_int elementsiz;
- bool_t (*xdr_element)();
-.DE
-The parameter
-.I ap
-is the address of the pointer to the array.
-If
-.I *ap
-is
-.I NULL
-when the array is being deserialized,
-XDR allocates an array of the appropriate size and sets
-.I *ap
-to that array.
-The element count of the array is obtained from
-.I *lp
-when the array is serialized;
-.I *lp
-is set to the array length when the array is deserialized.
-The parameter
-.I maxlength
-is the maximum number of elements that the array is allowed to have;
-.I elementsiz
-is the byte size of each element of the array
-(the C function
-.I sizeof()
-can be used to obtain this value).
-The
-.I xdr_element()
-.IX xdr_element() "" \fIxdr_element()\fP
-routine is called to serialize, deserialize, or free
-each element of the array.
-.br
-.LP
-Before defining more constructed data types, it is appropriate to
-present three examples.
-.LP
-.I "Example A:"
-.br
-A user on a networked machine can be identified by
-(a) the machine name, such as
-.I krypton :
-see the
-.I gethostname
-man page; (b) the user's UID: see the
-.I geteuid
-man page; and (c) the group numbers to which the user belongs:
-see the
-.I getgroups
-man page. A structure with this information and its associated
-XDR routine could be coded like this:
-.ie t .DS
-.el .DS L
-.ft CW
-struct netuser {
- char *nu_machinename;
- int nu_uid;
- u_int nu_glen;
- int *nu_gids;
-};
-#define NLEN 255 /* \fImachine names < 256 chars\fP */
-#define NGRPS 20 /* \fIuser can't be in > 20 groups\fP */
-.sp.5
-bool_t
-xdr_netuser(xdrs, nup)
- XDR *xdrs;
- struct netuser *nup;
-{
- return(xdr_string(xdrs, &nup->nu_machinename, NLEN) &&
- xdr_int(xdrs, &nup->nu_uid) &&
- xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen,
- NGRPS, sizeof (int), xdr_int));
-}
-.DE
-.LP
-.I "Example B:"
-.br
-A party of network users could be implemented
-as an array of
-.I netuser
-structure.
-The declaration and its associated XDR routines
-are as follows:
-.ie t .DS
-.el .DS L
-.ft CW
-struct party {
- u_int p_len;
- struct netuser *p_nusers;
-};
-#define PLEN 500 /* \fImax number of users in a party\fP */
-.sp.5
-bool_t
-xdr_party(xdrs, pp)
- XDR *xdrs;
- struct party *pp;
-{
- return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN,
- sizeof (struct netuser), xdr_netuser));
-}
-.DE
-.LP
-.I "Example C:"
-.br
-The well-known parameters to
-.I main ,
-.I argc
-and
-.I argv
-can be combined into a structure.
-An array of these structures can make up a history of commands.
-The declarations and XDR routines might look like:
-.ie t .DS
-.el .DS L
-.ft CW
-struct cmd {
- u_int c_argc;
- char **c_argv;
-};
-#define ALEN 1000 /* \fIargs cannot be > 1000 chars\fP */
-#define NARGC 100 /* \fIcommands cannot have > 100 args\fP */
-
-struct history {
- u_int h_len;
- struct cmd *h_cmds;
-};
-#define NCMDS 75 /* \fIhistory is no more than 75 commands\fP */
-
-bool_t
-xdr_wrap_string(xdrs, sp)
- XDR *xdrs;
- char **sp;
-{
- return(xdr_string(xdrs, sp, ALEN));
-}
-.DE
-.ie t .DS
-.el .DS L
-.ft CW
-bool_t
-xdr_cmd(xdrs, cp)
- XDR *xdrs;
- struct cmd *cp;
-{
- return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC,
- sizeof (char *), xdr_wrap_string));
-}
-.DE
-.ie t .DS
-.el .DS L
-.ft CW
-bool_t
-xdr_history(xdrs, hp)
- XDR *xdrs;
- struct history *hp;
-{
- return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS,
- sizeof (struct cmd), xdr_cmd));
-}
-.DE
-The most confusing part of this example is that the routine
-.I xdr_wrap_string()
-is needed to package the
-.I xdr_string()
-routine, because the implementation of
-.I xdr_array()
-only passes two parameters to the array element description routine;
-.I xdr_wrap_string()
-supplies the third parameter to
-.I xdr_string ().
-.LP
-By now the recursive nature of the XDR library should be obvious.
-Let's continue with more constructed data types.
-.NH 3
-\&Opaque Data
-.IX "XDR library" "opaque data"
-.LP
-In some protocols, handles are passed from a server to client.
-The client passes the handle back to the server at some later time.
-Handles are never inspected by clients;
-they are obtained and submitted.
-That is to say, handles are opaque.
-The
-.I xdr_opaque()
-.IX xdr_opaque() "" \fIxdr_opaque()\fP
-primitive is used for describing fixed sized, opaque bytes.
-.DS
-.ft CW
-bool_t xdr_opaque(xdrs, p, len)
- XDR *xdrs;
- char *p;
- u_int len;
-.DE
-The parameter
-.I p
-is the location of the bytes;
-.I len
-is the number of bytes in the opaque object.
-By definition, the actual data
-contained in the opaque object are not machine portable.
-.NH 3
-\&Fixed Sized Arrays
-.IX "XDR library" "fixed sized arrays"
-.LP
-The XDR library provides a primitive,
-.I xdr_vector (),
-for fixed-length arrays.
-.ie t .DS
-.el .DS L
-.ft CW
-#define NLEN 255 /* \fImachine names must be < 256 chars\fP */
-#define NGRPS 20 /* \fIuser belongs to exactly 20 groups\fP */
-.sp.5
-struct netuser {
- char *nu_machinename;
- int nu_uid;
- int nu_gids[NGRPS];
-};
-.sp.5
-bool_t
-xdr_netuser(xdrs, nup)
- XDR *xdrs;
- struct netuser *nup;
-{
- int i;
-.sp.5
- if (!xdr_string(xdrs, &nup->nu_machinename, NLEN))
- return(FALSE);
- if (!xdr_int(xdrs, &nup->nu_uid))
- return(FALSE);
- if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int),
- xdr_int)) {
- return(FALSE);
- }
- return(TRUE);
-}
-.DE
-.NH 3
-\&Discriminated Unions
-.IX "XDR library" "discriminated unions"
-.LP
-The XDR library supports discriminated unions.
-A discriminated union is a C union and an
-.I enum_t
-value that selects an \*Qarm\*U of the union.
-.DS
-.ft CW
-struct xdr_discrim {
- enum_t value;
- bool_t (*proc)();
-};
-.sp.5
-bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm)
- XDR *xdrs;
- enum_t *dscmp;
- char *unp;
- struct xdr_discrim *arms;
- bool_t (*defaultarm)(); /* \fImay equal NULL\fP */
-.DE
-First the routine translates the discriminant of the union located at
-.I *dscmp .
-The discriminant is always an
-.I enum_t .
-Next the union located at
-.I *unp
-is translated.
-The parameter
-.I arms
-is a pointer to an array of
-.I xdr_discrim
-structures.
-Each structure contains an ordered pair of
-.I [value,proc] .
-If the union's discriminant is equal to the associated
-.I value ,
-then the
-.I proc
-is called to translate the union.
-The end of the
-.I xdr_discrim
-structure array is denoted by a routine of value
-.I NULL
-(0). If the discriminant is not found in the
-.I arms
-array, then the
-.I defaultarm
-procedure is called if it is non-null;
-otherwise the routine returns
-.I FALSE .
-.LP
-.I "Example D:"
-Suppose the type of a union may be integer,
-character pointer (a string), or a
-.I gnumbers
-structure.
-Also, assume the union and its current type
-are declared in a structure.
-The declaration is:
-.ie t .DS
-.el .DS L
-.ft CW
-enum utype { INTEGER=1, STRING=2, GNUMBERS=3 };
-.sp.5
-struct u_tag {
- enum utype utype; /* \fIthe union's discriminant\fP */
- union {
- int ival;
- char *pval;
- struct gnumbers gn;
- } uval;
-};
-.DE
-The following constructs and XDR procedure (de)serialize
-the discriminated union:
-.ie t .DS
-.el .DS L
-.ft CW
-struct xdr_discrim u_tag_arms[4] = {
- { INTEGER, xdr_int },
- { GNUMBERS, xdr_gnumbers }
- { STRING, xdr_wrap_string },
- { __dontcare__, NULL }
- /* \fIalways terminate arms with a NULL xdr_proc\fP */
-}
-.sp.5
-bool_t
-xdr_u_tag(xdrs, utp)
- XDR *xdrs;
- struct u_tag *utp;
-{
- return(xdr_union(xdrs, &utp->utype, &utp->uval,
- u_tag_arms, NULL));
-}
-.DE
-The routine
-.I xdr_gnumbers()
-was presented above in
-.I "The XDR Library"
-section.
-.I xdr_wrap_string()
-was presented in example C.
-The default
-.I arm
-parameter to
-.I xdr_union()
-(the last parameter) is
-.I NULL
-in this example. Therefore the value of the union's discriminant
-may legally take on only values listed in the
-.I u_tag_arms
-array. This example also demonstrates that
-the elements of the arm's array do not need to be sorted.
-.LP
-It is worth pointing out that the values of the discriminant
-may be sparse, though in this example they are not.
-It is always good
-practice to assign explicitly integer values to each element of the
-discriminant's type.
-This practice both documents the external
-representation of the discriminant and guarantees that different
-C compilers emit identical discriminant values.
-.LP
-Exercise: Implement
-.I xdr_union()
-using the other primitives in this section.
-.NH 3
-\&Pointers
-.IX "XDR library" "pointers"
-.LP
-In C it is often convenient to put pointers
-to another structure within a structure.
-The
-.I xdr_reference()
-.IX xdr_reference() "" \fIxdr_reference()\fP
-primitive makes it easy to serialize, deserialize, and free
-these referenced structures.
-.DS
-.ft CW
-bool_t xdr_reference(xdrs, pp, size, proc)
- XDR *xdrs;
- char **pp;
- u_int ssize;
- bool_t (*proc)();
-.DE
-.LP
-Parameter
-.I pp
-is the address of
-the pointer to the structure;
-parameter
-.I ssize
-is the size in bytes of the structure (use the C function
-.I sizeof()
-to obtain this value); and
-.I proc
-is the XDR routine that describes the structure.
-When decoding data, storage is allocated if
-.I *pp
-is
-.I NULL .
-.LP
-There is no need for a primitive
-.I xdr_struct()
-to describe structures within structures,
-because pointers are always sufficient.
-.LP
-Exercise: Implement
-.I xdr_reference()
-using
-.I xdr_array ().
-Warning:
-.I xdr_reference()
-and
-.I xdr_array()
-are NOT interchangeable external representations of data.
-.LP
-.I "Example E:"
-Suppose there is a structure containing a person's name
-and a pointer to a
-.I gnumbers
-structure containing the person's gross assets and liabilities.
-The construct is:
-.DS
-.ft CW
-struct pgn {
- char *name;
- struct gnumbers *gnp;
-};
-.DE
-The corresponding XDR routine for this structure is:
-.DS
-.ft CW
-bool_t
-xdr_pgn(xdrs, pp)
- XDR *xdrs;
- struct pgn *pp;
-{
- if (xdr_string(xdrs, &pp->name, NLEN) &&
- xdr_reference(xdrs, &pp->gnp,
- sizeof(struct gnumbers), xdr_gnumbers))
- return(TRUE);
- return(FALSE);
-}
-.DE
-.IX "pointer semantics and XDR"
-.I "Pointer Semantics and XDR"
-.LP
-In many applications, C programmers attach double meaning to
-the values of a pointer. Typically the value
-.I NULL
-(or zero) means data is not needed,
-yet some application-specific interpretation applies.
-In essence, the C programmer is encoding
-a discriminated union efficiently
-by overloading the interpretation of the value of a pointer.
-For instance, in example E a
-.I NULL
-pointer value for
-.I gnp
-could indicate that
-the person's assets and liabilities are unknown.
-That is, the pointer value encodes two things:
-whether or not the data is known;
-and if it is known, where it is located in memory.
-Linked lists are an extreme example of the use
-of application-specific pointer interpretation.
-.LP
-The primitive
-.I xdr_reference()
-.IX xdr_reference() "" \fIxdr_reference()\fP
-cannot and does not attach any special
-meaning to a null-value pointer during serialization.
-That is, passing an address of a pointer whose value is
-.I NULL
-to
-.I xdr_reference()
-when serialing data will most likely cause a memory fault and, on the UNIX
-system, a core dump.
-.LP
-.I xdr_pointer()
-correctly handles
-.I NULL
-pointers. For more information about its use, see
-the
-.I "Linked Lists"
-topics below.
-.LP
-.I Exercise:
-After reading the section on
-.I "Linked Lists" ,
-return here and extend example E so that
-it can correctly deal with
-.I NULL
-pointer values.
-.LP
-.I Exercise:
-Using the
-.I xdr_union (),
-.I xdr_reference()
-and
-.I xdr_void()
-primitives, implement a generic pointer handling primitive
-that implicitly deals with
-.I NULL
-pointers. That is, implement
-.I xdr_pointer ().
-.NH 2
-\&Non-filter Primitives
-.IX "XDR" "non-filter primitives"
-.LP
-XDR streams can be manipulated with
-the primitives discussed in this section.
-.DS
-.ft CW
-u_int xdr_getpos(xdrs)
- XDR *xdrs;
-.sp.5
-bool_t xdr_setpos(xdrs, pos)
- XDR *xdrs;
- u_int pos;
-.sp.5
-xdr_destroy(xdrs)
- XDR *xdrs;
-.DE
-The routine
-.I xdr_getpos()
-.IX xdr_getpos() "" \fIxdr_getpos()\fP
-returns an unsigned integer
-that describes the current position in the data stream.
-Warning: In some XDR streams, the returned value of
-.I xdr_getpos()
-is meaningless;
-the routine returns a \-1 in this case
-(though \-1 should be a legitimate value).
-.LP
-The routine
-.I xdr_setpos()
-.IX xdr_setpos() "" \fIxdr_setpos()\fP
-sets a stream position to
-.I pos .
-Warning: In some XDR streams, setting a position is impossible;
-in such cases,
-.I xdr_setpos()
-will return
-.I FALSE .
-This routine will also fail if the requested position is out-of-bounds.
-The definition of bounds varies from stream to stream.
-.LP
-The
-.I xdr_destroy()
-.IX xdr_destroy() "" \fIxdr_destroy()\fP
-primitive destroys the XDR stream.
-Usage of the stream
-after calling this routine is undefined.
-.NH 2
-\&XDR Operation Directions
-.IX XDR "operation directions"
-.IX "direction of XDR operations"
-.LP
-At times you may wish to optimize XDR routines by taking
-advantage of the direction of the operation \(em
-.I XDR_ENCODE
-.I XDR_DECODE
-or
-.I XDR_FREE
-The value
-.I xdrs->x_op
-always contains the direction of the XDR operation.
-Programmers are not encouraged to take advantage of this information.
-Therefore, no example is presented here. However, an example in the
-.I "Linked Lists"
-topic below, demonstrates the usefulness of the
-.I xdrs->x_op
-field.
-.NH 2
-\&XDR Stream Access
-.IX "XDR" "stream access"
-.LP
-An XDR stream is obtained by calling the appropriate creation routine.
-These creation routines take arguments that are tailored to the
-specific properties of the stream.
-.LP
-Streams currently exist for (de)serialization of data to or from
-standard I/O
-.I FILE
-streams, TCP/IP connections and UNIX files, and memory.
-.NH 3
-\&Standard I/O Streams
-.IX "XDR" "standard I/O streams"
-.LP
-XDR streams can be interfaced to standard I/O using the
-.I xdrstdio_create()
-.IX xdrstdio_create() "" \fIxdrstdio_create()\fP
-routine as follows:
-.DS
-.ft CW
-#include <stdio.h>
-#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */
-.sp.5
-void
-xdrstdio_create(xdrs, fp, x_op)
- XDR *xdrs;
- FILE *fp;
- enum xdr_op x_op;
-.DE
-The routine
-.I xdrstdio_create()
-initializes an XDR stream pointed to by
-.I xdrs .
-The XDR stream interfaces to the standard I/O library.
-Parameter
-.I fp
-is an open file, and
-.I x_op
-is an XDR direction.
-.NH 3
-\&Memory Streams
-.IX "XDR" "memory streams"
-.LP
-Memory streams allow the streaming of data into or out of
-a specified area of memory:
-.DS
-.ft CW
-#include <rpc/rpc.h>
-.sp.5
-void
-xdrmem_create(xdrs, addr, len, x_op)
- XDR *xdrs;
- char *addr;
- u_int len;
- enum xdr_op x_op;
-.DE
-The routine
-.I xdrmem_create()
-.IX xdrmem_create() "" \fIxdrmem_create()\fP
-initializes an XDR stream in local memory.
-The memory is pointed to by parameter
-.I addr ;
-parameter
-.I len
-is the length in bytes of the memory.
-The parameters
-.I xdrs
-and
-.I x_op
-are identical to the corresponding parameters of
-.I xdrstdio_create ().
-Currently, the UDP/IP implementation of RPC uses
-.I xdrmem_create ().
-Complete call or result messages are built in memory before calling the
-.I sendto()
-system routine.
-.NH 3
-\&Record (TCP/IP) Streams
-.IX "XDR" "record (TCP/IP) streams"
-.LP
-A record stream is an XDR stream built on top of
-a record marking standard that is built on top of the
-UNIX file or 4.2 BSD connection interface.
-.DS
-.ft CW
-#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */
-.sp.5
-xdrrec_create(xdrs,
- sendsize, recvsize, iohandle, readproc, writeproc)
- XDR *xdrs;
- u_int sendsize, recvsize;
- char *iohandle;
- int (*readproc)(), (*writeproc)();
-.DE
-The routine
-.I xdrrec_create()
-provides an XDR stream interface that allows for a bidirectional,
-arbitrarily long sequence of records.
-The contents of the records are meant to be data in XDR form.
-The stream's primary use is for interfacing RPC to TCP connections.
-However, it can be used to stream data into or out of normal
-UNIX files.
-.LP
-The parameter
-.I xdrs
-is similar to the corresponding parameter described above.
-The stream does its own data buffering similar to that of standard I/O.
-The parameters
-.I sendsize
-and
-.I recvsize
-determine the size in bytes of the output and input buffers, respectively;
-if their values are zero (0), then predetermined defaults are used.
-When a buffer needs to be filled or flushed, the routine
-.I readproc()
-or
-.I writeproc()
-is called, respectively.
-The usage and behavior of these
-routines are similar to the UNIX system calls
-.I read()
-and
-.I write ().
-However,
-the first parameter to each of these routines is the opaque parameter
-.I iohandle .
-The other two parameters
-.I buf ""
-and
-.I nbytes )
-and the results
-(byte count) are identical to the system routines.
-If
-.I xxx
-is
-.I readproc()
-or
-.I writeproc (),
-then it has the following form:
-.DS
-.ft CW
-.ft I
-/*
- * returns the actual number of bytes transferred.
- * -1 is an error
- */
-.ft CW
-int
-xxx(iohandle, buf, len)
- char *iohandle;
- char *buf;
- int nbytes;
-.DE
-The XDR stream provides means for delimiting records in the byte stream.
-The implementation details of delimiting records in a stream are
-discussed in the
-.I "Advanced Topics"
-topic below.
-The primitives that are specific to record streams are as follows:
-.DS
-.ft CW
-bool_t
-xdrrec_endofrecord(xdrs, flushnow)
- XDR *xdrs;
- bool_t flushnow;
-.sp.5
-bool_t
-xdrrec_skiprecord(xdrs)
- XDR *xdrs;
-.sp.5
-bool_t
-xdrrec_eof(xdrs)
- XDR *xdrs;
-.DE
-The routine
-.I xdrrec_endofrecord()
-.IX xdrrec_endofrecord() "" \fIxdrrec_endofrecord()\fP
-causes the current outgoing data to be marked as a record.
-If the parameter
-.I flushnow
-is
-.I TRUE ,
-then the stream's
-.I writeproc
-will be called; otherwise,
-.I writeproc
-will be called when the output buffer has been filled.
-.LP
-The routine
-.I xdrrec_skiprecord()
-.IX xdrrec_skiprecord() "" \fIxdrrec_skiprecord()\fP
-causes an input stream's position to be moved past
-the current record boundary and onto the
-beginning of the next record in the stream.
-.LP
-If there is no more data in the stream's input buffer,
-then the routine
-.I xdrrec_eof()
-.IX xdrrec_eof() "" \fIxdrrec_eof()\fP
-returns
-.I TRUE .
-That is not to say that there is no more data
-in the underlying file descriptor.
-.NH 2
-\&XDR Stream Implementation
-.IX "XDR" "stream implementation"
-.IX "stream implementation in XDR"
-.LP
-This section provides the abstract data types needed
-to implement new instances of XDR streams.
-.NH 3
-\&The XDR Object
-.IX "XDR" "object"
-.LP
-The following structure defines the interface to an XDR stream:
-.ie t .DS
-.el .DS L
-.ft CW
-enum xdr_op { XDR_ENCODE=0, XDR_DECODE=1, XDR_FREE=2 };
-.sp.5
-typedef struct {
- enum xdr_op x_op; /* \fIoperation; fast added param\fP */
- struct xdr_ops {
- bool_t (*x_getlong)(); /* \fIget long from stream\fP */
- bool_t (*x_putlong)(); /* \fIput long to stream\fP */
- bool_t (*x_getbytes)(); /* \fIget bytes from stream\fP */
- bool_t (*x_putbytes)(); /* \fIput bytes to stream\fP */
- u_int (*x_getpostn)(); /* \fIreturn stream offset\fP */
- bool_t (*x_setpostn)(); /* \fIreposition offset\fP */
- caddr_t (*x_inline)(); /* \fIptr to buffered data\fP */
- VOID (*x_destroy)(); /* \fIfree private area\fP */
- } *x_ops;
- caddr_t x_public; /* \fIusers' data\fP */
- caddr_t x_private; /* \fIpointer to private data\fP */
- caddr_t x_base; /* \fIprivate for position info\fP */
- int x_handy; /* \fIextra private word\fP */
-} XDR;
-.DE
-The
-.I x_op
-field is the current operation being performed on the stream.
-This field is important to the XDR primitives,
-but should not affect a stream's implementation.
-That is, a stream's implementation should not depend
-on this value.
-The fields
-.I x_private ,
-.I x_base ,
-and
-.I x_handy
-are private to the particular
-stream's implementation.
-The field
-.I x_public
-is for the XDR client and should never be used by
-the XDR stream implementations or the XDR primitives.
-.I x_getpostn() ,
-.I x_setpostn()
-and
-.I x_destroy()
-are macros for accessing operations. The operation
-.I x_inline()
-takes two parameters:
-an XDR *, and an unsigned integer, which is a byte count.
-The routine returns a pointer to a piece of
-the stream's internal buffer.
-The caller can then use the buffer segment for any purpose.
-From the stream's point of view, the bytes in the
-buffer segment have been consumed or put.
-The routine may return
-.I NULL
-if it cannot return a buffer segment of the requested size.
-(The
-.I x_inline()
-routine is for cycle squeezers.
-Use of the resulting buffer is not data-portable.
-Users are encouraged not to use this feature.)
-.LP
-The operations
-.I x_getbytes()
-and
-.I x_putbytes()
-blindly get and put sequences of bytes
-from or to the underlying stream;
-they return
-.I TRUE
-if they are successful, and
-.I FALSE
-otherwise. The routines have identical parameters (replace
-.I xxx ):
-.DS
-.ft CW
-bool_t
-xxxbytes(xdrs, buf, bytecount)
- XDR *xdrs;
- char *buf;
- u_int bytecount;
-.DE
-The operations
-.I x_getlong()
-and
-.I x_putlong()
-receive and put
-long numbers from and to the data stream.
-It is the responsibility of these routines
-to translate the numbers between the machine representation
-and the (standard) external representation.
-The UNIX primitives
-.I htonl()
-and
-.I ntohl()
-can be helpful in accomplishing this.
-The higher-level XDR implementation assumes that
-signed and unsigned long integers contain the same number of bits,
-and that nonnegative integers
-have the same bit representations as unsigned integers.
-The routines return
-.I TRUE
-if they succeed, and
-.I FALSE
-otherwise. They have identical parameters:
-.DS
-.ft CW
-bool_t
-xxxlong(xdrs, lp)
- XDR *xdrs;
- long *lp;
-.DE
-Implementors of new XDR streams must make an XDR structure
-(with new operation routines) available to clients,
-using some kind of create routine.
-.NH 1
-\&Advanced Topics
-.IX XDR "advanced topics"
-.LP
-This section describes techniques for passing data structures that
-are not covered in the preceding sections. Such structures include
-linked lists (of arbitrary lengths). Unlike the simpler examples
-covered in the earlier sections, the following examples are written
-using both the XDR C library routines and the XDR data description
-language.
-The
-.I "External Data Representation Standard: Protocol Specification"
-describes this
-language in complete detail.
-.NH 2
-\&Linked Lists
-.IX XDR "linked lists"
-.LP
-The last example in the
-.I Pointers
-topic earlier in this chapter
-presented a C data structure and its associated XDR
-routines for a individual's gross assets and liabilities.
-The example is duplicated below:
-.ie t .DS
-.el .DS L
-.ft CW
-struct gnumbers {
- long g_assets;
- long g_liabilities;
-};
-.sp.5
-bool_t
-xdr_gnumbers(xdrs, gp)
- XDR *xdrs;
- struct gnumbers *gp;
-{
- if (xdr_long(xdrs, &(gp->g_assets)))
- return(xdr_long(xdrs, &(gp->g_liabilities)));
- return(FALSE);
-}
-.DE
-.LP
-Now assume that we wish to implement a linked list of such information.
-A data structure could be constructed as follows:
-.ie t .DS
-.el .DS L
-.ft CW
-struct gnumbers_node {
- struct gnumbers gn_numbers;
- struct gnumbers_node *gn_next;
-};
-.sp .5
-typedef struct gnumbers_node *gnumbers_list;
-.DE
-.LP
-The head of the linked list can be thought of as the data object;
-that is, the head is not merely a convenient shorthand for a
-structure. Similarly the
-.I gn_next
-field is used to indicate whether or not the object has terminated.
-Unfortunately, if the object continues, the
-.I gn_next
-field is also the address of where it continues. The link addresses
-carry no useful information when the object is serialized.
-.LP
-The XDR data description of this linked list is described by the
-recursive declaration of
-.I gnumbers_list :
-.ie t .DS
-.el .DS L
-.ft CW
-struct gnumbers {
- int g_assets;
- int g_liabilities;
-};
-.sp .5
-struct gnumbers_node {
- gnumbers gn_numbers;
- gnumbers_node *gn_next;
-};
-.DE
-.LP
-In this description, the boolean indicates whether there is more data
-following it. If the boolean is
-.I FALSE ,
-then it is the last data field of the structure. If it is
-.I TRUE ,
-then it is followed by a gnumbers structure and (recursively) by a
-.I gnumbers_list .
-Note that the C declaration has no boolean explicitly declared in it
-(though the
-.I gn_next
-field implicitly carries the information), while the XDR data
-description has no pointer explicitly declared in it.
-.LP
-Hints for writing the XDR routines for a
-.I gnumbers_list
-follow easily from the XDR description above. Note how the primitive
-.I xdr_pointer()
-is used to implement the XDR union above.
-.ie t .DS
-.el .DS L
-.ft CW
-bool_t
-xdr_gnumbers_node(xdrs, gn)
- XDR *xdrs;
- gnumbers_node *gn;
-{
- return(xdr_gnumbers(xdrs, &gn->gn_numbers) &&
- xdr_gnumbers_list(xdrs, &gp->gn_next));
-}
-.sp .5
-bool_t
-xdr_gnumbers_list(xdrs, gnp)
- XDR *xdrs;
- gnumbers_list *gnp;
-{
- return(xdr_pointer(xdrs, gnp,
- sizeof(struct gnumbers_node),
- xdr_gnumbers_node));
-}
-.DE
-.LP
-The unfortunate side effect of XDR'ing a list with these routines
-is that the C stack grows linearly with respect to the number of
-node in the list. This is due to the recursion. The following
-routine collapses the above two mutually recursive into a single,
-non-recursive one.
-.ie t .DS
-.el .DS L
-.ft CW
-bool_t
-xdr_gnumbers_list(xdrs, gnp)
- XDR *xdrs;
- gnumbers_list *gnp;
-{
- bool_t more_data;
- gnumbers_list *nextp;
-.sp .5
- for (;;) {
- more_data = (*gnp != NULL);
- if (!xdr_bool(xdrs, &more_data)) {
- return(FALSE);
- }
- if (! more_data) {
- break;
- }
- if (xdrs->x_op == XDR_FREE) {
- nextp = &(*gnp)->gn_next;
- }
- if (!xdr_reference(xdrs, gnp,
- sizeof(struct gnumbers_node), xdr_gnumbers)) {
-
- return(FALSE);
- }
- gnp = (xdrs->x_op == XDR_FREE) ?
- nextp : &(*gnp)->gn_next;
- }
- *gnp = NULL;
- return(TRUE);
-}
-.DE
-.LP
-The first task is to find out whether there is more data or not,
-so that this boolean information can be serialized. Notice that
-this statement is unnecessary in the
-.I XDR_DECODE
-case, since the value of more_data is not known until we
-deserialize it in the next statement.
-.LP
-The next statement XDR's the more_data field of the XDR union.
-Then if there is truly no more data, we set this last pointer to
-.I NULL
-to indicate the end of the list, and return
-.I TRUE
-because we are done. Note that setting the pointer to
-.I NULL
-is only important in the
-.I XDR_DECODE
-case, since it is already
-.I NULL
-in the
-.I XDR_ENCODE
-and
-XDR_FREE
-cases.
-.LP
-Next, if the direction is
-.I XDR_FREE ,
-the value of
-.I nextp
-is set to indicate the location of the next pointer in the list.
-We do this now because we need to dereference gnp to find the
-location of the next item in the list, and after the next
-statement the storage pointed to by
-.I gnp
-will be freed up and no be longer valid. We can't do this for all
-directions though, because in the
-.I XDR_DECODE
-direction the value of
-.I gnp
-won't be set until the next statement.
-.LP
-Next, we XDR the data in the node using the primitive
-.I xdr_reference ().
-.I xdr_reference()
-is like
-.I xdr_pointer()
-which we used before, but it does not
-send over the boolean indicating whether there is more data.
-We use it instead of
-.I xdr_pointer()
-because we have already XDR'd this information ourselves. Notice
-that the xdr routine passed is not the same type as an element
-in the list. The routine passed is
-.I xdr_gnumbers (),
-for XDR'ing gnumbers, but each element in the list is actually of
-type
-.I gnumbers_node .
-We don't pass
-.I xdr_gnumbers_node()
-because it is recursive, and instead use
-.I xdr_gnumbers()
-which XDR's all of the non-recursive part. Note that this trick
-will work only if the
-.I gn_numbers
-field is the first item in each element, so that their addresses
-are identical when passed to
-.I xdr_reference ().
-.LP
-Finally, we update
-.I gnp
-to point to the next item in the list. If the direction is
-.I XDR_FREE ,
-we set it to the previously saved value, otherwise we can
-dereference
-.I gnp
-to get the proper value. Though harder to understand than the
-recursive version, this non-recursive routine is far less likely
-to blow the C stack. It will also run more efficiently since
-a lot of procedure call overhead has been removed. Most lists
-are small though (in the hundreds of items or less) and the
-recursive version should be sufficient for them.
-.EQ
-delim off
-.EN
diff --git a/cpukit/librpc/src/rpc/PSD.doc/xdr.rfc.ms b/cpukit/librpc/src/rpc/PSD.doc/xdr.rfc.ms
deleted file mode 100644
index d4baff5391..0000000000
--- a/cpukit/librpc/src/rpc/PSD.doc/xdr.rfc.ms
+++ /dev/null
@@ -1,1058 +0,0 @@
-.\"
-.\" Must use -- tbl -- with this one
-.\"
-.\" @(#)xdr.rfc.ms 2.2 88/08/05 4.0 RPCSRC
-.de BT
-.if \\n%=1 .tl ''- % -''
-..
-.ND
-.\" prevent excess underlining in nroff
-.if n .fp 2 R
-.OH 'External Data Representation Standard''Page %'
-.EH 'Page %''External Data Representation Standard'
-.IX "External Data Representation"
-.if \\n%=1 .bp
-.SH
-\&External Data Representation Standard: Protocol Specification
-.IX XDR RFC
-.IX XDR "protocol specification"
-.LP
-.NH 0
-\&Status of this Standard
-.nr OF 1
-.IX XDR "RFC status"
-.LP
-Note: This chapter specifies a protocol that Sun Microsystems, Inc., and
-others are using. It has been designated RFC1014 by the ARPA Network
-Information Center.
-.NH 1
-Introduction
-\&
-.LP
-XDR is a standard for the description and encoding of data. It is
-useful for transferring data between different computer
-architectures, and has been used to communicate data between such
-diverse machines as the Sun Workstation, VAX, IBM-PC, and Cray.
-XDR fits into the ISO presentation layer, and is roughly analogous in
-purpose to X.409, ISO Abstract Syntax Notation. The major difference
-between these two is that XDR uses implicit typing, while X.409 uses
-explicit typing.
-.LP
-XDR uses a language to describe data formats. The language can only
-be used only to describe data; it is not a programming language.
-This language allows one to describe intricate data formats in a
-concise manner. The alternative of using graphical representations
-(itself an informal language) quickly becomes incomprehensible when
-faced with complexity. The XDR language itself is similar to the C
-language [1], just as Courier [4] is similar to Mesa. Protocols such
-as Sun RPC (Remote Procedure Call) and the NFS (Network File System)
-use XDR to describe the format of their data.
-.LP
-The XDR standard makes the following assumption: that bytes (or
-octets) are portable, where a byte is defined to be 8 bits of data.
-A given hardware device should encode the bytes onto the various
-media in such a way that other hardware devices may decode the bytes
-without loss of meaning. For example, the Ethernet standard
-suggests that bytes be encoded in "little-endian" style [2], or least
-significant bit first.
-.NH 2
-\&Basic Block Size
-.IX XDR "basic block size"
-.IX XDR "block size"
-.LP
-The representation of all items requires a multiple of four bytes (or
-32 bits) of data. The bytes are numbered 0 through n-1. The bytes
-are read or written to some byte stream such that byte m always
-precedes byte m+1. If the n bytes needed to contain the data are not
-a multiple of four, then the n bytes are followed by enough (0 to 3)
-residual zero bytes, r, to make the total byte count a multiple of 4.
-.LP
-We include the familiar graphic box notation for illustration and
-comparison. In most illustrations, each box (delimited by a plus
-sign at the 4 corners and vertical bars and dashes) depicts a byte.
-Ellipses (...) between boxes show zero or more additional bytes where
-required.
-.ie t .DS
-.el .DS L
-\fIA Block\fP
-
-\f(CW+--------+--------+...+--------+--------+...+--------+
-| byte 0 | byte 1 |...|byte n-1| 0 |...| 0 |
-+--------+--------+...+--------+--------+...+--------+
-|<-----------n bytes---------->|<------r bytes------>|
-|<-----------n+r (where (n+r) mod 4 = 0)>----------->|\fP
-
-.DE
-.NH 1
-\&XDR Data Types
-.IX XDR "data types"
-.IX "XDR data types"
-.LP
-Each of the sections that follow describes a data type defined in the
-XDR standard, shows how it is declared in the language, and includes
-a graphic illustration of its encoding.
-.LP
-For each data type in the language we show a general paradigm
-declaration. Note that angle brackets (< and >) denote
-variable length sequences of data and square brackets ([ and ]) denote
-fixed-length sequences of data. "n", "m" and "r" denote integers.
-For the full language specification and more formal definitions of
-terms such as "identifier" and "declaration", refer to
-.I "The XDR Language Specification" ,
-below.
-.LP
-For some data types, more specific examples are included.
-A more extensive example of a data description is in
-.I "An Example of an XDR Data Description"
-below.
-.NH 2
-\&Integer
-.IX XDR integer
-.LP
-An XDR signed integer is a 32-bit datum that encodes an integer in
-the range [-2147483648,2147483647]. The integer is represented in
-two's complement notation. The most and least significant bytes are
-0 and 3, respectively. Integers are declared as follows:
-.ie t .DS
-.el .DS L
-\fIInteger\fP
-
-\f(CW(MSB) (LSB)
-+-------+-------+-------+-------+
-|byte 0 |byte 1 |byte 2 |byte 3 |
-+-------+-------+-------+-------+
-<------------32 bits------------>\fP
-.DE
-.NH 2
-\&Unsigned Integer
-.IX XDR "unsigned integer"
-.IX XDR "integer, unsigned"
-.LP
-An XDR unsigned integer is a 32-bit datum that encodes a nonnegative
-integer in the range [0,4294967295]. It is represented by an
-unsigned binary number whose most and least significant bytes are 0
-and 3, respectively. An unsigned integer is declared as follows:
-.ie t .DS
-.el .DS L
-\fIUnsigned Integer\fP
-
-\f(CW(MSB) (LSB)
-+-------+-------+-------+-------+
-|byte 0 |byte 1 |byte 2 |byte 3 |
-+-------+-------+-------+-------+
-<------------32 bits------------>\fP
-.DE
-.NH 2
-\&Enumeration
-.IX XDR enumeration
-.LP
-Enumerations have the same representation as signed integers.
-Enumerations are handy for describing subsets of the integers.
-Enumerated data is declared as follows:
-.ft CW
-.DS
-enum { name-identifier = constant, ... } identifier;
-.DE
-For example, the three colors red, yellow, and blue could be
-described by an enumerated type:
-.DS
-.ft CW
-enum { RED = 2, YELLOW = 3, BLUE = 5 } colors;
-.DE
-It is an error to encode as an enum any other integer than those that
-have been given assignments in the enum declaration.
-.NH 2
-\&Boolean
-.IX XDR boolean
-.LP
-Booleans are important enough and occur frequently enough to warrant
-their own explicit type in the standard. Booleans are declared as
-follows:
-.DS
-.ft CW
-bool identifier;
-.DE
-This is equivalent to:
-.DS
-.ft CW
-enum { FALSE = 0, TRUE = 1 } identifier;
-.DE
-.NH 2
-\&Hyper Integer and Unsigned Hyper Integer
-.IX XDR "hyper integer"
-.IX XDR "integer, hyper"
-.LP
-The standard also defines 64-bit (8-byte) numbers called hyper
-integer and unsigned hyper integer. Their representations are the
-obvious extensions of integer and unsigned integer defined above.
-They are represented in two's complement notation. The most and
-least significant bytes are 0 and 7, respectively. Their
-declarations:
-.ie t .DS
-.el .DS L
-\fIHyper Integer\fP
-\fIUnsigned Hyper Integer\fP
-
-\f(CW(MSB) (LSB)
-+-------+-------+-------+-------+-------+-------+-------+-------+
-|byte 0 |byte 1 |byte 2 |byte 3 |byte 4 |byte 5 |byte 6 |byte 7 |
-+-------+-------+-------+-------+-------+-------+-------+-------+
-<----------------------------64 bits---------------------------->\fP
-.DE
-.NH 2
-\&Floating-point
-.IX XDR "integer, floating point"
-.IX XDR "floating-point integer"
-.LP
-The standard defines the floating-point data type "float" (32 bits or
-4 bytes). The encoding used is the IEEE standard for normalized
-single-precision floating-point numbers [3]. The following three
-fields describe the single-precision floating-point number:
-.RS
-.IP \fBS\fP:
-The sign of the number. Values 0 and 1 represent positive and
-negative, respectively. One bit.
-.IP \fBE\fP:
-The exponent of the number, base 2. 8 bits are devoted to this
-field. The exponent is biased by 127.
-.IP \fBF\fP:
-The fractional part of the number's mantissa, base 2. 23 bits
-are devoted to this field.
-.RE
-.LP
-Therefore, the floating-point number is described by:
-.DS
-(-1)**S * 2**(E-Bias) * 1.F
-.DE
-It is declared as follows:
-.ie t .DS
-.el .DS L
-\fISingle-Precision Floating-Point\fP
-
-\f(CW+-------+-------+-------+-------+
-|byte 0 |byte 1 |byte 2 |byte 3 |
-S| E | F |
-+-------+-------+-------+-------+
-1|<- 8 ->|<-------23 bits------>|
-<------------32 bits------------>\fP
-.DE
-Just as the most and least significant bytes of a number are 0 and 3,
-the most and least significant bits of a single-precision floating-
-point number are 0 and 31. The beginning bit (and most significant
-bit) offsets of S, E, and F are 0, 1, and 9, respectively. Note that
-these numbers refer to the mathematical positions of the bits, and
-NOT to their actual physical locations (which vary from medium to
-medium).
-.LP
-The IEEE specifications should be consulted concerning the encoding
-for signed zero, signed infinity (overflow), and denormalized numbers
-(underflow) [3]. According to IEEE specifications, the "NaN" (not a
-number) is system dependent and should not be used externally.
-.NH 2
-\&Double-precision Floating-point
-.IX XDR "integer, double-precision floating point"
-.IX XDR "double-precision floating-point integer"
-.LP
-The standard defines the encoding for the double-precision floating-
-point data type "double" (64 bits or 8 bytes). The encoding used is
-the IEEE standard for normalized double-precision floating-point
-numbers [3]. The standard encodes the following three fields, which
-describe the double-precision floating-point number:
-.RS
-.IP \fBS\fP:
-The sign of the number. Values 0 and 1 represent positive and
-negative, respectively. One bit.
-.IP \fBE\fP:
-The exponent of the number, base 2. 11 bits are devoted to this
-field. The exponent is biased by 1023.
-.IP \fBF\fP:
-The fractional part of the number's mantissa, base 2. 52 bits
-are devoted to this field.
-.RE
-.LP
-Therefore, the floating-point number is described by:
-.DS
-(-1)**S * 2**(E-Bias) * 1.F
-.DE
-It is declared as follows:
-.ie t .DS
-.el .DS L
-\fIDouble-Precision Floating-Point\fP
-
-\f(CW+------+------+------+------+------+------+------+------+
-|byte 0|byte 1|byte 2|byte 3|byte 4|byte 5|byte 6|byte 7|
-S| E | F |
-+------+------+------+------+------+------+------+------+
-1|<--11-->|<-----------------52 bits------------------->|
-<-----------------------64 bits------------------------->\fP
-.DE
-Just as the most and least significant bytes of a number are 0 and 3,
-the most and least significant bits of a double-precision floating-
-point number are 0 and 63. The beginning bit (and most significant
-bit) offsets of S, E , and F are 0, 1, and 12, respectively. Note
-that these numbers refer to the mathematical positions of the bits,
-and NOT to their actual physical locations (which vary from medium to
-medium).
-.LP
-The IEEE specifications should be consulted concerning the encoding
-for signed zero, signed infinity (overflow), and denormalized numbers
-(underflow) [3]. According to IEEE specifications, the "NaN" (not a
-number) is system dependent and should not be used externally.
-.NH 2
-\&Fixed-length Opaque Data
-.IX XDR "fixed-length opaque data"
-.IX XDR "opaque data, fixed length"
-.LP
-At times, fixed-length uninterpreted data needs to be passed among
-machines. This data is called "opaque" and is declared as follows:
-.DS
-.ft CW
-opaque identifier[n];
-.DE
-where the constant n is the (static) number of bytes necessary to
-contain the opaque data. If n is not a multiple of four, then the n
-bytes are followed by enough (0 to 3) residual zero bytes, r, to make
-the total byte count of the opaque object a multiple of four.
-.ie t .DS
-.el .DS L
-\fIFixed-Length Opaque\fP
-
-\f(CW0 1 ...
-+--------+--------+...+--------+--------+...+--------+
-| byte 0 | byte 1 |...|byte n-1| 0 |...| 0 |
-+--------+--------+...+--------+--------+...+--------+
-|<-----------n bytes---------->|<------r bytes------>|
-|<-----------n+r (where (n+r) mod 4 = 0)------------>|\fP
-.DE
-.NH 2
-\&Variable-length Opaque Data
-.IX XDR "variable-length opaque data"
-.IX XDR "opaque data, variable length"
-.LP
-The standard also provides for variable-length (counted) opaque data,
-defined as a sequence of n (numbered 0 through n-1) arbitrary bytes
-to be the number n encoded as an unsigned integer (as described
-below), and followed by the n bytes of the sequence.
-.LP
-Byte m of the sequence always precedes byte m+1 of the sequence, and
-byte 0 of the sequence always follows the sequence's length (count).
-enough (0 to 3) residual zero bytes, r, to make the total byte count
-a multiple of four. Variable-length opaque data is declared in the
-following way:
-.DS
-.ft CW
-opaque identifier<m>;
-.DE
-or
-.DS
-.ft CW
-opaque identifier<>;
-.DE
-The constant m denotes an upper bound of the number of bytes that the
-sequence may contain. If m is not specified, as in the second
-declaration, it is assumed to be (2**32) - 1, the maximum length.
-The constant m would normally be found in a protocol specification.
-For example, a filing protocol may state that the maximum data
-transfer size is 8192 bytes, as follows:
-.DS
-.ft CW
-opaque filedata<8192>;
-.DE
-This can be illustrated as follows:
-.ie t .DS
-.el .DS L
-\fIVariable-Length Opaque\fP
-
-\f(CW0 1 2 3 4 5 ...
-+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
-| length n |byte0|byte1|...| n-1 | 0 |...| 0 |
-+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
-|<-------4 bytes------->|<------n bytes------>|<---r bytes--->|
-|<----n+r (where (n+r) mod 4 = 0)---->|\fP
-.DE
-.LP
-It is an error to encode a length greater than the maximum
-described in the specification.
-.NH 2
-\&String
-.IX XDR string
-.LP
-The standard defines a string of n (numbered 0 through n-1) ASCII
-bytes to be the number n encoded as an unsigned integer (as described
-above), and followed by the n bytes of the string. Byte m of the
-string always precedes byte m+1 of the string, and byte 0 of the
-string always follows the string's length. If n is not a multiple of
-four, then the n bytes are followed by enough (0 to 3) residual zero
-bytes, r, to make the total byte count a multiple of four. Counted
-byte strings are declared as follows:
-.DS
-.ft CW
-string object<m>;
-.DE
-or
-.DS
-.ft CW
-string object<>;
-.DE
-The constant m denotes an upper bound of the number of bytes that a
-string may contain. If m is not specified, as in the second
-declaration, it is assumed to be (2**32) - 1, the maximum length.
-The constant m would normally be found in a protocol specification.
-For example, a filing protocol may state that a file name can be no
-longer than 255 bytes, as follows:
-.DS
-.ft CW
-string filename<255>;
-.DE
-Which can be illustrated as:
-.ie t .DS
-.el .DS L
-\fIA String\fP
-
-\f(CW0 1 2 3 4 5 ...
-+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
-| length n |byte0|byte1|...| n-1 | 0 |...| 0 |
-+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
-|<-------4 bytes------->|<------n bytes------>|<---r bytes--->|
-|<----n+r (where (n+r) mod 4 = 0)---->|\fP
-.DE
-.LP
-It is an error to encode a length greater than the maximum
-described in the specification.
-.NH 2
-\&Fixed-length Array
-.IX XDR "fixed-length array"
-.IX XDR "array, fixed length"
-.LP
-Declarations for fixed-length arrays of homogeneous elements are in
-the following form:
-.DS
-.ft CW
-type-name identifier[n];
-.DE
-Fixed-length arrays of elements numbered 0 through n-1 are encoded by
-individually encoding the elements of the array in their natural
-order, 0 through n-1. Each element's size is a multiple of four
-bytes. Though all elements are of the same type, the elements may
-have different sizes. For example, in a fixed-length array of
-strings, all elements are of type "string", yet each element will
-vary in its length.
-.ie t .DS
-.el .DS L
-\fIFixed-Length Array\fP
-
-\f(CW+---+---+---+---+---+---+---+---+...+---+---+---+---+
-| element 0 | element 1 |...| element n-1 |
-+---+---+---+---+---+---+---+---+...+---+---+---+---+
-|<--------------------n elements------------------->|\fP
-.DE
-.NH 2
-\&Variable-length Array
-.IX XDR "variable-length array"
-.IX XDR "array, variable length"
-.LP
-Counted arrays provide the ability to encode variable-length arrays
-of homogeneous elements. The array is encoded as the element count n
-(an unsigned integer) followed by the encoding of each of the array's
-elements, starting with element 0 and progressing through element n-
-1. The declaration for variable-length arrays follows this form:
-.DS
-.ft CW
-type-name identifier<m>;
-.DE
-or
-.DS
-.ft CW
-type-name identifier<>;
-.DE
-The constant m specifies the maximum acceptable element count of an
-array; if m is not specified, as in the second declaration, it is
-assumed to be (2**32) - 1.
-.ie t .DS
-.el .DS L
-\fICounted Array\fP
-
-\f(CW0 1 2 3
-+--+--+--+--+--+--+--+--+--+--+--+--+...+--+--+--+--+
-| n | element 0 | element 1 |...|element n-1|
-+--+--+--+--+--+--+--+--+--+--+--+--+...+--+--+--+--+
-|<-4 bytes->|<--------------n elements------------->|\fP
-.DE
-It is an error to encode a value of n that is greater than the
-maximum described in the specification.
-.NH 2
-\&Structure
-.IX XDR structure
-.LP
-Structures are declared as follows:
-.DS
-.ft CW
-struct {
- component-declaration-A;
- component-declaration-B;
- \&...
-} identifier;
-.DE
-The components of the structure are encoded in the order of their
-declaration in the structure. Each component's size is a multiple of
-four bytes, though the components may be different sizes.
-.ie t .DS
-.el .DS L
-\fIStructure\fP
-
-\f(CW+-------------+-------------+...
-| component A | component B |...
-+-------------+-------------+...\fP
-.DE
-.NH 2
-\&Discriminated Union
-.IX XDR "discriminated union"
-.IX XDR union discriminated
-.LP
-A discriminated union is a type composed of a discriminant followed
-by a type selected from a set of prearranged types according to the
-value of the discriminant. The type of discriminant is either "int",
-"unsigned int", or an enumerated type, such as "bool". The component
-types are called "arms" of the union, and are preceded by the value
-of the discriminant which implies their encoding. Discriminated
-unions are declared as follows:
-.DS
-.ft CW
-union switch (discriminant-declaration) {
- case discriminant-value-A:
- arm-declaration-A;
- case discriminant-value-B:
- arm-declaration-B;
- \&...
- default: default-declaration;
-} identifier;
-.DE
-Each "case" keyword is followed by a legal value of the discriminant.
-The default arm is optional. If it is not specified, then a valid
-encoding of the union cannot take on unspecified discriminant values.
-The size of the implied arm is always a multiple of four bytes.
-.LP
-The discriminated union is encoded as its discriminant followed by
-the encoding of the implied arm.
-.ie t .DS
-.el .DS L
-\fIDiscriminated Union\fP
-
-\f(CW0 1 2 3
-+---+---+---+---+---+---+---+---+
-| discriminant | implied arm |
-+---+---+---+---+---+---+---+---+
-|<---4 bytes--->|\fP
-.DE
-.NH 2
-\&Void
-.IX XDR void
-.LP
-An XDR void is a 0-byte quantity. Voids are useful for describing
-operations that take no data as input or no data as output. They are
-also useful in unions, where some arms may contain data and others do
-not. The declaration is simply as follows:
-.DS
-.ft CW
-void;
-.DE
-Voids are illustrated as follows:
-.ie t .DS
-.el .DS L
-\fIVoid\fP
-
-\f(CW ++
- ||
- ++
---><-- 0 bytes\fP
-.DE
-.NH 2
-\&Constant
-.IX XDR constant
-.LP
-The data declaration for a constant follows this form:
-.DS
-.ft CW
-const name-identifier = n;
-.DE
-"const" is used to define a symbolic name for a constant; it does not
-declare any data. The symbolic constant may be used anywhere a
-regular constant may be used. For example, the following defines a
-symbolic constant DOZEN, equal to 12.
-.DS
-.ft CW
-const DOZEN = 12;
-.DE
-.NH 2
-\&Typedef
-.IX XDR typedef
-.LP
-"typedef" does not declare any data either, but serves to define new
-identifiers for declaring data. The syntax is:
-.DS
-.ft CW
-typedef declaration;
-.DE
-The new type name is actually the variable name in the declaration
-part of the typedef. For example, the following defines a new type
-called "eggbox" using an existing type called "egg":
-.DS
-.ft CW
-typedef egg eggbox[DOZEN];
-.DE
-Variables declared using the new type name have the same type as the
-new type name would have in the typedef, if it was considered a
-variable. For example, the following two declarations are equivalent
-in declaring the variable "fresheggs":
-.DS
-.ft CW
-eggbox fresheggs;
-egg fresheggs[DOZEN];
-.DE
-When a typedef involves a struct, enum, or union definition, there is
-another (preferred) syntax that may be used to define the same type.
-In general, a typedef of the following form:
-.DS
-.ft CW
-typedef <<struct, union, or enum definition>> identifier;
-.DE
-may be converted to the alternative form by removing the "typedef"
-part and placing the identifier after the "struct", "union", or
-"enum" keyword, instead of at the end. For example, here are the two
-ways to define the type "bool":
-.DS
-.ft CW
-typedef enum { /* \fIusing typedef\fP */
- FALSE = 0,
- TRUE = 1
- } bool;
-
-enum bool { /* \fIpreferred alternative\fP */
- FALSE = 0,
- TRUE = 1
- };
-.DE
-The reason this syntax is preferred is one does not have to wait
-until the end of a declaration to figure out the name of the new
-type.
-.NH 2
-\&Optional-data
-.IX XDR "optional data"
-.IX XDR "data, optional"
-.LP
-Optional-data is one kind of union that occurs so frequently that we
-give it a special syntax of its own for declaring it. It is declared
-as follows:
-.DS
-.ft CW
-type-name *identifier;
-.DE
-This is equivalent to the following union:
-.DS
-.ft CW
-union switch (bool opted) {
- case TRUE:
- type-name element;
- case FALSE:
- void;
-} identifier;
-.DE
-It is also equivalent to the following variable-length array
-declaration, since the boolean "opted" can be interpreted as the
-length of the array:
-.DS
-.ft CW
-type-name identifier<1>;
-.DE
-Optional-data is not so interesting in itself, but it is very useful
-for describing recursive data-structures such as linked-lists and
-trees. For example, the following defines a type "stringlist" that
-encodes lists of arbitrary length strings:
-.DS
-.ft CW
-struct *stringlist {
- string item<>;
- stringlist next;
-};
-.DE
-It could have been equivalently declared as the following union:
-.DS
-.ft CW
-union stringlist switch (bool opted) {
- case TRUE:
- struct {
- string item<>;
- stringlist next;
- } element;
- case FALSE:
- void;
-};
-.DE
-or as a variable-length array:
-.DS
-.ft CW
-struct stringlist<1> {
- string item<>;
- stringlist next;
-};
-.DE
-Both of these declarations obscure the intention of the stringlist
-type, so the optional-data declaration is preferred over both of
-them. The optional-data type also has a close correlation to how
-recursive data structures are represented in high-level languages
-such as Pascal or C by use of pointers. In fact, the syntax is the
-same as that of the C language for pointers.
-.NH 2
-\&Areas for Future Enhancement
-.IX XDR futures
-.LP
-The XDR standard lacks representations for bit fields and bitmaps,
-since the standard is based on bytes. Also missing are packed (or
-binary-coded) decimals.
-.LP
-The intent of the XDR standard was not to describe every kind of data
-that people have ever sent or will ever want to send from machine to
-machine. Rather, it only describes the most commonly used data-types
-of high-level languages such as Pascal or C so that applications
-written in these languages will be able to communicate easily over
-some medium.
-.LP
-One could imagine extensions to XDR that would let it describe almost
-any existing protocol, such as TCP. The minimum necessary for this
-are support for different block sizes and byte-orders. The XDR
-discussed here could then be considered the 4-byte big-endian member
-of a larger XDR family.
-.NH 1
-\&Discussion
-.sp 2
-.NH 2
-\&Why a Language for Describing Data?
-.IX XDR language
-.LP
-There are many advantages in using a data-description language such
-as XDR versus using diagrams. Languages are more formal than
-diagrams and lead to less ambiguous descriptions of data.
-Languages are also easier to understand and allow one to think of
-other issues instead of the low-level details of bit-encoding.
-Also, there is a close analogy between the types of XDR and a
-high-level language such as C or Pascal. This makes the
-implementation of XDR encoding and decoding modules an easier task.
-Finally, the language specification itself is an ASCII string that
-can be passed from machine to machine to perform on-the-fly data
-interpretation.
-.NH 2
-\&Why Only one Byte-Order for an XDR Unit?
-.IX XDR "byte order"
-.LP
-Supporting two byte-orderings requires a higher level protocol for
-determining in which byte-order the data is encoded. Since XDR is
-not a protocol, this can't be done. The advantage of this, though,
-is that data in XDR format can be written to a magnetic tape, for
-example, and any machine will be able to interpret it, since no
-higher level protocol is necessary for determining the byte-order.
-.NH 2
-\&Why does XDR use Big-Endian Byte-Order?
-.LP
-Yes, it is unfair, but having only one byte-order means you have to
-be unfair to somebody. Many architectures, such as the Motorola
-68000 and IBM 370, support the big-endian byte-order.
-.NH 2
-\&Why is the XDR Unit Four Bytes Wide?
-.LP
-There is a tradeoff in choosing the XDR unit size. Choosing a small
-size such as two makes the encoded data small, but causes alignment
-problems for machines that aren't aligned on these boundaries. A
-large size such as eight means the data will be aligned on virtually
-every machine, but causes the encoded data to grow too big. We chose
-four as a compromise. Four is big enough to support most
-architectures efficiently, except for rare machines such as the
-eight-byte aligned Cray. Four is also small enough to keep the
-encoded data restricted to a reasonable size.
-.NH 2
-\&Why must Variable-Length Data be Padded with Zeros?
-.IX XDR "variable-length data"
-.LP
-It is desirable that the same data encode into the same thing on all
-machines, so that encoded data can be meaningfully compared or
-checksummed. Forcing the padded bytes to be zero ensures this.
-.NH 2
-\&Why is there No Explicit Data-Typing?
-.LP
-Data-typing has a relatively high cost for what small advantages it
-may have. One cost is the expansion of data due to the inserted type
-fields. Another is the added cost of interpreting these type fields
-and acting accordingly. And most protocols already know what type
-they expect, so data-typing supplies only redundant information.
-However, one can still get the benefits of data-typing using XDR. One
-way is to encode two things: first a string which is the XDR data
-description of the encoded data, and then the encoded data itself.
-Another way is to assign a value to all the types in XDR, and then
-define a universal type which takes this value as its discriminant
-and for each value, describes the corresponding data type.
-.NH 1
-\&The XDR Language Specification
-.IX XDR language
-.sp 1
-.NH 2
-\&Notational Conventions
-.IX "XDR language" notation
-.LP
-This specification uses an extended Backus-Naur Form notation for
-describing the XDR language. Here is a brief description of the
-notation:
-.IP 1.
-The characters
-.I | ,
-.I ( ,
-.I ) ,
-.I [ ,
-.I ] ,
-.I " ,
-and
-.I *
-are special.
-.IP 2.
-Terminal symbols are strings of any characters surrounded by
-double quotes.
-.IP 3.
-Non-terminal symbols are strings of non-special characters.
-.IP 4.
-Alternative items are separated by a vertical bar ("\fI|\fP").
-.IP 5.
-Optional items are enclosed in brackets.
-.IP 6.
-Items are grouped together by enclosing them in parentheses.
-.IP 7.
-A
-.I *
-following an item means 0 or more occurrences of that item.
-.LP
-For example, consider the following pattern:
-.DS L
-"a " "very" (", " " very")* [" cold " "and"] " rainy " ("day" | "night")
-.DE
-.LP
-An infinite number of strings match this pattern. A few of them
-are:
-.DS
-"a very rainy day"
-"a very, very rainy day"
-"a very cold and rainy day"
-"a very, very, very cold and rainy night"
-.DE
-.NH 2
-\&Lexical Notes
-.IP 1.
-Comments begin with '/*' and terminate with '*/'.
-.IP 2.
-White space serves to separate items and is otherwise ignored.
-.IP 3.
-An identifier is a letter followed by an optional sequence of
-letters, digits or underbar ('_'). The case of identifiers is
-not ignored.
-.IP 4.
-A constant is a sequence of one or more decimal digits,
-optionally preceded by a minus-sign ('-').
-.NH 2
-\&Syntax Information
-.IX "XDR language" syntax
-.DS
-.ft CW
-declaration:
- type-specifier identifier
- | type-specifier identifier "[" value "]"
- | type-specifier identifier "<" [ value ] ">"
- | "opaque" identifier "[" value "]"
- | "opaque" identifier "<" [ value ] ">"
- | "string" identifier "<" [ value ] ">"
- | type-specifier "*" identifier
- | "void"
-.DE
-.DS
-.ft CW
-value:
- constant
- | identifier
-
-type-specifier:
- [ "unsigned" ] "int"
- | [ "unsigned" ] "hyper"
- | "float"
- | "double"
- | "bool"
- | enum-type-spec
- | struct-type-spec
- | union-type-spec
- | identifier
-.DE
-.DS
-.ft CW
-enum-type-spec:
- "enum" enum-body
-
-enum-body:
- "{"
- ( identifier "=" value )
- ( "," identifier "=" value )*
- "}"
-.DE
-.DS
-.ft CW
-struct-type-spec:
- "struct" struct-body
-
-struct-body:
- "{"
- ( declaration ";" )
- ( declaration ";" )*
- "}"
-.DE
-.DS
-.ft CW
-union-type-spec:
- "union" union-body
-
-union-body:
- "switch" "(" declaration ")" "{"
- ( "case" value ":" declaration ";" )
- ( "case" value ":" declaration ";" )*
- [ "default" ":" declaration ";" ]
- "}"
-
-constant-def:
- "const" identifier "=" constant ";"
-.DE
-.DS
-.ft CW
-type-def:
- "typedef" declaration ";"
- | "enum" identifier enum-body ";"
- | "struct" identifier struct-body ";"
- | "union" identifier union-body ";"
-
-definition:
- type-def
- | constant-def
-
-specification:
- definition *
-.DE
-.NH 3
-\&Syntax Notes
-.IX "XDR language" syntax
-.LP
-.IP 1.
-The following are keywords and cannot be used as identifiers:
-"bool", "case", "const", "default", "double", "enum", "float",
-"hyper", "opaque", "string", "struct", "switch", "typedef", "union",
-"unsigned" and "void".
-.IP 2.
-Only unsigned constants may be used as size specifications for
-arrays. If an identifier is used, it must have been declared
-previously as an unsigned constant in a "const" definition.
-.IP 3.
-Constant and type identifiers within the scope of a specification
-are in the same name space and must be declared uniquely within this
-scope.
-.IP 4.
-Similarly, variable names must be unique within the scope of
-struct and union declarations. Nested struct and union declarations
-create new scopes.
-.IP 5.
-The discriminant of a union must be of a type that evaluates to
-an integer. That is, "int", "unsigned int", "bool", an enumerated
-type or any typedefed type that evaluates to one of these is legal.
-Also, the case values must be one of the legal values of the
-discriminant. Finally, a case value may not be specified more than
-once within the scope of a union declaration.
-.NH 1
-\&An Example of an XDR Data Description
-.LP
-Here is a short XDR data description of a thing called a "file",
-which might be used to transfer files from one machine to another.
-.ie t .DS
-.el .DS L
-.ft CW
-
-const MAXUSERNAME = 32; /*\fI max length of a user name \fP*/
-const MAXFILELEN = 65535; /*\fI max length of a file \fP*/
-const MAXNAMELEN = 255; /*\fI max length of a file name \fP*/
-
-.ft I
-/*
- * Types of files:
- */
-.ft CW
-
-enum filekind {
- TEXT = 0, /*\fI ascii data \fP*/
- DATA = 1, /*\fI raw data \fP*/
- EXEC = 2 /*\fI executable \fP*/
-};
-
-.ft I
-/*
- * File information, per kind of file:
- */
-.ft CW
-
-union filetype switch (filekind kind) {
- case TEXT:
- void; /*\fI no extra information \fP*/
- case DATA:
- string creator<MAXNAMELEN>; /*\fI data creator \fP*/
- case EXEC:
- string interpretor<MAXNAMELEN>; /*\fI program interpretor \fP*/
-};
-
-.ft I
-/*
- * A complete file:
- */
-.ft CW
-
-struct file {
- string filename<MAXNAMELEN>; /*\fI name of file \fP*/
- filetype type; /*\fI info about file \fP*/
- string owner<MAXUSERNAME>; /*\fI owner of file \fP*/
- opaque data<MAXFILELEN>; /*\fI file data \fP*/
-};
-.DE
-.LP
-Suppose now that there is a user named "john" who wants to store
-his lisp program "sillyprog" that contains just the data "(quit)".
-His file would be encoded as follows:
-.TS
-box tab (&) ;
-lfI lfI lfI lfI
-rfL rfL rfL l .
-Offset&Hex Bytes&ASCII&Description
-_
-0&00 00 00 09&....&Length of filename = 9
-4&73 69 6c 6c&sill&Filename characters
-8&79 70 72 6f&ypro& ... and more characters ...
-12&67 00 00 00&g...& ... and 3 zero-bytes of fill
-16&00 00 00 02&....&Filekind is EXEC = 2
-20&00 00 00 04&....&Length of interpretor = 4
-24&6c 69 73 70&lisp&Interpretor characters
-28&00 00 00 04&....&Length of owner = 4
-32&6a 6f 68 6e&john&Owner characters
-36&00 00 00 06&....&Length of file data = 6
-40&28 71 75 69&(qui&File data bytes ...
-44&74 29 00 00&t)..& ... and 2 zero-bytes of fill
-.TE
-.NH 1
-\&References
-.LP
-[1] Brian W. Kernighan & Dennis M. Ritchie, "The C Programming
-Language", Bell Laboratories, Murray Hill, New Jersey, 1978.
-.LP
-[2] Danny Cohen, "On Holy Wars and a Plea for Peace", IEEE Computer,
-October 1981.
-.LP
-[3] "IEEE Standard for Binary Floating-Point Arithmetic", ANSI/IEEE
-Standard 754-1985, Institute of Electrical and Electronics
-Engineers, August 1985.
-.LP
-[4] "Courier: The Remote Procedure Call Protocol", XEROX
-Corporation, XSIS 038112, December 1981.