To implement the server, we must define call handler functions for each of the message types we defined in the protocol. The functions for handling dispatched calls are contained in fserver-dispatch.c. These handlers are bound together into a dispatch specification which associates each incoming message tag with a particular function:
Let's examine the implementation of fserv_open_srv() call as an example:
The handler takes four parameters:
The data
field of the LWMsgParams structure contains the actual payload of the call, so we cast it to the appropriate type:
We will need to perform a permissions check when opening a file. This means querying for the identity of the caller. In order to get the identity of the caller, we need access to the session through the lwmsg_call_get_session() function:
Now that we have the request parameters and the session, we can call a helper function to perform our permissions check.
The fserv_check_permissions()
function begins by extracting the peer security token from the session:
If the token is NULL, the session was not authenticated and an error is returned. The lwmsg_security_token_get_type() is used to query the type of the security token. For UNIX domain socket connections, it should be "local":
Now that we know we have a "local" security token, we can use lwmsg_local_token_get_eid() to extract the effective uid and gid of the caller:
The rest of the function performs the actual permission check. If fserv_check_permissions()
returns a failure code, we allocate a status reply data structure:
Finally, we fill in the reply and set the out parameters structure. The tag
is set to the generic error reply tag defined in the protocol, and the data
is set to the status reply structure.
If the permissions check passed, we open the file:
If the file open fails, we set the output parameters to an error status response as before. If it succeeds, we allocate and fill in a handle structure:
Before the handle can be given to the caller, we must register it with the lwmsg session. This will give us an LWMsgHandle which can be placed in the output parameters. This is performed with the lwmsg_session_register_handle() call:
Notice that the second parameter to the call is the string form of the name passed to LWMSG_HANDLE() in the type specification. The fourth parameter is a callback function which will be invoked when the handle is no longer used. A handle can become unused explicitly by dropping its reference count to zero. A calling client disconnecting from our server will terminate its session and cause the handle to become unused implicitly. In either case, the callback will be invoked to clean up the unused handle.
Now that the handle is registered, it can safely be placed in the output parameters:
Before returning, there is one last step to perform. All handles have a reference count which is used to track how many users of the handle remain. The reference count can be increased with lwmsg_session_retain_handle() and decreased with lwmsg_session_release_handle(). When a handle is registered, it has an implicit reference count of 1. Because we are about to give a reference to the handle to the caller, we need to increment the reference count:
Abstractly, the handle will have two references after the call returns. The client's reference represents its ability to use to the handle in subsequent calls to the server. The server's reference represents its willingness to accept the handle in subsequent calls from the client. When the client makes a call to close the handle, they will both release their references and the handle will be cleaned up.
Concretely, the picture is slightly different. The LWMsg call discipline specifies that the caller assumes ownership of the output parameters of a call and is responsible for freeing them. Because the server can only send the output parameters to the client as a stream of serialized data, any server-side memory must be automatically freed afterwards. The client then allocates its own memory and unpacks the serialized data into it.
Analagously, the server will send a serialized representation of the handle to the client and then automatically decrement the reference count on the handle to 1. The client will create a proxy handle structure on its end with an implicit reference count of 1. Both the client and server will believe the handle to have exactly 1 reference, and therefore both will keep the handle alive for use in subsequent calls. When the client makes a close call, they will both drop their single references; the server will clean up the physical handle and the client will clean up its proxy.
These memory ownership and handle reference count semantics are consistent regardless of the locations of the client and server. For example, the client and server could be in the same process. As an optimization, output parameters from a call could be given directly to the client without serialization. The client would assume direct ownership of any memory and would receive a reference to the physical handle, not a proxy. Both client and server would agree that the handle has 2 references, and the handle would be cleaned up when both references are released as part of a close call.
With all the call handlers defined, the only remaining task is creating and starting the call listener. This is performed in fserver.c which contains the definition of main()
for the fserver
program.
In order to take advantage of the logging available inside lwmsg, we first create an LWMsgContext and set a logging function on it:
As with the client side, the server is set up as an LWMsgPeer. The procedure is similar – create a protocol structure, add a protocol spec, and create the peer structure with the protocol:
This time, we add our dispatch spec to the peer structure to register our call handlers:
Next we add an endpoint on which we will listen for clients:
Now the peer structure is passed to the run()
helper function, which begins by making it listen for calls:
The program then enters a signal wait loop until it receives SIGTERM
or SIGINT
, indicating that it should shut down. It then stops listening for calls before exiting:
Likewise Message Library, part of the Likewise platform
Copyright © 2017 Likewise Software. All rights reserved.