Programming Interfaces Guide

Sockets and Servers

Most servers are accessed at well-known Internet port numbers or UNIX family names. The service rlogin is an example of a well-known UNIX family name. The main loop of a remote login server is shown in Example 8–7.

The server dissociates from the controlling terminal of its invoker unless the server is operating in DEBUG mode.

(void) close(0);
(void) close(1);
(void) close(2);
(void) open("/", O_RDONLY);
(void) dup2(0, 1);
(void) dup2(0, 2);
setsid();

Dissociating prevents the server from receiving signals from the process group of the controlling terminal. After a server has dissociated from the controlling terminal, the server cannot send reports of errors to the terminal. The dissociated server must log errors with syslog(3C).

The server gets its service definition by calling getaddrinfo(3SOCKET).

bzero(&hints, sizeof (hints));
hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(NULL, "rlogin", &hints, &api);

The result, which is returned in api, contains the Internet port at which the program listens for service requests. Some standard port numbers are defined in /usr/include/netinet/in.h.

The server then creates a socket, and listens for service requests. The bind(3SOCKET) routine ensures that the server listens at the expected location. Because the remote login server listens at a restricted port number, the server runs as superuser. The main body of the server is the following loop.


Example 8–7 Server Main Loop

/* Wait for a connection request. */
for (;;) {
    faddrlen = sizeof (faddr);
    new_sock = accept(sock, (struct sockaddr *)api->ai_addr,
            api->ai_addrlen)
    if (new_sock == -1) {
        if (errno != EINTR && errno != ECONNABORTED) {
            perror("rlogind: accept");
        }
        continue;
    }
    if (fork() == 0) {
        close (sock);
        doit (new_sock, &faddr);
    }
    close (new_sock);
}
/*NOTREACHED*/

accept(3SOCKET) blocks messages until a client requests service. Furthermore, accept(3SOCKET) returns a failure indication if accept is interrupted by a signal, such as SIGCHLD. The return value from accept(3SOCKET) is checked, and an error is logged with syslog(3C), if an error occurs.

The server then forks a child process, and invokes the main body of the remote login protocol processing. The socket used by the parent to queue connection requests is closed in the child. The socket created by accept(3SOCKET) is closed in the parent. The address of the client is passed to the server application's doit() routine, which authenticates the client.