doveadm-connection.c revision 16a5712c1a774b7bd3bbf22032b61ccc9398499e
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
9ddd3d7d8651985e373a6c48e0ddc76b8a4ef1c7Timo Sirainen#define DOVEADM_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n"
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen#define DOVEADM_CONNECTION_RING_SYNC_TIMEOUT_MSECS (30*1000)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen DOVEADM_DIRECTOR_CMD_FLAG_PRE_RING_SYNC = 0x01,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainendoveadm_connection_ring_sync_callback_t(struct doveadm_connection *);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen unsigned int host_start_idx, host_idx, hosts_count;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen doveadm_connection_ring_sync_callback_t *ring_sync_callback;
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainenstatic struct doveadm_connection *doveadm_connections;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainenstatic struct doveadm_connection *doveadm_ring_sync_pending_connections;
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainenstatic struct director_reset_cmd *reset_cmds = NULL;
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainenstatic struct director_kick_cmd *kick_cmds = NULL;
419baa2c17c63ae516b2df6cc5695f15aaccbff8Timo Sirainenstatic void doveadm_connection_set_io(struct doveadm_connection *conn);
419baa2c17c63ae516b2df6cc5695f15aaccbff8Timo Sirainenstatic void doveadm_connection_deinit(struct doveadm_connection **_conn);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainendoveadm_connection_ring_sync_list_move(struct doveadm_connection *conn);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void doveadm_connection_cmd_run_synced(struct doveadm_connection *conn);
419baa2c17c63ae516b2df6cc5695f15aaccbff8Timo Sirainendoveadm_cmd_host_list(struct doveadm_connection *conn,
419baa2c17c63ae516b2df6cc5695f15aaccbff8Timo Sirainen array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_append_tabescaped(str, mail_host_get_tag(*hostp));
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen str_printfa(str, "\t%c\t%ld", (*hostp)->down ? 'D' : 'U',
419baa2c17c63ae516b2df6cc5695f15aaccbff8Timo Sirainen o_stream_nsend(conn->output, str_data(str), str_len(str));
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipekdoveadm_cmd_host_list_removed(struct doveadm_connection *conn,
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen struct mail_host *const *orig_hosts, *const *cur_hosts;
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen unsigned int i, j, orig_hosts_count, cur_hosts_count;
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen orig_hosts_list = mail_hosts_init(conn->dir->set->director_user_expire,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen (void)mail_hosts_parse_and_add(orig_hosts_list,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen orig_hosts = array_get(mail_hosts_get(orig_hosts_list),
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen cur_hosts = array_get(mail_hosts_get(conn->dir->mail_hosts),
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen /* the hosts are sorted by IP */
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen for (i = j = 0; i < orig_hosts_count && j < cur_hosts_count; ) {
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen ret = net_ip_cmp(&orig_hosts[i]->ip, &cur_hosts[j]->ip);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen else if (ret > 0)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_printfa(str, "%s\n", orig_hosts[i]->ip_str);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen for (; i < orig_hosts_count; i++)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_printfa(str, "%s\n", orig_hosts[i]->ip_str);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen o_stream_nsend(conn->output, str_data(str), str_len(str));
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_director_host_append_status(const struct director_host *host,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen time_t last_failed = I_MAX(host->last_network_failure,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_printfa(str, "%s\t%u\t%s\t%"PRIdTIME_T"\t",
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainenstatic void doveadm_director_append_status(struct director *dir, string_t *str)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_printfa(str, "syncing - last sync %d secs ago",
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen (int)(ioloop_time - dir->ring_last_sync_time));
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_printfa(str, "\t%u", dir->last_sync_msecs);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_director_connection_append_status(struct director_connection *conn,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen director_connection_get_status(conn, &status);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen str_printfa(str, "\t%u\t%"PRIuUOFF_T"\t%"PRIuUOFF_T"\t%zu\t%zu\t"
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen "%"PRIdTIME_T"\t%"PRIdTIME_T, status.last_ping_msecs,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen status.bytes_buffered, status.peak_bytes_buffered,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen status.last_input.tv_sec, status.last_output.tv_sec);
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainendoveadm_cmd_director_list(struct doveadm_connection *conn,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen t_array_init(&hosts, array_count(&dir->dir_hosts));
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen /* show each individual connection */
419baa2c17c63ae516b2df6cc5695f15aaccbff8Timo Sirainen /* NOTE: for incoming connections host is initially NULL */
9ddd3d7d8651985e373a6c48e0ddc76b8a4ef1c7Timo Sirainen else if (director_connection_is_incoming(*connp))
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen doveadm_director_host_append_status(host, type, str);
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen doveadm_director_connection_append_status(*connp, str);
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen /* show the rest of the hosts that don't have any connections */
b0df0e9a8ed8889ad4bf032043ab245ce8851fdeTimo Sirainen doveadm_director_host_append_status(host, type, str);
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen o_stream_nsend(conn->output, str_data(str), str_len(str));
b0df0e9a8ed8889ad4bf032043ab245ce8851fdeTimo Sirainendoveadm_cmd_director_add(struct doveadm_connection *conn,
b0df0e9a8ed8889ad4bf032043ab245ce8851fdeTimo Sirainen const char *const *args)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen (args[1] != NULL && net_str2port(args[1], &port) < 0)) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen i_error("doveadm sent invalid DIRECTOR-ADD parameters");
9ddd3d7d8651985e373a6c48e0ddc76b8a4ef1c7Timo Sirainen if (director_host_lookup(conn->dir, &ip, port) == NULL) {
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen host = director_host_add(conn->dir, &ip, port);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen director_notify_ring_added(host, conn->dir->self_host, TRUE);
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainendoveadm_cmd_director_remove(struct doveadm_connection *conn,
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen const char *const *args)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen (args[1] != NULL && net_str2port(args[1], &port) < 0)) {
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen i_error("doveadm sent invalid DIRECTOR-REMOVE parameters");
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen o_stream_nsend_str(conn->output, "NOTFOUND\n");
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen director_ring_remove(host, conn->dir->self_host);
742b9a0fc1d5513552fe99aa726497d1250d762eTimo Sirainendoveadm_cmd_host_set_or_update(struct doveadm_connection *conn,
84de4076ab3a33b34049a46e72ee0456afad8ad4Timo Sirainen if (ip_str == NULL || net_addr2ip(ip_str, &ip) < 0 ||
84de4076ab3a33b34049a46e72ee0456afad8ad4Timo Sirainen (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0) ||
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen if (vhost_count > MAX_VALID_VHOST_COUNT && vhost_count != UINT_MAX) {
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen o_stream_nsend_str(conn->output, "vhost count too large\n");
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen host = mail_host_lookup(dir->mail_hosts, &ip);
991367db5ec720fa0b764bbd6df21b7f7c654d2cTimo Sirainen host = mail_host_add_ip(dir->mail_hosts, &ip, tag);
991367db5ec720fa0b764bbd6df21b7f7c654d2cTimo Sirainen } else if (tag[0] != '\0' && strcmp(mail_host_get_tag(host), tag) != 0) {
991367db5ec720fa0b764bbd6df21b7f7c654d2cTimo Sirainen o_stream_nsend_str(conn->output, "host tag can't be changed\n");
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek "host is already being updated - try again later\n");
e63aa0fe623cee01975d57755381e2b873e5bf93Timo Sirainen mail_host_set_vhost_count(host, vhost_count, "doveadm: ");
a272994d43de80a306a8ed1f2983960d1f3102d0Timo Sirainen /* NOTE: we don't support changing a tag for an existing host.
e63aa0fe623cee01975d57755381e2b873e5bf93Timo Sirainen it needs to be removed first. otherwise it would be a bit ugly to
e63aa0fe623cee01975d57755381e2b873e5bf93Timo Sirainen director_update_host(dir, dir->self_host, NULL, host);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_cmd_host_set(struct doveadm_connection *conn, const char *const *args)
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen return doveadm_cmd_host_set_or_update(conn, args, FALSE);
a272994d43de80a306a8ed1f2983960d1f3102d0Timo Sirainendoveadm_cmd_host_update(struct doveadm_connection *conn, const char *const *args)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen return doveadm_cmd_host_set_or_update(conn, args, TRUE);
9ddd3d7d8651985e373a6c48e0ddc76b8a4ef1c7Timo Sirainendoveadm_cmd_host_updown(struct doveadm_connection *conn, bool down,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen const char *const *args)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0) {
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen i_error("doveadm sent invalid %s parameters: %s",
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen o_stream_nsend_str(conn->output, "NOTFOUND\n");
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen "host is already being updated - try again later\n");
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen mail_host_set_down(host, down, ioloop_time, "doveadm: ");
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen director_update_host(conn->dir, conn->dir->self_host,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_cmd_host_up(struct doveadm_connection *conn,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen const char *const *args)
21c1655dbc5fe861a152dc9a8a388d0d64f5ae20Timo Sirainen return doveadm_cmd_host_updown(conn, FALSE, args);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_cmd_host_down(struct doveadm_connection *conn,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen const char *const *args)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen return doveadm_cmd_host_updown(conn, TRUE, args);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_cmd_host_remove(struct doveadm_connection *conn,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen const char *const *args)
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0) {
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen i_error("doveadm sent invalid HOST-REMOVE parameters");
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen o_stream_nsend_str(conn->output, "NOTFOUND\n");
d2b6189eb61fc0e8a37f8814427282304e39e72cAki Tuomi director_remove_host(conn->dir, conn->dir->self_host,
0f3d4fbcf88e2ffd674893aed8cc1288fe17d290Timo Sirainendoveadm_cmd_host_flush_all(struct doveadm_connection *conn)
0f3d4fbcf88e2ffd674893aed8cc1288fe17d290Timo Sirainen unsigned int total_user_count = 0;
f540662dd02e930cf704c1468cccd05d512bc663Timo Sirainen array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen director_flush_host(conn->dir, conn->dir->self_host,
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen i_warning("Flushed all backend hosts with %u users. This is an unsafe "
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen "operation and may cause the same users to end up in multiple backends.",
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainendoveadm_cmd_host_flush(struct doveadm_connection *conn, const char *const *args)
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen i_error("doveadm sent invalid HOST-FLUSH parameters");
a9b135760aea6d1790d447d351c56b78889dac22Aki Tuomi host = mail_host_lookup(conn->dir->mail_hosts, &ip);
return FALSE;
TRUE);
} T_END;
unsigned int count;
return FALSE;
return TRUE;
static enum doveadm_director_cmd_ret
const char *const *args)
unsigned int i = 0, count;
max_moving_users == 0)) {
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
args[0]);
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
for (i = 0; i < count; i++) {
if (i == count) {
return DOVEADM_DIRECTOR_CMD_RET_OK;
return DOVEADM_DIRECTOR_CMD_RET_OK;
static enum doveadm_director_cmd_ret
const char *const *args)
unsigned int username_hash;
return DOVEADM_DIRECTOR_CMD_RET_OK;
return DOVEADM_DIRECTOR_CMD_RET_OK;
static enum doveadm_director_cmd_ret
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
} T_END;
return DOVEADM_DIRECTOR_CMD_RET_OK;
static enum doveadm_director_cmd_ret
unsigned int username_hash;
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
return DOVEADM_DIRECTOR_CMD_RET_OK;
return DOVEADM_DIRECTOR_CMD_RET_OK;
return DOVEADM_DIRECTOR_CMD_RET_OK;
return DOVEADM_DIRECTOR_CMD_RET_OK;
return FALSE;
return TRUE;
static enum doveadm_director_cmd_ret
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
if (wait) {
return DOVEADM_DIRECTOR_CMD_RET_OK;
static enum doveadm_director_cmd_ret
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
if (wait) {
return DOVEADM_DIRECTOR_CMD_RET_OK;
const char *name;
} doveadm_director_commands[] = {
static enum doveadm_director_cmd_ret
const char *const *args, unsigned int i)
return ret;
return DOVEADM_DIRECTOR_CMD_RET_OK;
static enum doveadm_director_cmd_ret
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
args++;
return DOVEADM_DIRECTOR_CMD_RET_FAIL;
const char *line;
T_BEGIN {
} T_END;
struct doveadm_connection *
return conn;
void doveadm_connections_deinit(void)
unsigned int pending_count = 0;
if (pending_count > 0)
static void doveadm_connections_continue_reset_cmds(void)
void doveadm_connections_ring_synced(void)