diff options
Diffstat (limited to 'src/director/director.h')
-rw-r--r-- | src/director/director.h | 274 |
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 |