]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
anvil: Add anvil_connection_find()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 27 Dec 2021 15:42:27 +0000 (17:42 +0200)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 8 Feb 2022 09:48:24 +0000 (10:48 +0100)
src/anvil/anvil-connection.c
src/anvil/anvil-connection.h

index 40139a4e33f5968bebeaae958d1cd4a34e6a786a..e4b4e3566083a1b9b59e602b313409c07b3de6a8 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
+#include "hash.h"
 #include "llist.h"
 #include "istream.h"
 #include "ostream.h"
 #define ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION 2
 #define ANVIL_CLIENT_PROTOCOL_MINOR_VERSION 0
 
+struct anvil_connection_key {
+       const char *service;
+       pid_t pid;
+};
+
 struct anvil_connection {
        struct connection conn;
 
        char *service;
        bool master:1;
        bool fifo:1;
+       bool added_to_hash:1;
 };
 
 static struct connection_list *anvil_connections;
+static HASH_TABLE(struct anvil_connection_key *, struct anvil_connection *)
+       anvil_connections_hash;
 
 static void anvil_connection_destroy(struct connection *_conn);
 
+static unsigned int
+anvil_connection_key_hash(const struct anvil_connection_key *key)
+{
+       return str_hash(key->service) ^ key->pid;
+}
+
+static int anvil_connection_key_cmp(const struct anvil_connection_key *key1,
+                                   const struct anvil_connection_key *key2)
+{
+       if (key1->pid != key2->pid)
+               return 1;
+       return strcmp(key1->service, key2->service);
+}
+
 static bool
 connect_limit_key_parse(const char *const **_args,
                        struct connect_limit_key *key_r)
@@ -258,6 +281,27 @@ anvil_connection_handshake(struct anvil_connection *conn,
        o_stream_unref(&orig_output);
 
        connection_streams_changed(&conn->conn);
+
+       struct anvil_connection_key *hash_key, key = {
+               .service = conn->service,
+               .pid = conn->conn.remote_pid,
+       };
+       struct anvil_connection *hash_conn;
+       if (hash_table_lookup_full(anvil_connections_hash, &key,
+                                  &hash_key, &hash_conn)) {
+               e_warning(conn->conn.event,
+                         "Handshake with duplicate service=%s pid=%ld - "
+                         "replacing the old connection",
+                         key.service, (long)key.pid);
+               hash_table_remove(anvil_connections_hash, hash_key);
+               i_assert(hash_conn->added_to_hash);
+               hash_conn->added_to_hash = FALSE;
+       } else {
+               hash_key = i_new(struct anvil_connection_key, 1);
+               *hash_key = key;
+       }
+       hash_table_insert(anvil_connections_hash, hash_key, conn);
+       conn->added_to_hash = TRUE;
        return 0;
 }
 
@@ -328,6 +372,21 @@ static void anvil_connection_destroy(struct connection *_conn)
        bool fifo = conn->fifo;
 
        connection_deinit(&conn->conn);
+
+       if (conn->added_to_hash) {
+               struct anvil_connection_key *hash_key, key = {
+                       .service = conn->service,
+                       .pid = conn->conn.remote_pid,
+               };
+               struct anvil_connection *hash_conn;
+               if (!hash_table_lookup_full(anvil_connections_hash, &key,
+                                           &hash_key, &hash_conn))
+                       i_unreached();
+               i_assert(hash_conn == conn);
+               hash_table_remove(anvil_connections_hash, &key);
+               i_free(hash_key);
+       }
+
        o_stream_destroy(&conn->conn.output);
        i_free(conn->service);
        i_free(conn);
@@ -336,6 +395,15 @@ static void anvil_connection_destroy(struct connection *_conn)
                master_service_client_connection_destroyed(master_service);
 }
 
+struct anvil_connection *anvil_connection_find(const char *service, pid_t pid)
+{
+       struct anvil_connection_key key = {
+               .service = service,
+               .pid = pid,
+       };
+       return hash_table_lookup(anvil_connections_hash, &key);
+}
+
 static struct connection_settings anvil_connections_set = {
        .dont_send_version = TRUE,
        .input_max_size = MAX_INBUF_SIZE,
@@ -348,6 +416,8 @@ static struct connection_vfuncs anvil_connections_vfuncs = {
 
 void anvil_connections_init(void)
 {
+       hash_table_create(&anvil_connections_hash, default_pool, 0,
+                         anvil_connection_key_hash, anvil_connection_key_cmp);
        anvil_connections = connection_list_init(&anvil_connections_set,
                                                 &anvil_connections_vfuncs);
 }
@@ -355,4 +425,7 @@ void anvil_connections_init(void)
 void anvil_connections_deinit(void)
 {
        connection_list_deinit(&anvil_connections);
+
+       i_assert(hash_table_count(anvil_connections_hash) == 0);
+       hash_table_destroy(&anvil_connections_hash);
 }
index 746cc77a86c180952360d4e7807fb2073a3f654b..6b3f426800e012bf12e63a58a9bfa617f7b56add 100644 (file)
@@ -3,6 +3,9 @@
 
 void anvil_connection_create(int fd, bool master, bool fifo);
 
+/* Find an existing anvil connection from the specified process. */
+struct anvil_connection *anvil_connection_find(const char *service, pid_t pid);
+
 void anvil_connections_init(void);
 void anvil_connections_deinit(void);