The book defines 3 customized functions:
int serv_listen(const char *name);
//Returns: file descriptor to listen on if OK, negative value on error
int serv_accept(int listenfd, uid_t *uidptr);
//Returns: new file descriptor if OK, negative value on error
int cli_conn(const char *name);
//Returns: file descriptor if OK, negative value on error
The
serv_acceptfunction (Figure 17.9) is used by a server to wait for a client’s connect request to arrive. When one arrives, the system automatically creates a new UNIX domain socket, connects it to the client’s socket, and returns the new socket to the server. Additionally, the effective user ID of the client is stored in the memory to whichuidptrpoints.
serv_accept function code and description:
#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <errno.h>
#define STALE 30 /* client's name can't be older than this (sec) */
/*
* Wait for a client connection to arrive, and accept it.
* We also obtain the client's user ID from the pathname
* that it must bind before calling us.
* Returns new fd if all OK, <0 on error
*/
int
serv_accept(int listenfd, uid_t *uidptr)
{
int clifd, err, rval;
socklen_t len;
time_t staletime;
struct sockaddr_un un;
struct stat statbuf;
char *name;
/* allocate enough space for longest name plus terminating null */
if ((name = malloc(sizeof(un.sun_path + 1))) == NULL)
return(-1);
len = sizeof(un);
if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) {
free(name);
return(-2); /* often errno=EINTR, if signal caught */
}
/* obtain the client's uid from its calling address */
len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
memcpy(name, un.sun_path, len);
name[len] = 0; /* null terminate */
if (stat(name, &statbuf) < 0) {
rval = -3;
goto errout;
}
#ifdef S_ISSOCK /* not defined for SVR4 */
if (S_ISSOCK(statbuf.st_mode) == 0) {
rval = -4; /* not a socket */
goto errout;
}
#endif
if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) ||
(statbuf.st_mode & S_IRWXU) != S_IRWXU) {
rval = -5; /* is not rwx------ */
goto errout;
}
staletime = time(NULL) - STALE;
if (statbuf.st_atime < staletime ||
statbuf.st_ctime < staletime ||
statbuf.st_mtime < staletime) {
rval = -6; /* i-node is too old */
goto errout;
}
if (uidptr != NULL)
*uidptr = statbuf.st_uid; /* return uid of caller */
unlink(name); /* we're done with pathname now */
free(name);
return(clifd);
errout:
err = errno;
close(clifd);
free(name);
errno = err;
return(rval);
}
... Then we call
statto verify that the pathname is indeed a socket and that the permissions allow only user-read, user-write, and user-execute. We also verify that the three times associated with the socket are no older than 30 seconds.If all these checks are OK, we assume that the identity of the client (its effective user ID) is the owner of the socket.
Why does the server code unlink(name) the file attached to the client's socket?
Other 2 functions code are provided via a link:
https://wandbox.org/permlink/jq5BajJYLgoh4yO6
Why does the server code
unlink(name)the file attached to the client's socket?
It's more accurate to say that the server is deleting the filepath attached to the client's socket. Or more colloquially, the client's socket's name.
Recall that unlink() does not delete named objects which are currently open in some process; the client's socket is presumably still open in the client, so unlink(name) doesn't yet delete the socket. Rather, it ensures that the socket will be deleted when it is no longer being used by a running process.
What it does do immediately is free up the name, so that the name can be reused with a different socket.
So why do that? Mostly so that the filesystem doesn't fill up with zombie socket names. It doesn't help the current client reuse the name (for example to connect to a different service) because the client anyway unlinks the name before attempting to use it. But the zombie name could be a problem for a different future client process with a different uid which happens to be assigned the same pid. That future process might not have sufficient permissions to unlink the name, in which case it will end up not being able to use this IPC mechanism (at least with this library).
Ok, so why is it unlinked by the server? The server makes use of the filepath for the stat call, and the client has no way of knowing when that happens. Since it's basically a good idea to unlink the name as soon as possible, it's better in this case for the server to unlink the name; it knows when it no longer needs the name.
Of course, the code as presented is not perfect. There are execution paths which will result in some names not being unlinked (for example, if the server process crashes at a bad time). But these should be rare. Experience shows that clients crash much more often than servers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With