From bb1caff70f0fedccda54d22ebd0e8392c2c49026 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 19 Aug 2020 10:00:57 +0200 Subject: [PATCH] MINOR: fd: add a new "exported" flag and use it for all regular listeners This new flag will be used to mark FDs that must be passed to any future process across the CLI's "_getsocks" command. The scheme here is quite complex and full of special cases: - FDs inherited from parent processes are *not* exported this way, as they are supposed to instead be passed by the master process itself across reloads. However such FDs ought never to be paused otherwise this would disrupt the socket in the parent process as well; - FDs resulting from a "bind" performed over a socket pair, which are in fact one side of a socket pair passed inside another control socket pair must not be passed either. Since all of them are used the same way, for now it's enough never to put this "exported" flag to FDs bound by the socketpair code. - FDs belonging to temporary listeners (e.g. a passive FTP data port) must not be passed either. Fortunately we don't have such FDs yet. - the rest of the listeners for now are made of TCP, UNIX stream, ABNS sockets and are exportable, so they get the flag. - UDP listeners were wrongly created as listeners and are not suitable here. Their FDs should be passed but for now they are not since the client doesn't even distinguish the SO_TYPE of the retrieved sockets. In addition, it's important to keep in mind that: - inherited FDs may never be closed in master process but may be closed in worker processes if the service is shut down (useless since still bound, but technically possible) ; - inherited FDs may not be disabled ; - exported FDs may be disabled because the caller will perform the subsequent listen() on them. However that might not work for all OSes - exported FDs may be closed, it just means the service was shut down from the worker, and will be rebound in the new process. This implies that we have to disable exported on close(). => as such, contrary to an apparently obvious equivalence, the "exported" status doesn't imply anything regarding the ability to close a listener's FD or not. --- include/haproxy/fd-t.h | 1 + include/haproxy/fd.h | 1 + src/fd.c | 1 + src/proto_tcp.c | 4 ++++ src/proto_uxst.c | 4 ++++ 5 files changed, 11 insertions(+) diff --git a/include/haproxy/fd-t.h b/include/haproxy/fd-t.h index 6ff8ef135..2b51125c9 100644 --- a/include/haproxy/fd-t.h +++ b/include/haproxy/fd-t.h @@ -134,6 +134,7 @@ struct fdtab { unsigned char cloned:1; /* 1 if a cloned socket, requires EPOLL_CTL_DEL on close */ unsigned char initialized:1; /* 1 if init phase was done on this fd (e.g. set non-blocking) */ unsigned char et_possible:1; /* 1 if edge-triggered is possible on this FD */ + unsigned char exported:1; /* 1 if the FD is exported and must not be closed */ #ifdef DEBUG_FD unsigned int event_count; /* number of events reported */ #endif diff --git a/include/haproxy/fd.h b/include/haproxy/fd.h index 240842a18..6f0401e02 100644 --- a/include/haproxy/fd.h +++ b/include/haproxy/fd.h @@ -441,6 +441,7 @@ static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), unsigned fdtab[fd].linger_risk = 0; fdtab[fd].cloned = 0; fdtab[fd].et_possible = 0; + fdtab[fd].exported = 0; #ifdef DEBUG_FD fdtab[fd].event_count = 0; #endif diff --git a/src/fd.c b/src/fd.c index 86c2d4359..c72dddd82 100644 --- a/src/fd.c +++ b/src/fd.c @@ -329,6 +329,7 @@ void fd_delete(int fd) fdinfo[fd].port_range = NULL; fdtab[fd].owner = NULL; fdtab[fd].thread_mask = 0; + fdtab[fd].exported = 0; close(fd); _HA_ATOMIC_SUB(&ha_used_fds, 1); if (locked) diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 2f8ec22e8..98a6310d0 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -1114,6 +1114,10 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) fd_insert(fd, listener, listener->proto->accept, thread_mask(listener->bind_conf->bind_thread) & all_threads_mask); + /* for now, all regularly bound TCP listeners are exportable */ + if (!(listener->options & LI_O_INHERITED)) + fdtab[fd].exported = 1; + tcp_return: if (msg && errlen) { char pn[INET6_ADDRSTRLEN]; diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 586b11931..2a4610f68 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -352,6 +352,10 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle fd_insert(fd, listener, listener->proto->accept, thread_mask(listener->bind_conf->bind_thread) & all_threads_mask); + /* for now, all regularly bound UNIX listeners are exportable */ + if (!(listener->options & LI_O_INHERITED)) + fdtab[fd].exported = 1; + return err; err_rename: