From: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> Update `unix_stream_listen()` to take an options structure to override default behaviors. This includes the size of the `listen()` backlog and whether it should always unlink the socket file before trying to create a new one. Also eliminate calls to `die()` if it cannot create a socket. Normally, `unix_stream_listen()` always tries to `unlink()` the socket-path before calling `bind()`. If there is an existing server/daemon already bound and listening on that socket-path, our `unlink()` would have the effect of disassociating the existing server's bound-socket-fd from the socket-path without notifying the existing server. The existing server could continue to service existing connections (accepted-socket-fd's), but would not receive any futher new connections (since clients rendezvous via the socket-path). The existing server would effectively be offline but yet appear to be active. Furthermore, `unix_stream_listen()` creates an opportunity for a brief race condition for connecting clients if they try to connect in the interval between the forced `unlink()` and the subsequent `bind()` (which recreates the socket-path that is bound to a new socket-fd in the current process). Signed-off-by: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> --- builtin/credential-cache--daemon.c | 3 ++- unix-socket.c | 28 +++++++++++++++++++++------- unix-socket.h | 14 +++++++++++++- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c index c61f123a3b8..4c6c89ab0de 100644 --- a/builtin/credential-cache--daemon.c +++ b/builtin/credential-cache--daemon.c @@ -203,9 +203,10 @@ static int serve_cache_loop(int fd) static void serve_cache(const char *socket_path, int debug) { + struct unix_stream_listen_opts opts = UNIX_STREAM_LISTEN_OPTS_INIT; int fd; - fd = unix_stream_listen(socket_path); + fd = unix_stream_listen(socket_path, &opts); if (fd < 0) die_errno("unable to bind to '%s'", socket_path); diff --git a/unix-socket.c b/unix-socket.c index ef2aeb46bcd..8bcef18ea55 100644 --- a/unix-socket.c +++ b/unix-socket.c @@ -88,24 +88,35 @@ int unix_stream_connect(const char *path) return -1; } -int unix_stream_listen(const char *path) +int unix_stream_listen(const char *path, + const struct unix_stream_listen_opts *opts) { - int fd, saved_errno; + int fd = -1; + int saved_errno; + int bind_successful = 0; + int backlog; struct sockaddr_un sa; struct unix_sockaddr_context ctx; - unlink(path); - if (unix_sockaddr_init(&sa, path, &ctx) < 0) return -1; + fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) - die_errno("unable to create socket"); + goto fail; + + if (opts->force_unlink_before_bind) + unlink(path); if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) goto fail; + bind_successful = 1; - if (listen(fd, 5) < 0) + if (opts->listen_backlog_size > 0) + backlog = opts->listen_backlog_size; + else + backlog = 5; + if (listen(fd, backlog) < 0) goto fail; unix_sockaddr_cleanup(&ctx); @@ -114,7 +125,10 @@ int unix_stream_listen(const char *path) fail: saved_errno = errno; unix_sockaddr_cleanup(&ctx); - close(fd); + if (fd != -1) + close(fd); + if (bind_successful) + unlink(path); errno = saved_errno; return -1; } diff --git a/unix-socket.h b/unix-socket.h index e271aeec5a0..c28372ef48e 100644 --- a/unix-socket.h +++ b/unix-socket.h @@ -1,7 +1,19 @@ #ifndef UNIX_SOCKET_H #define UNIX_SOCKET_H +struct unix_stream_listen_opts { + int listen_backlog_size; + unsigned int force_unlink_before_bind:1; +}; + +#define UNIX_STREAM_LISTEN_OPTS_INIT \ +{ \ + .listen_backlog_size = 5, \ + .force_unlink_before_bind = 1, \ +} + int unix_stream_connect(const char *path); -int unix_stream_listen(const char *path); +int unix_stream_listen(const char *path, + const struct unix_stream_listen_opts *opts); #endif /* UNIX_SOCKET_H */ -- gitgitgadget