A remote procedure call request packet has the following form:
@begin(programexample)
struct rpc_request {
	char protocol_version;
	char request_type;
	long nonce;
	long uid;
	long capability;
	char argbuf[ARBITRARY_LENGTH];
};
@end(programexample)
Each argument is passed in the argument buffer as a
bytestream containing the data that forms the argument preceded by a 
16-bit length-of-data field.  Argument lengths always begin on an even-byte
boundary.  For example, the arguments "Chris" and "Joe" are passed as:
@begin(programexample)
 05   "Ch"   "ri"   "s"-  03   "Jo"   "e"-
0x5 0x4368 0x7269 0x7300 0x3 0x4a6f 0x6500
@end(programexample)
(this is VAX, PDP11, and NS16032 order).  
A number of unions make it easier to access most system calls from C
routines (although note that the order of the arguments here is @i(not)
the same as if these were real remote procedure calls).
@begin(programexample)
union {
	struct mode_struct{
		short mode_len;
		int	  mode;
		short name_len;
		char  name[filename_len];
	} modes;
	struct open_struct{
		short flag_len;
		int	  flags;
		short mode_len;
		int	  modes;
		short name_len;
		char  name[filename_len];
	} open;
	struct name_struct {
		short name_len;
		char  name[filename_len];
	} name;
} argbuf;
@end(programexample)
The @b(prpc) routine therefore looks something like this:
@begin(programexample)
prpc(system_call, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
{
	struct rpc_request request;
	struct rpc_reply *reply;
	
	if((rpc_reply = rpc_reply_alloc()) == NULL)
		do some kind of error return;
		
	/*
	 * Construct a request packet for this call.
	 *
	 * First, the invariant part.
	 */
	request.protocol_version = PROTOCOL_VERSION;
	request.request_type = system_call;
	request.nonce = make_nonce();
	request.uid = getuid();
	request.capability = 0;

	/*
	 * Now the variable part.
	 */
	switch(system_call)
	{
	case ACCESS:
	case CHMOD:
	case CHOWN:
	case READLINK:
		return(prpc_modes(&request, arg1, arg2));
		break;
		
	case LINK:
	case SYMLINK:
		 return(rpc_link(&request, arg1, arg2));

	case MKDIR:
	case OPEN:
	case RMDIR:
	case UNLINK:
	case STAT:
	}
}

prpc_modes(request, pathname, mode)
{
	char *filename;
	char *namespace_name;

	request->args.mode.mode_len = sizeof(int);
	request->args.mode.modes = mode;
	namespace_name = extract_namespace_name((char *) pathname);
	filename = extract_file_name((char *) pathname);
	namespace_reply = namespace_nameserver(namespace_name);
	switch(namespace_reply->type)
	{
	case NEGATIVE_REPLY:
		garbage collect all allocated structures;
		return an error indicating a non-existent file;
	case IPADDR:
		break;
	default:
		return an indication of an illegal reply from the 
			namespace server. 
	}
	/* Now we know how to get in touch with the namespace 
	 * manager.
	 */
	channel = reply->data.channel;
	
	request->args.pathname_length = strlen(filename);
	copy the string at filename into request->args.pathname.
		(this probably involves allocating a buffer for it).
	reply = send_to_channel(&channel, request);
	if(reply->type == PATHNAME)
	{
		if(is_local_filename(reply->data.string))
			return(reply);
		else
		{
			namespace_name = extract_namespace_name(reply->data.string);
			filename = extract_filename(reply->data.string);
			goto access_name_loop;
		}
	}
	else
		return(reply);
}
