Use a lock file to be able to log an error when the UDS socket's path is in use by another instance, avoid silently unlink()ing it.
There's no way for multiple instances (e.g. in a multi-domain setup) to bind() to the same address, there's nothing like SO_REUSEADDR for unix sockets. Just unlinking the socket makes for a behaviour that's initially confusing to the user, and creates a race condition where one instane's bind() call can happen between another instane's unlink() and bind() causing the latter to fail. Resort to logging an error when uds_{,ro_}address is in use. Signed-off-by: Andrew Zaborowski <andrew.zaborow...@intel.com> --- uds.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/uds.c b/uds.c index 6d39dc8..4960bbe 100644 --- a/uds.c +++ b/uds.c @@ -23,7 +23,9 @@ #include <sys/socket.h> #include <sys/stat.h> #include <sys/un.h> +#include <sys/file.h> #include <unistd.h> +#include <fcntl.h> #include "address.h" #include "contain.h" @@ -34,19 +36,32 @@ struct uds { struct transport t; struct address address; + int lock_fd; }; static int uds_close(struct transport *t, struct fdarray *fda) { + struct uds *uds = container_of(t, struct uds, t); struct sockaddr_un sa; socklen_t len = sizeof(sa); + char *lock_name; if (!getsockname(fda->fd[FD_GENERAL], (struct sockaddr *) &sa, &len) && sa.sun_family == AF_LOCAL) { unlink(sa.sun_path); + + if (asprintf(&lock_name, "%s.lock", sa.sun_path) != -1) { + /* Must be done after the socket was unlinked. The lock + * isn't released until the close() below but another + * process can no longer open our lock file to check this. + */ + unlink(lock_name); + free(lock_name); + } } close(fda->fd[FD_GENERAL]); + close(uds->lock_fd); return 0; } @@ -60,7 +75,9 @@ static int uds_open(struct transport *t, struct interface *iface, struct fdarray const char* file_mode_cfg; struct sockaddr_un sa; mode_t file_mode; - int fd, err; + int fd, err, lock_fd; + char *lock_name; + int ret = -1; fd = socket(AF_LOCAL, SOCK_DGRAM, 0); if (fd < 0) { @@ -71,13 +88,31 @@ static int uds_open(struct transport *t, struct interface *iface, struct fdarray sa.sun_family = AF_LOCAL; strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1); + if (asprintf(&lock_name, "%s.lock", name) == -1) { + pr_err("uds: asprinf error"); + return -1; + } + lock_fd = open(lock_name, O_RDWR | O_CREAT | O_CLOEXEC, 0600); + if (lock_fd == -1) { + pr_err("uds: failed to open(%s): %m", lock_name); + goto free_lock_name; + } + + if (flock(lock_fd, LOCK_EX | LOCK_NB) != 0) { + pr_err("uds: can't acquire lock, another instance may be using %s", + name); + goto free_lock_name; + } + unlink(name); err = bind(fd, (struct sockaddr *) &sa, sizeof(sa)); if (err < 0) { pr_err("uds: bind failed: %m"); close(fd); - return -1; + close(lock_fd); + unlink(lock_name); + goto free_lock_name; } file_mode_cfg = "uds_file_mode"; @@ -96,7 +131,12 @@ static int uds_open(struct transport *t, struct interface *iface, struct fdarray chmod(name, file_mode); fda->fd[FD_EVENT] = -1; fda->fd[FD_GENERAL] = fd; - return 0; + uds->lock_fd = lock_fd; + ret = 0; + +free_lock_name: + free(lock_name); + return ret; } static int uds_recv(struct transport *t, int fd, void *buf, int buflen, -- 2.34.1 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel