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 */
#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)
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);
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;
}
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)