]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: deinit: close all receivers/listeners before scanning proxies
authorWilly Tarreau <w@1wt.eu>
Wed, 23 Sep 2020 14:46:22 +0000 (16:46 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 9 Oct 2020 09:27:29 +0000 (11:27 +0200)
Because of the zombie state, proxies have a skewed vision of the state
of listeners, which explains why there are hacks switching the state
from ZOMBIE to INIT in the proxy cleaning loop. This is particularly
complicated and not needed, as all the information is now available
in the protocol list and the fdtab.

What we do here instead is to first close all active listeners or
receivers by protocol and clean their protocol parts. Then we scan the
fdtab to get rid of remaining ones that were necessarily in INIT state
after a previous invocation of delete_listener(). From this point, we
know the listeners are cleaned, the can safely be freed by scanning the
proxies.

src/haproxy.c

index 335ccb78ab8dd58cd62f8e3039f2852f96d6a61f..69f4f20b4df928b371b0ffb20f8c1374261c37ec 100644 (file)
@@ -2422,6 +2422,37 @@ void deinit(void)
        struct post_server_check_fct *pscf, *pscfb;
        struct post_check_fct *pcf, *pcfb;
        struct post_proxy_check_fct *ppcf, *ppcfb;
+       int cur_fd;
+
+       /* At this point the listeners state is weird:
+        *  - most listeners are still bound and referenced in their protocol
+        *  - some might be zombies that are not in their proto anymore, but
+        *    still appear in their proxy's listeners with a valid FD.
+        *  - some might be stopped and still appear in their proxy as FD #-1
+        *  - among all of them, some might be inherited hence shared and we're
+        *    not allowed to pause them or whatever, we must just close them.
+        *  - finally some are not listeners (pipes, logs, stdout, etc) and
+        *    must be left intact.
+        *
+        * The safe way to proceed is to unbind (and close) whatever is not yet
+        * unbound so that no more receiver/listener remains alive. Then close
+        * remaining listener FDs, which correspond to zombie listeners (those
+        * belonging to disabled proxies that were in another process).
+        * objt_listener() would be cleaner here but not converted yet.
+        */
+       protocol_unbind_all();
+
+       for (cur_fd = 0; cur_fd < global.maxsock; cur_fd++) {
+               if (!fdtab[cur_fd].owner)
+                       continue;
+
+               if (fdtab[cur_fd].iocb == listener_accept) {
+                       struct listener *l = fdtab[cur_fd].owner;
+
+                       BUG_ON(l->state != LI_INIT);
+                       unbind_listener(l);
+               }
+       }
 
        deinit_signals();
        while (p) {
@@ -2609,18 +2640,6 @@ void deinit(void)
                }/* end while(s) */
 
                list_for_each_entry_safe(l, l_next, &p->conf.listeners, by_fe) {
-                       /*
-                        * Zombie proxy, the listener just pretend to be up
-                        * because they still hold an opened fd.
-                        * Close it and give the listener its real state.
-                        */
-                       if (p->state == PR_STSTOPPED && l->state >= LI_ZOMBIE) {
-                               fd_delete(l->rx.fd);
-                               l->rx.fd = -1;
-                               l->state = LI_INIT;
-                       }
-                       unbind_listener(l);
-                       delete_listener(l);
                        LIST_DEL(&l->by_fe);
                        LIST_DEL(&l->by_bind);
                        free(l->name);
@@ -2694,8 +2713,6 @@ void deinit(void)
 
        deinit_log_buffers();
 
-       protocol_unbind_all();
-
        list_for_each_entry(pdf, &post_deinit_list, list)
                pdf->fct();