summaryrefslogtreecommitdiffstats
path: root/src/director/director.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/director/director.h')
-rw-r--r--src/director/director.h274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/director/director.h b/src/director/director.h
new file mode 100644
index 0000000..8f2d2a1
--- /dev/null
+++ b/src/director/director.h
@@ -0,0 +1,274 @@
+#ifndef DIRECTOR_H
+#define DIRECTOR_H
+
+#include "net.h"
+#include "director-settings.h"
+
+#define DIRECTOR_VERSION_NAME "director"
+#define DIRECTOR_VERSION_MAJOR 1
+#define DIRECTOR_VERSION_MINOR 9
+
+/* weak users supported in protocol */
+#define DIRECTOR_VERSION_WEAK_USERS 1
+/* director ring remove supported */
+#define DIRECTOR_VERSION_RING_REMOVE 2
+/* quit reason supported */
+#define DIRECTOR_VERSION_QUIT 3
+/* user-kick supported */
+#define DIRECTOR_VERSION_USER_KICK 4
+/* options supported in handshake */
+#define DIRECTOR_VERSION_OPTIONS 5
+/* user tags supported */
+#define DIRECTOR_VERSION_TAGS 5
+/* up/down state is tracked */
+#define DIRECTOR_VERSION_UPDOWN 6
+/* user tag version 2 supported */
+#define DIRECTOR_VERSION_TAGS_V2 7
+/* user-kick-alt supported */
+#define DIRECTOR_VERSION_USER_KICK_ALT 8
+/* Users are sent as "U" command in handshake */
+#define DIRECTOR_VERSION_HANDSHAKE_U_CMD 9
+/* USER event with timestamp supported */
+#define DIRECTOR_VERSION_USER_TIMESTAMP 9
+
+/* Minimum time between even attempting to communicate with a director that
+ failed due to a protocol error. */
+#define DIRECTOR_PROTOCOL_FAILURE_RETRY_SECS 60
+
+struct director;
+struct mail_host;
+struct user;
+struct director_user_init;
+
+enum user_kill_state {
+ /* User isn't being killed */
+ USER_KILL_STATE_NONE,
+ /* We're still killing the user's connections */
+ USER_KILL_STATE_KILLING,
+ /* Like above, but our left side already announced it was finished
+ with killing its user connections */
+ USER_KILL_STATE_KILLING_NOTIFY_RECEIVED,
+ /* We're done killing, but we have to wait for the left side to
+ finish killing its user connections before sending USER-KILLED to
+ our right side */
+ USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY,
+ /* We're done killing, but waiting for USER-KILLED-EVERYWHERE
+ notification until this state gets reset. */
+ USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE,
+ /* Waiting for the flush socket to finish. */
+ USER_KILL_STATE_FLUSHING,
+ /* Wait for a while for the user connections to actually die. Note that
+ only at this stage we can be sure that all the directors know about
+ the user move (although it could be earlier if we added a new
+ USER-MOVED notification). */
+ USER_KILL_STATE_DELAY
+ /* NOTE: remember to update also user_kill_state_names[] */
+};
+extern const char *user_kill_state_names[USER_KILL_STATE_DELAY+1];
+
+typedef void director_state_change_callback_t(struct director *dir);
+typedef director_state_change_callback_t director_kick_callback_t;
+
+/* When a user gets freed, the kill_ctx may still be left alive. It's also
+ possible for the user to come back, in which case the kill_ctx is usually
+ NULL, but another kill could have also started. The previous kill_ctx is
+ valid only if it matches the current user's kill_ctx. */
+#define DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx) \
+ ((user) != NULL && (user)->kill_ctx == ctx)
+
+struct director_kill_context {
+ struct director *dir;
+ struct mail_tag *tag;
+ unsigned int username_hash;
+ struct ip_addr old_host_ip;
+ unsigned int old_host_vhost_count;
+ bool old_host_down;
+ bool kill_is_self_initiated;
+ bool callback_pending;
+
+ enum user_kill_state kill_state;
+ /* Move timeout to make sure user's connections won't silently hang
+ indefinitely if there is some trouble moving it. */
+ struct timeout *to_move;
+ /* IPC command to kick the user */
+ struct ipc_client_cmd *ipc_cmd;
+
+ /* these are set only for director_flush_socket handling: */
+ struct ip_addr host_ip;
+ struct program_client *pclient;
+ struct ostream *reply;
+ char *socket_path;
+};
+
+struct director {
+ struct event *event;
+ const struct director_settings *set;
+
+ /* IP and port of this director. self_host->ip/port must equal these. */
+ struct ip_addr self_ip;
+ in_port_t self_port;
+
+ in_port_t test_port;
+
+ struct director_host *self_host;
+ /* left and right connections are set only after they have finished
+ handshaking. until then they're in the connections list, although
+ updates are still sent to them during handshaking if the USER list
+ is long. */
+ struct director_connection *left, *right;
+ /* all director connections */
+ ARRAY(struct director_connection *) connections;
+ struct timeout *to_reconnect;
+ struct timeout *to_sync;
+ struct timeout *to_callback;
+
+ /* current mail hosts */
+ struct mail_host_list *mail_hosts;
+ /* original mail hosts configured in config file.
+ this is used only for doveadm lookups */
+ struct mail_host_list *orig_config_hosts;
+ /* Number of users currently being moved */
+ unsigned int users_moving_count;
+ /* Number of users currently being kicked */
+ unsigned int users_kicking_count;
+ /* Number of requests currently delayed */
+ unsigned int requests_delayed_count;
+
+ /* these requests are waiting for directors to be in synced */
+ ARRAY(struct director_request *) pending_requests;
+ struct timeout *to_request;
+ struct timeout *to_handshake_warning;
+
+ director_state_change_callback_t *state_change_callback;
+ director_kick_callback_t *kick_callback;
+
+ /* director hosts are sorted by IP (and port) */
+ ARRAY(struct director_host *) dir_hosts;
+ struct timeout *to_remove_dirs;
+
+ struct ipc_client *ipc_proxy;
+ unsigned int sync_seq;
+ unsigned int ring_change_counter;
+ unsigned int last_sync_sent_ring_change_counter;
+ /* Timestamp when the last SYNC was initiated by us */
+ struct timeval last_sync_start_time;
+ /* the lowest minor version supported by the ring */
+ unsigned int ring_min_version;
+ /* Timestamp when ring became synced or unsynced the last time */
+ time_t ring_last_sync_time;
+ /* How many milliseconds it took for the last SYNC to travel through
+ the ring. */
+ unsigned int last_sync_msecs;
+
+ time_t ring_first_alone;
+
+ uint64_t num_requests, num_incoming_requests;
+ uint64_t ring_traffic_input, ring_traffic_output;
+
+ /* director ring handshaking is complete.
+ director can start serving clients. */
+ bool ring_handshaked:1;
+ bool ring_handshake_warning_sent:1;
+ bool ring_synced:1;
+ bool sync_frozen:1;
+ bool sync_pending:1;
+};
+
+/* Create a new director. If listen_ip specifies an actual IP, it's used with
+ listen_port for finding ourself from the director_servers setting.
+ listen_port is used regardless by director_host_add_from_string() for hosts
+ without specified port. */
+struct director *
+director_init(const struct director_settings *set,
+ const struct ip_addr *listen_ip, in_port_t listen_port,
+ director_state_change_callback_t *callback,
+ director_kick_callback_t *kick_callback);
+void director_deinit(struct director **dir);
+void director_find_self(struct director *dir);
+
+/* Start connecting to other directors */
+void director_connect(struct director *dir, const char *reason);
+
+void director_set_ring_handshaked(struct director *dir);
+void director_set_ring_synced(struct director *dir);
+void director_set_ring_unsynced(struct director *dir);
+void director_set_state_changed(struct director *dir);
+void director_sync_send(struct director *dir, struct director_host *host,
+ uint32_t seq, unsigned int minor_version,
+ unsigned int timestamp, unsigned int hosts_hash);
+bool director_resend_sync(struct director *dir);
+
+void director_notify_ring_added(struct director_host *added_host,
+ struct director_host *src, bool log);
+void director_ring_remove(struct director_host *removed_host,
+ struct director_host *src);
+
+void director_update_host(struct director *dir, struct director_host *src,
+ struct director_host *orig_src,
+ struct mail_host *host) ATTR_NULL(3);
+void director_resend_hosts(struct director *dir);
+void director_remove_host(struct director *dir, struct director_host *src,
+ struct director_host *orig_src,
+ struct mail_host *host) ATTR_NULL(2, 3);
+void director_flush_host(struct director *dir, struct director_host *src,
+ struct director_host *orig_src,
+ struct mail_host *host) ATTR_NULL(3);
+void director_update_user(struct director *dir, struct director_host *src,
+ struct user *user);
+void director_update_user_weak(struct director *dir, struct director_host *src,
+ struct director_connection *src_conn,
+ struct director_host *orig_src,
+ struct user *user) ATTR_NULL(3);
+void director_kill_user(struct director *dir, struct director_host *src,
+ struct user *user, struct mail_tag *tag,
+ struct mail_host *old_host, bool forced_kick);
+void director_move_user(struct director *dir, struct director_host *src,
+ struct director_host *orig_src,
+ unsigned int username_hash, struct mail_host *host)
+ ATTR_NULL(3);
+void director_kick_user(struct director *dir, struct director_host *src,
+ struct director_host *orig_src, const char *username)
+ ATTR_NULL(3);
+void director_kick_user_alt(struct director *dir, struct director_host *src,
+ struct director_host *orig_src,
+ const char *field, const char *value)
+ ATTR_NULL(3);
+void director_kick_user_hash(struct director *dir, struct director_host *src,
+ struct director_host *orig_src,
+ unsigned int username_hash,
+ const struct ip_addr *except_ip)
+ ATTR_NULL(3);
+void director_user_killed(struct director *dir, unsigned int username_hash);
+void director_user_killed_everywhere(struct director *dir,
+ struct director_host *src,
+ struct director_host *orig_src,
+ unsigned int username_hash) ATTR_NULL(3);
+void director_user_weak(struct director *dir, struct user *user);
+
+void director_sync_freeze(struct director *dir);
+void director_sync_thaw(struct director *dir);
+
+/* Send data to all directors using both left and right connections
+ (unless they're the same). */
+void director_update_send(struct director *dir, struct director_host *src,
+ const char *cmd);
+void director_update_send_version(struct director *dir,
+ struct director_host *src,
+ unsigned int min_version, const char *cmd);
+
+int director_connect_host(struct director *dir, struct director_host *host,
+ const char *reason);
+
+bool
+director_get_username_hash(struct director *dir, const char *username,
+ unsigned int *hash_r);
+
+void directors_init(void);
+void directors_deinit(void);
+
+struct director_user_iter *
+director_iterate_users_init(struct director *dir, bool iter_until_current_tail);
+struct user *director_iterate_users_next(struct director_user_iter *iter);
+void director_iterate_users_deinit(struct director_user_iter **_iter);
+
+#endif