ecore_con: bug workaround SO_REUSEADDR and EADDRINUSE from bind (fix)
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>
Wed, 20 Dec 2017 12:10:53 +0000 (21:10 +0900)
committerWonki Kim <wonki_.kim@samsung.com>
Wed, 10 Jan 2018 11:08:13 +0000 (20:08 +0900)
what i'm seeing is this with local unix sockets:

1. server process not cleanly shut down (kill -9 for example).
2. run server process again and bind fails due to EADDRINUSE
3. we ARE doing setsockopt() with SO_REUSEADDR set to 1 ...

this just makes no sense because setsockopt() SHOULD allow use to
re-use... the previous efreetd process for example is gone. no such
process, yet socket is not re-usable. this should just not happen due
to SO_REUSEADDR, but it does. this has nasty consequences like efreetd
maybe never running because of stale sockets. this should never have
happened, but it does. odd. so a hacky workaround:

1. try bind.
2. if bind fails with EADDRINUSE and its a socket path AND
pd->unlink_before_bind is NOT set... then try a connect to the socket.
3. if connect succeeds then fail as normal (close socket and error on
bind'ing)
   if connect fails then we have a stale socket, so unlink it
forcibly. create the socket again and try bind again.

hacky but... fixes the core issue.

@fix

src/lib/ecore_con/efl_net_server_unix.c

index b1a1d2d..65f2b25 100644 (file)
@@ -50,7 +50,7 @@ _efl_net_server_unix_bind(Eo *o, Efl_Net_Server_Unix_Data *pd)
    const char *address = efl_net_server_address_get(o);
    struct sockaddr_un addr = { .sun_family = AF_UNIX };
    socklen_t addrlen;
-   SOCKET fd;
+   SOCKET fd = INVALID_SOCKET;
    Eina_Error err = 0;
    int r;
 
@@ -108,6 +108,17 @@ _efl_net_server_unix_bind(Eo *o, Efl_Net_Server_Unix_Data *pd)
              if ((err == EADDRINUSE) && (pd->unlink_before_bind) && (addr.sun_path[0] != '\0'))
                {
                   closesocket(fd);
+                  fd = INVALID_SOCKET;
+                  err = 0;
+                  continue;
+               }
+             if ((err == EADDRINUSE) && (addr.sun_path[0] != '\0') &&
+                 (connect(fd, (struct sockaddr *)&addr, addrlen) != 0))
+               {
+                  DBG("bind(" SOCKET_FMT ", %s): failed with EADDRINUSE but connect also failed, so unlink socket file and try again", fd, address);
+                  closesocket(fd);
+                  unlink(addr.sun_path);
+                  fd = INVALID_SOCKET;
                   err = 0;
                   continue;
                }
@@ -118,6 +129,7 @@ _efl_net_server_unix_bind(Eo *o, Efl_Net_Server_Unix_Data *pd)
      }
    while (pd->unlink_before_bind);
 
+   if (fd == INVALID_SOCKET) goto error;
    efl_loop_fd_set(o, fd);
 
    r = listen(fd, 0);