An Open Server, Version 1
Using file descriptor passing, we now develop an open server—a program that is executed by a process to open one or more files. Instead of sending the contents of the file back to the calling process, however, this server sends back an open file descriptor. As a result, the open server can work with any type of file (such as a device or a socket) and not simply regular files. The client and server exchange a minimum amount of information using IPC: the filename and open mode sent by the client, and the descriptor returned by the server. The contents of the file are not exchanged using IPC.
There are several advantages in designing the server to be a separate executable program (either one that is executed by the client, as we develop in this section, or a daemon server, which we develop in the next section).
-
The server can easily be contacted by any client, similar to the client calling a library function. We are not hard-coding a particular service into the application, but designing a general facility that others can reuse.
-
If we need to change the server, only a single program is affected. Conversely, updating a library function can require that all programs that call the function be updated (i.e., relinked with the link editor). Shared libraries can simplify this updating (Section 7.7).
-
The server can be a set-user-ID program, providing it with additional permissions that the client does not have. Note that a library function (or shared library function) can’t provide this capability. The client process creates an fd-pipe and then calls fork and exec to invoke the server. The client sends requests across the fd-pipe using one end, and the server sends back responses over the fd-pipe using the other end.
We define the following application protocol between the client and the server.
1.The client sends a request of the form ‘‘open <pathname> <openmode>\0’’ across the fd-pipe to the server. The <openmode> is the numeric value, in ASCII decimal, of the second argument to the open function. This request string is terminated by a null byte.
2.The server sends back an open descriptor or an error by calling either send_fd or send_err.
The total length of the code is too long, so I suggest you read the book instead of reading this article.
An Open Server, Version 2
In the previous section, we developed an open server that was invoked by a fork and exec by the client, demonstrating how we can pass file descriptors from a child to a parent. In this section, we develop an open server as a daemon process. One server handles all clients. We expect this design to be more efficient, since a fork and an exec are avoided. We use a UNIX domain socket connection between the client and the server and demonstrate passing file descriptors between unrelated processes. We’ll use the three functions serv_listen, serv_accept, and cli_conn introduced in Section 17.3. This server also demonstrates how a single server can handle multiple clients, using both the select and poll functions from Section 14.4.
This version of the client is similar to the client from Section 17.5. Indeed, the file main.c is identical (Figure 17.18). We add the following line to the open.h header (Figure 17.17):
#define CS_OPEN "/tmp/opend.socket" /* server’s well-known name */
The Single UNIX Specification includes a set of conventions and guidelines that promote consistent command-line syntax. They include such suggestions as ‘‘Restrict each command-line option to a single alphanumeric character’’ and ‘‘All options should be preceded by a − character.’’
Luckily, the getopt function exists to help command developers process command-line options in a consistent manner.
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *options);
extern int optind, opterr, optopt;
extern char *optarg;