From 3104aa430ae360599dd54369a8cb1fd61f07894c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 13 Jul 2015 14:53:30 +0000 Subject: automatically clean up left-over Unix domain socket files when trying to bind (fixes #3723) --- src/arm/gnunet-service-arm.c | 25 ++++++++++------- src/include/gnunet_network_lib.h | 15 +++++++++++ src/util/network.c | 58 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 84 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 0dffafeeb..a411546d7 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -603,7 +603,8 @@ accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * @param sl service entry for the service in question */ static void -create_listen_socket (struct sockaddr *sa, socklen_t addr_len, +create_listen_socket (struct sockaddr *sa, + socklen_t addr_len, struct ServiceList *sl) { static int on = 1; @@ -652,14 +653,18 @@ create_listen_socket (struct sockaddr *sa, socklen_t addr_len, GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); #endif - +#ifndef WINDOWS + if (AF_UNIX == sa->sa_family) + GNUNET_NETWORK_unix_precheck ((struct sockaddr_un *) sa); +#endif if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ - ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), - sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno)); + _("Unable to bind listening socket for service `%s' to address `%s': %s\n"), + sl->name, + GNUNET_a2s (sa, addr_len), + STRERROR (errno)); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); GNUNET_free (sa); return; @@ -1394,11 +1399,11 @@ setup_service (void *cls, "FORCESTART")) { sl->force_start = GNUNET_YES; - /* FIXME: we might like the pre-binding even for - _certain_ services that have force_start set, - otherwise interdependencies may again force - client's to retry connections during startup. */ - return; + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "NOARMBIND")) + return; } else { diff --git a/src/include/gnunet_network_lib.h b/src/include/gnunet_network_lib.h index f70509830..83d820a5b 100644 --- a/src/include/gnunet_network_lib.h +++ b/src/include/gnunet_network_lib.h @@ -106,6 +106,21 @@ char * GNUNET_NETWORK_shorten_unixpath (char *unixpath); +#ifndef WINDOWS +/** + * If services crash, they can leave a unix domain socket file on the + * disk. This needs to be manually removed, because otherwise both + * bind() and connect() for the respective address will fail. In this + * function, we test if such a left-over file exists, and if so, + * remove it (unless there is a listening service at the address). + * + * @param un unix domain socket address to check + */ +void +GNUNET_NETWORK_unix_precheck (const struct sockaddr_un *un); +#endif + + /** * Accept a new connection on a socket. Configure it for non-blocking * IO and mark it as non-inheritable to child processes (set the diff --git a/src/util/network.c b/src/util/network.c index d141732c2..489966426 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -147,6 +147,53 @@ GNUNET_NETWORK_shorten_unixpath (char *unixpath) } +#ifndef WINDOWS +/** + * If services crash, they can leave a unix domain socket file on the + * disk. This needs to be manually removed, because otherwise both + * bind() and connect() for the respective address will fail. In this + * function, we test if such a left-over file exists, and if so, + * remove it (unless there is a listening service at the address). + * + * @param un unix domain socket address to check + */ +void +GNUNET_NETWORK_unix_precheck (const struct sockaddr_un *un) +{ + int s; + int eno; + struct stat sbuf; + int ret; + + s = socket (AF_UNIX, SOCK_STREAM, 0); + ret = connect (s, + (struct sockaddr *) un, + sizeof (struct sockaddr_un)); + eno = errno; + GNUNET_break (0 == close (s)); + if (0 == ret) + return; /* another process is listening, do not remove! */ + if (ECONNREFUSED != eno) + return; /* some other error, likely "no such file or directory" -- all well */ + /* should unlink, but sanity checks first */ + if (0 != stat (un->sun_path, + &sbuf)) + return; /* failed to 'stat', likely does not exist after all */ + if (S_IFSOCK != (S_IFMT & sbuf.st_mode)) + return; /* refuse to unlink anything except sockets */ + /* finally, really unlink */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Removing left-over `%s' from previous exeuction\n", + un->sun_path); + if (0 != unlink (un->sun_path)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "unlink", + un->sun_path); +} +#endif + + + #ifndef FD_COPY #define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set))) #endif @@ -447,6 +494,8 @@ GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc, #endif #endif #ifndef WINDOWS + if (AF_UNIX == address->sa_family) + GNUNET_NETWORK_unix_precheck ((const struct sockaddr_un *) address); { const int on = 1; @@ -459,8 +508,6 @@ GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc, LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, "setsockopt"); } -#endif -#ifndef WINDOWS { /* set permissions of newly created non-abstract UNIX domain socket to "user-only"; applications can choose to relax this later */ @@ -476,7 +523,10 @@ GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc, old_mask = umask (S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IROTH | S_IXOTH); #endif - ret = bind (desc->fd, address, address_len); + ret = bind (desc->fd, + address, + address_len); + #ifndef WINDOWS if (not_abstract) (void) umask (old_mask); @@ -486,7 +536,7 @@ GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc, if (SOCKET_ERROR == ret) SetErrnoFromWinsockError (WSAGetLastError ()); #endif - if (ret != 0) + if (0 != ret) return GNUNET_SYSERR; #ifndef MINGW desc->addr = GNUNET_malloc (address_len); -- cgit v1.2.3