diff --git a/src/proxymain.c b/src/proxymain.c index 41cfc90..3c0090c 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -898,6 +898,7 @@ struct listener { char *netns, *device; struct sockoptset opts; int maxseg; + uint64_t undev, unino; /* its file, for a Unix socket on a path */ int optversion; /* the configuration whose options are set */ /* the services accepting on it, linked by lisnext */ struct srvparam *owners; @@ -972,6 +973,28 @@ static void listenerrelease(struct srvparam *srv); static int srvopen(struct srvstart *s); +#ifdef WITH_UN +/* A Unix socket bound to a path: its file is noted, as a service of a + newer configuration may bind the same path anew before this socket is + closed, when the file is the other's, which closing must not remove. */ +static void unbound(PROXYSOCKADDRTYPE *sa, uint64_t *dev, uint64_t *ino){ + struct stat st; + + if(*SAFAMILY(sa) == AF_UNIX && *SAADDR(sa) && !stat((char *)SAADDR(sa), &st)){ + *dev = (uint64_t)st.st_dev; + *ino = (uint64_t)st.st_ino; + } +} + +static void ununlink(PROXYSOCKADDRTYPE *sa, uint64_t dev, uint64_t ino){ + struct stat st; + + if(*SAFAMILY(sa) == AF_UNIX && *SAADDR(sa) && !stat((char *)SAADDR(sa), &st) && + (uint64_t)st.st_dev == dev && (uint64_t)st.st_ino == ino) + unlink((char *)SAADDR(sa)); +} +#endif + /* The listening socket of the service: its listener's, opened by the first of the listener's services to get here. */ static int srvlisten(struct srvstart *s){ @@ -993,6 +1016,8 @@ static int srvlisten(struct srvstart *s){ _3proxy_mutex_lock(&listener_mutex); l->sock = srv->srvsock; l->optversion = srv->version; + l->undev = srv->undev; + l->unino = srv->unino; _3proxy_mutex_unlock(&listener_mutex); } else if(srv->srvsock != INVALID_SOCKET){ @@ -1087,6 +1112,9 @@ static int srvopen(struct srvstart *s){ sleeptime = (sleeptime<<1); if(!sleeptime) return -3; } +#ifdef WITH_UN + unbound(&srv->intsa, &srv->undev, &srv->unino); +#endif #ifndef NOTCPMAIN if(!s->isudp){ if(srv->so._listen (srv->so.state, sock, srv->backlog?srv->backlog : 1+(srv->maxchild>>3))==-1) { @@ -1838,7 +1866,7 @@ static void listenerstop(struct srvparam *srv){ if(sock == INVALID_SOCKET) return; so._closesocket(so.state, sock); #ifdef WITH_UN - if(*SAFAMILY(&l->sa) == AF_UNIX && *SAADDR(&l->sa))unlink((char *)SAADDR(&l->sa)); + ununlink(&l->sa, l->undev, l->unino); #endif } @@ -2047,7 +2075,7 @@ void srvfree(struct srvparam * srv){ if(srv->srvsock != INVALID_SOCKET) { so._closesocket(srv->so.state, srv->srvsock); #ifdef WITH_UN - if(*SAFAMILY(&srv->intsa) == AF_UNIX && *SAADDR(&srv->intsa))unlink((char *)SAADDR(&srv->intsa)); + ununlink(&srv->intsa, srv->undev, srv->unino); #endif } srv->srvsock = INVALID_SOCKET; diff --git a/src/structures.h b/src/structures.h index d8159c5..10a762e 100644 --- a/src/structures.h +++ b/src/structures.h @@ -113,6 +113,7 @@ int #ifdef WITH_UN #include +#include #endif #define ALLOW 0 @@ -631,6 +632,8 @@ struct srvparam { AUTHFUNC authfunc; PROXYFUNC pf; SOCKET srvsock, cbsock; + /* the file of a Unix socket bound to a path, to remove only it */ + uint64_t undev, unino; int childcount; int maxchild; int backlog; diff --git a/tests/cases/unixsock.py b/tests/cases/unixsock.py index b965e99..2690917 100644 --- a/tests/cases/unixsock.py +++ b/tests/cases/unixsock.py @@ -157,6 +157,18 @@ def run(t): t.fail("its file is still there", path, "missing") t.eq((200, 1000), unix_get(path, url), "and the proxy answers on it") + # --- a bind-time option changed: a new socket on the same path, and + # the old one, closing after, must not remove the new one's file ------- + t.write_config("unixsock", config().replace(f"proxy -iunix:{path}", + f"proxy -olSO_REUSEADDR=0 -iunix:{path}")) + t.http(f"http://127.0.0.1:{adm}/R") + # the old services stop within a second of the reload + time.sleep(2) + t.ok("a new socket on the same path keeps its file when the old one closes") \ + if os.path.exists(path) else \ + t.fail("a new socket on the same path keeps its file when the old one closes", path, "missing") + t.eq((200, 1000), unix_get(path, url), "and the proxy answers on it") + # --- no service listens there any more: the file goes ------------------- t.write_config("unixsock", config(unix=False)) t.http(f"http://127.0.0.1:{adm}/R")