close file descriptors upon forking a connection shoveler (fix #521)

This commit is contained in:
Yves Rutschle committed 2026-02-15 11:17:36 +01:00
1 parent ecc76071c1
commit e2d01a7440
8 files changed
+59 -9

No files matched your search

+6
View File
@@ -12,6 +12,12 @@ vNEXT:
`msg_probe_error` to `msg_probe_info`: beware if you
happen to use them...
Close unused file descriptors upon forking a worker
for a connection (in sslh-ev and sslh-select). In
particular, this prevents forked processes to
inherit the listen sockets and block a new main
process from starting.
v2.3.0:
Added `max_connections` setting to `listen` and
`protocol` configuration; see the
+10 -2
View File
@@ -28,6 +28,7 @@
/* Info to keep track of all connections */
struct cnx_collection {
gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */
int max_fd;
};
/* Allocates and initialises a new collection of connections with at least
@@ -42,6 +43,7 @@ cnx_collection* collection_init(int len)
memset(collection, 0, sizeof(*collection));
collection->fd2cnx = gap_init(len);
collection->max_fd = 0;
return collection;
}
@@ -54,10 +56,17 @@ void collection_destroy(cnx_collection* collection)
free(collection);
}
int collection_max_fd(cnx_collection* collection)
{
return collection->max_fd;
}
/* Points the file descriptor to the specified connection index */
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd)
{
gap_set(collection->fd2cnx, fd, cnx);
if (fd > collection->max_fd)
collection->max_fd = fd;
return 0;
}
@@ -74,8 +83,7 @@ struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collectio
cnx->state = ST_PROBING;
cnx->probe_timeout = time(NULL) + cfg.timeout;
gap_set(collection->fd2cnx, fd, cnx);
collection_add_fd(collection, cnx, fd);
return cnx;
}
+1
View File
@@ -9,6 +9,7 @@ void collection_destroy(cnx_collection* collection);
struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd);
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd);
int collection_max_fd(cnx_collection* collection);
/* Remove a connection from the collection */
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx);
+2 -1
View File
@@ -196,7 +196,7 @@ static int next_power_of_two(int n)
return n + 1;
}
void loop_init(struct loop_info* loop, int num_addr_listen)
void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen)
{
memset(loop, 0, sizeof(*loop));
loop->collection = collection_init(0);
@@ -206,6 +206,7 @@ void loop_init(struct loop_info* loop, int num_addr_listen)
udp_init(loop);
tcp_init();
loop->listen_sockets = listen_sockets;
loop->num_addr_listen = num_addr_listen;
int max_forks = next_power_of_two(max_forking_connections());
+5 -1
View File
@@ -23,6 +23,8 @@ struct loop_info {
hash* pid2proto; /* to follow which forked PID is processing what protocol for connection count */
watchers* watchers;
struct listen_endpoint* listen_sockets;
int num_addr_listen; /* How many listen endpoints do we have here */
cnx_collection* collection; /* Collection of connections linked to this loop */
@@ -32,7 +34,7 @@ void cnx_read_process(struct loop_info* fd_info, int fd);
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket);
int tidy_connection(struct connection *cnx, struct loop_info* fd_info);
void loop_init(struct loop_info* loop, int num_addr_listen);
void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen);
void remember_child_data(struct loop_info* fd_info,
struct connection* cnx, pid_t pid);
@@ -47,4 +49,6 @@ void watchers_del_write(watchers* w, int fd);
void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pid);
void close_listen_endpoints(struct loop_info* loop);
#endif
+1 -2
View File
@@ -207,13 +207,12 @@ void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pi
ev_child_start(EV_DEFAULT_ cw);
}
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
{
struct loop_info ev_info;
loop = EV_DEFAULT;
loop_init(&ev_info, num_addr_listen);
loop_init(&ev_info, listen_sockets, num_addr_listen);
watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
ev_set_userdata(EV_A_ &ev_info);
+1 -1
View File
@@ -204,7 +204,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
int i, res;
setup_sigalrm();
loop_init(&fd_info, num_addr_listen);
loop_init(&fd_info, listen_sockets, num_addr_listen);
watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);
+33 -2
View File
@@ -296,6 +296,37 @@ static void connect_proxy(struct connection *cnx)
}
/* This ensures the kernel will close the listening socket when the
* parent process closes; otherwise, the listening socket is kept
* around by the inactive file descriptor in the child */
void close_listen_endpoints(struct loop_info* loop)
{
struct listen_endpoint* listen_sockets = loop->listen_sockets;
watchers* w = loop->watchers;
for (int i = 0; i < loop->num_addr_listen; i++) {
int fd = listen_sockets[i].socketfd;
watchers_del_read(w, fd);
watchers_del_write(w, fd);
close(fd);
}
}
/* Close all connections except specified.
* This saves file descriptors and allocated memory when forking */
void tidy_other_connections(struct loop_info* fd_info, struct connection* keep_cnx)
{
struct connection* cnx;
cnx_collection* cnx_coll = fd_info->collection;
for (int i = 0; i < collection_max_fd(cnx_coll); i++) {
cnx = collection_get_cnx_from_fd(cnx_coll, i);
if (cnx && (cnx != keep_cnx))
tidy_connection(cnx, fd_info);
}
}
/* Forks a shoveler process for one protocol */
void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) {
pid_t pid;
@@ -307,8 +338,8 @@ void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) {
switch (pid = fork()) {
case 0: /* child */
/* TODO: close all file descriptors except 2 */
/* free(cnx); */
close_listen_endpoints(fd_info);
tidy_other_connections(fd_info, cnx);
connect_proxy(cnx);
exit(0);
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));