]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-master: Support handling a pre-accept()ed connection from master process
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 16 Oct 2025 09:24:06 +0000 (12:24 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Tue, 21 Oct 2025 11:03:24 +0000 (11:03 +0000)
src/lib-master/master-interface.h
src/lib-master/master-service-private.h
src/lib-master/master-service.c

index 73691fee004efd0836f09c6f4e5a9d6072d518ce..0207bec2792aa27d79f692ad27abab16e8d64eae 100644 (file)
@@ -113,6 +113,12 @@ enum master_login_state {
    socket. */
 #define DOVECOT_STATS_WRITER_SOCKET_PATH "STATS_WRITER_SOCKET_PATH"
 
+/* If master has already accepted a connection,
+   getenv(DOVECOT_ACCEPTED_CLIENT_LISTENER_FD_ENV) contains the listener's
+   file descriptor for the accepted connection. */
+#define DOVECOT_ACCEPTED_CLIENT_LISTENER_FD_ENV \
+       "DOVECOT_ACCEPTED_CLIENT_LISTENER_FD"
+
 /* Write pipe to anvil. */
 #define MASTER_ANVIL_FD 3
 /* Anvil reads new log fds from this fd */
@@ -127,13 +133,16 @@ enum master_login_state {
 #define MASTER_DEAD_FD 6
 /* Configuration file descriptor. */
 #define MASTER_CONFIG_FD 7
+/* If master pre-accepted a connection for a child process, this is the fd
+   number where it's passed to. */
+#define MASTER_ACCEPTED_CLIENT_FD 8
 /* First file descriptor where process is expected to be listening.
    The file descriptor count is given in -s parameter, defaulting to 1.
 
    master_status.available_count reports how many accept()s we're still
    accepting. Once no children are listening, master will do it and create
    new child processes when needed. */
-#define MASTER_LISTEN_FD_FIRST 8
+#define MASTER_LISTEN_FD_FIRST 9
 
 /* Timeouts: base everything on how long we can wait for login clients. */
 #define MASTER_LOGIN_TIMEOUT_SECS (3*60)
index bf7c53c72c089ed5be40cf8cb4d53d2b7b7be60f..98eedfefcd8089bdf18e009e950bdcd2baf13695 100644 (file)
@@ -43,6 +43,7 @@ struct master_service {
        int syslog_facility;
        data_stack_frame_t datastack_frame_id;
 
+       int accepted_listener_fd;
        struct master_service_listener *listeners;
        unsigned int socket_count;
 
index dfa1d973876ab9d4b74a24073485ef4ee73c50dc..ba198c26588922fb3cb120042b3b05c0e4db85f5 100644 (file)
@@ -637,6 +637,16 @@ master_service_init(const char *name, enum master_service_flags flags,
 
        master_service_verify_version_string(service);
 
+       value = getenv(DOVECOT_ACCEPTED_CLIENT_LISTENER_FD_ENV);
+       if (value == NULL)
+               service->accepted_listener_fd = -1;
+       else if (str_to_int(value, &service->accepted_listener_fd) < 0 ||
+                service->accepted_listener_fd < MASTER_LISTEN_FD_FIRST ||
+                service->accepted_listener_fd - MASTER_LISTEN_FD_FIRST > (int)service->socket_count) {
+               i_fatal("Invalid DOVECOT_ACCEPTED_CLIENT_SETTINGS environment: "
+                       "Invalid listener fd '%s'", value);
+       }
+
        if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
                env_remove(MASTER_SERVICE_ENV);
                env_remove(MASTER_SERVICE_SOCKET_COUNT_ENV);
@@ -1193,10 +1203,35 @@ master_service_get_settings_root(struct master_service *service)
        return service->settings_root;
 }
 
+static void master_service_start_accepted_fd(struct master_service *service)
+{
+       struct master_service_connection conn = {
+               .fd = MASTER_ACCEPTED_CLIENT_FD,
+               .listen_fd = service->accepted_listener_fd,
+       };
+       service->accepted_listener_fd = -1;
+
+       const struct master_service_listener *l =
+               &service->listeners[conn.listen_fd - MASTER_LISTEN_FD_FIRST];
+
+       (void)net_getpeername(conn.fd, &conn.remote_ip, &conn.remote_port);
+       master_service_connection_init_finish(&conn, l);
+
+       /* Note that master admin connections aren't pre-accepted */
+       master_service_client_connection_created(service);
+       if (l->haproxy)
+               master_service_haproxy_new(service, &conn);
+       else
+               master_service_client_connection_callback(service, &conn);
+}
+
 void master_service_run(struct master_service *service,
                        master_service_connection_callback_t *callback)
 {
        service->callback = callback;
+       if (service->accepted_listener_fd != -1) T_BEGIN {
+               master_service_start_accepted_fd(service);
+       } T_END;
        io_loop_run(service->ioloop);
        service->callback = NULL;
 }
@@ -1608,6 +1643,7 @@ static void master_service_deinit_real(struct master_service *service)
        master_service_category.name = NULL;
        event_unregister_callback(master_service_event_callback);
        master_service_unset_process_shutdown_filter(service);
+       i_close_fd(&service->accepted_listener_fd);
 }
 
 static void master_service_free(struct master_service **_service)