director.c revision 7a380e77afc69a81725d410cd67082a37cf140d2
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "lib.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "ioloop.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "array.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "str.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "strescape.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "log-throttle.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "ipc-client.h"
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen#include "program-client.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "var-expand.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "istream.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "ostream.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "iostream-temp.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "mail-user-hash.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "user-directory.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "mail-host.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "director-host.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "director-connection.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#include "director.h"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_IPC_PROXY_PATH "ipc"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_DNS_SOCKET_PATH "dns-client"
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_RECONNECT_RETRY_SECS 60
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_RECONNECT_TIMEOUT_MSECS (30*1000)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_USER_MOVE_TIMEOUT_MSECS (30*1000)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_SYNC_TIMEOUT_MSECS (5*1000)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_RING_MIN_WAIT_SECS 20
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_QUICK_RECONNECT_TIMEOUT_MSECS 1000
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen#define DIRECTOR_DELAYED_DIR_REMOVE_MSECS (1000*30)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenbool director_debug;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenconst char *user_kill_state_names[USER_KILL_STATE_DELAY+1] = {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "none",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "killing",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "notify-received",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "waiting-for-notify",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "waiting-for-everyone",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "flushing",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "delay",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen};
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic struct log_throttle *user_move_throttle;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic struct log_throttle *user_kill_fail_throttle;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic const struct log_throttle_settings director_log_throttle_settings = {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen .throttle_at_max_per_interval = 100,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen .unthrottle_at_max_per_interval = 2,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen};
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainendirector_user_kill_finish_delayed(struct director *dir, struct user *user,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen bool skip_delay);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic bool director_is_self_ip_set(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (net_ip_compare(&dir->self_ip, &net_ip4_any))
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen if (net_ip_compare(&dir->self_ip, &net_ip6_any))
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen return FALSE;
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen return TRUE;
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen}
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void director_find_self_ip(struct director *dir)
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen{
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen struct director_host *const *hosts;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int i, count;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hosts = array_get(&dir->dir_hosts, &count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < count; i++) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (net_try_bind(&hosts[i]->ip) == 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->self_ip = hosts[i]->ip;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_fatal("director_servers doesn't list ourself");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_find_self(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->self_host != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return;
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen if (!director_is_self_ip_set(dir))
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_find_self_ip(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->self_host = director_host_lookup(dir, &dir->self_ip,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->self_port);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->self_host == NULL) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_fatal("director_servers doesn't list ourself (%s:%u)",
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen net_ip2addr(&dir->self_ip), dir->self_port);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->self_host->self = TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic unsigned int director_find_self_idx(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *const *hosts;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int i, count;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(dir->self_host != NULL);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hosts = array_get(&dir->dir_hosts, &count);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen for (i = 0; i < count; i++) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (hosts[i] == dir->self_host)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return i;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen i_unreached();
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic bool
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainendirector_has_outgoing_connection(struct director *dir,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *host)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_connection *const *connp;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_foreach(&dir->connections, connp) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (director_connection_get_host(*connp) == host &&
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen !director_connection_is_incoming(*connp))
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenint director_connect_host(struct director *dir, struct director_host *host)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen in_port_t port;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen int fd;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (director_has_outgoing_connection(dir, host))
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return 0;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (director_debug) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen string_t *str = t_str_new(128);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_printfa(str, "Connecting to %s:%u (as %s",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&host->ip), host->port,
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen net_ip2addr(&dir->self_ip));
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen if (host->last_network_failure > 0) {
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen str_printfa(str, ", last network failure %ds ago",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (int)(ioloop_time - host->last_network_failure));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (host->last_protocol_failure > 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_printfa(str, ", last protocol failure %ds ago",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (int)(ioloop_time - host->last_protocol_failure));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen dir_debug("%s", str_c(str));
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen port = dir->test_port != 0 ? dir->test_port : host->port;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen fd = net_connect_ip(&host->ip, port, &dir->self_ip);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (fd == -1) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen host->last_network_failure = ioloop_time;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_error("connect(%s) failed: %m", host->name);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return -1;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* Reset timestamp so that director_connect() won't skip this host
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen while we're still trying to connect to it */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen host->last_network_failure = 0;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (void)director_connection_init_out(dir, fd, host);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return 0;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2498b8003eb181001b0c4fd45763c462b45493d1Timo Sirainen
2498b8003eb181001b0c4fd45763c462b45493d1Timo Sirainenstatic struct director_host *
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainendirector_get_preferred_right_host(struct director *dir)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director_host *const *hosts, *host;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int i, count, self_idx;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen hosts = array_get(&dir->dir_hosts, &count);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (count == 1) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* self */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return NULL;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen self_idx = director_find_self_idx(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < count; i++) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen host = hosts[(self_idx + i + 1) % count];
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (!host->removed)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return host;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* self, with some removed hosts */
2498b8003eb181001b0c4fd45763c462b45493d1Timo Sirainen return NULL;
2498b8003eb181001b0c4fd45763c462b45493d1Timo Sirainen}
2498b8003eb181001b0c4fd45763c462b45493d1Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic bool director_wait_for_others(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *const *hostp;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* don't assume we're alone until we've attempted to connect
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen to others for a while */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->ring_first_alone != 0 &&
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen ioloop_time - dir->ring_first_alone > DIRECTOR_RING_MIN_WAIT_SECS)
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen return FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->ring_first_alone == 0)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_first_alone = ioloop_time;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* reset all failures and try again */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_foreach(&dir->dir_hosts, hostp) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (*hostp)->last_network_failure = 0;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (*hostp)->last_protocol_failure = 0;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->to_reconnect != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout_remove(&dir->to_reconnect);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->to_reconnect = timeout_add(DIRECTOR_QUICK_RECONNECT_TIMEOUT_MSECS,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_connect, dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return TRUE;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_connect(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *const *hosts;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int i, count, self_idx;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen self_idx = director_find_self_idx(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* try to connect to first working server on our right side.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen the left side is supposed to connect to us. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hosts = array_get(&dir->dir_hosts, &count);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen for (i = 1; i < count; i++) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int idx = (self_idx + i) % count;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (hosts[idx]->removed)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen continue;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (hosts[idx]->last_network_failure +
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen DIRECTOR_RECONNECT_RETRY_SECS > ioloop_time) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* connection failed recently, don't try retrying here */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen continue;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (hosts[idx]->last_protocol_failure +
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen DIRECTOR_PROTOCOL_FAILURE_RETRY_SECS > ioloop_time) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* the director recently sent invalid protocol data,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen don't try retrying yet */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen continue;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (director_connect_host(dir, hosts[idx]) == 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* success */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (count > 1 && director_wait_for_others(dir))
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen /* we're the only one */
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen if (count > 1) {
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen i_warning("director: Couldn't connect to right side, "
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen "we must be the only director left");
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen }
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen if (dir->left != NULL) {
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen /* since we couldn't connect to it,
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen it must have failed recently */
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen i_warning("director: Assuming %s is dead, disconnecting",
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen director_connection_get_name(dir->left));
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen director_connection_deinit(&dir->left,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "This connection is dead?");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_min_version = DIRECTOR_VERSION_MINOR;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (!dir->ring_handshaked)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_set_ring_handshaked(dir);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen else
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_set_ring_synced(dir);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_set_ring_handshaked(struct director *dir)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(!dir->ring_handshaked);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->to_handshake_warning != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout_remove(&dir->to_handshake_warning);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->ring_handshake_warning_sent) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_warning("Directors have been connected, "
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "continuing delayed requests");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_handshake_warning_sent = FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir_debug("Director ring handshaked");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_handshaked = TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_set_ring_synced(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void director_reconnect_timeout(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *cur_host, *preferred_host =
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_get_preferred_right_host(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen cur_host = dir->right == NULL ? NULL :
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_get_host(dir->right);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (preferred_host == NULL) {
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen /* all directors have been removed, try again later */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen } else if (cur_host != preferred_host)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (void)director_connect_host(dir, preferred_host);
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen else {
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen /* the connection hasn't finished sync yet.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen keep this timeout for now. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenvoid director_set_ring_synced(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *host;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(!dir->ring_synced);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_assert((dir->left != NULL && dir->right != NULL) ||
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (dir->left == NULL && dir->right == NULL));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->to_handshake_warning != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout_remove(&dir->to_handshake_warning);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->ring_handshake_warning_sent) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_warning("Ring is synced, continuing delayed requests "
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "(syncing took %d secs, hosts_hash=%u)",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (int)(ioloop_time - dir->ring_last_sync_time),
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_hosts_hash(dir->mail_hosts));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_handshake_warning_sent = FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen host = dir->right == NULL ? NULL :
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_get_host(dir->right);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->to_reconnect != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout_remove(&dir->to_reconnect);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (host != director_get_preferred_right_host(dir)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* try to reconnect to preferred host later */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->to_reconnect =
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen timeout_add(DIRECTOR_RECONNECT_TIMEOUT_MSECS,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_reconnect_timeout, dir);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->left != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_set_synced(dir->left, TRUE);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->right != NULL)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen director_connection_set_synced(dir->right, TRUE);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (dir->to_sync != NULL)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen timeout_remove(&dir->to_sync);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen dir->ring_synced = TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_last_sync_time = ioloop_time;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_hosts_set_synced(dir->mail_hosts);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_set_state_changed(dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid director_sync_send(struct director *dir, struct director_host *host,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen uint32_t seq, unsigned int minor_version,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int timestamp, unsigned int hosts_hash)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen{
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen string_t *str;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (host == dir->self_host)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->last_sync_sent_ring_change_counter = dir->ring_change_counter;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str = t_str_new(128);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen str_printfa(str, "SYNC\t%s\t%u\t%u",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&host->ip), host->port, seq);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (minor_version > 0 &&
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen director_connection_get_minor_version(dir->right) > 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* only minor_version>0 supports extra parameters */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_printfa(str, "\t%u\t%u\t%u", minor_version,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timestamp, hosts_hash);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_append_c(str, '\n');
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_send(dir->right, str_c(str));
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* ping our connections in case either of them are hanging.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if they are, we want to know it fast. */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (dir->left != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_ping(dir->left);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_ping(dir->right);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenbool director_resend_sync(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (!dir->ring_synced && dir->left != NULL && dir->right != NULL) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* send a new SYNC in case the previous one got dropped */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen dir->self_host->last_sync_timestamp = ioloop_time;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_sync_send(dir, dir->self_host, dir->sync_seq,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen DIRECTOR_VERSION_MINOR, ioloop_time,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mail_hosts_hash(dir->mail_hosts));
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (dir->to_sync != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout_reset(dir->to_sync);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void director_sync_timeout(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(!dir->ring_synced);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (director_resend_sync(dir))
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_error("Ring SYNC appears to have got lost, resending");
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_set_ring_unsynced(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->ring_synced) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->ring_synced = FALSE;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->ring_last_sync_time = ioloop_time;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->to_sync == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->to_sync = timeout_add(DIRECTOR_SYNC_TIMEOUT_MSECS,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_sync_timeout, dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen } else {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen timeout_reset(dir->to_sync);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void director_sync(struct director *dir)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* we're synced again when we receive this SYNC back */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->sync_seq++;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->right == NULL && dir->left == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* we're alone. if we're already synced,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen don't become unsynced. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_set_ring_unsynced(dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->sync_frozen) {
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen dir->sync_pending = TRUE;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen if (dir->right == NULL) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(!dir->ring_synced ||
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen (dir->left == NULL && dir->right == NULL));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir_debug("Ring is desynced (seq=%u, sending SYNC to %s)",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->sync_seq, dir->right == NULL ? "(nowhere)" :
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_get_name(dir->right));
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* send PINGs to our connections more rapidly until we've synced again.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if the connection has actually died, we don't need to wait (and
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen delay requests) for as long to detect it */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->left != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_set_synced(dir->left, FALSE);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_set_synced(dir->right, FALSE);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_sync_send(dir, dir->self_host, dir->sync_seq,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen DIRECTOR_VERSION_MINOR, ioloop_time,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_hosts_hash(dir->mail_hosts));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_sync_freeze(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director_connection *const *connp;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(!dir->sync_frozen);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(!dir->sync_pending);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_foreach(&dir->connections, connp)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_cork(*connp);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->sync_frozen = TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_sync_thaw(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_connection *const *connp;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(dir->sync_frozen);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->sync_frozen = FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->sync_pending) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->sync_pending = FALSE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_sync(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_foreach(&dir->connections, connp)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_uncork(*connp);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_notify_ring_added(struct director_host *added_host,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *src)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen const char *cmd;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen added_host->dir->ring_change_counter++;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen cmd = t_strdup_printf("DIRECTOR\t%s\t%u\n",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&added_host->ip), added_host->port);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_update_send(added_host->dir, src, cmd);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void director_delayed_dir_remove_timeout(struct director *dir)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *const *hosts, *host;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen unsigned int i, count;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen timeout_remove(&dir->to_remove_dirs);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hosts = array_get(&dir->dir_hosts, &count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < count; ) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (hosts[i]->removed) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen host = hosts[i];
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_host_free(&host);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen hosts = array_get(&dir->dir_hosts, &count);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen } else {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i++;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid director_ring_remove(struct director_host *removed_host,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct director_host *src)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director *dir = removed_host->dir;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director_connection *const *conns, *conn;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int i, count;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen const char *cmd;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (removed_host->self) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* others will just disconnect us */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* mark the host as removed and fully remove it later. this delay is
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen needed, because the removal may trigger director reconnections,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen which may send the director back and we don't want to re-add it */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen removed_host->removed = TRUE;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->to_remove_dirs == NULL) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->to_remove_dirs =
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout_add(DIRECTOR_DELAYED_DIR_REMOVE_MSECS,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_delayed_dir_remove_timeout, dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* disconnect any connections to the host */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen conns = array_get(&dir->connections, &count);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen for (i = 0; i < count; ) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen conn = conns[i];
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (director_connection_get_host(conn) != removed_host)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i++;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen else {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_deinit(&conn, "Removing from ring");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen conns = array_get(&dir->connections, &count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->right == NULL)
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen director_connect(dir);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen cmd = t_strdup_printf("DIRECTOR-REMOVE\t%s\t%u\n",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&removed_host->ip),
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen removed_host->port);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_update_send_version(dir, src,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen DIRECTOR_VERSION_RING_REMOVE, cmd);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainendirector_send_host(struct director *dir, struct director_host *src,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *orig_src,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct mail_host *host)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen const char *host_tag = mail_host_get_tag(host);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen string_t *str;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (orig_src == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen orig_src = dir->self_host;
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen orig_src->last_seq++;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str = t_str_new(128);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen str_printfa(str, "HOST\t%s\t%u\t%u\t%s\t%u",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&orig_src->ip), orig_src->port,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen orig_src->last_seq,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&host->ip), host->vhost_count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->ring_min_version >= DIRECTOR_VERSION_TAGS_V2) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_append_c(str, '\t');
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_append_tabescaped(str, host_tag);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen } else if (host_tag[0] != '\0' &&
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen dir->ring_min_version < DIRECTOR_VERSION_TAGS_V2) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->ring_min_version < DIRECTOR_VERSION_TAGS) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_error("Ring has directors that don't support tags - removing host %s with tag '%s'",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&host->ip), host_tag);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen } else {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_error("Ring has directors that support mixed versions of tags - removing host %s with tag '%s'",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&host->ip), host_tag);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_remove_host(dir, NULL, NULL, host);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->ring_min_version >= DIRECTOR_VERSION_UPDOWN) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen str_printfa(str, "\t%c%ld\t", host->down ? 'D' : 'U',
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen (long)host->last_updown_change);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* add any further version checks here - these directors ignore
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen any extra unknown arguments */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (host->hostname != NULL)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_append_tabescaped(str, host->hostname);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen str_append_c(str, '\n');
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_update_send(dir, src, str_c(str));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_resend_hosts(struct director *dir)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct mail_host *const *hostp;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_foreach(mail_hosts_get(dir->mail_hosts), hostp)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_send_host(dir, dir->self_host, NULL, *hostp);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid director_update_host(struct director *dir, struct director_host *src,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director_host *orig_src,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct mail_host *host)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* update state in case this is the first mail host being added */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_set_state_changed(dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir_debug("Updating host %s vhost_count=%u "
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "down=%d last_updown_change=%ld (hosts_hash=%u)",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen net_ip2addr(&host->ip), host->vhost_count, host->down ? 1 : 0,
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen (long)host->last_updown_change,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_hosts_hash(dir->mail_hosts));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_send_host(dir, src, orig_src, host);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* mark the host desynced until ring is synced again. except if we're
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen alone in the ring that never happens. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (dir->right != NULL || dir->left != NULL)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen host->desynced = TRUE;
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen director_sync(dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid director_remove_host(struct director *dir, struct director_host *src,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director_host *orig_src,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct mail_host *host)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct user_directory *users = host->tag->users;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen if (src != NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (orig_src == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen orig_src = dir->self_host;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen orig_src->last_seq++;
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_update_send(dir, src, t_strdup_printf(
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "HOST-REMOVE\t%s\t%u\t%u\t%s\n",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen net_ip2addr(&orig_src->ip), orig_src->port,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen orig_src->last_seq, net_ip2addr(&host->ip)));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user_directory_remove_host(users, host);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_host_remove(host);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_sync(dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid director_flush_host(struct director *dir, struct director_host *src,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_host *orig_src,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct mail_host *host)
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct user_directory *users = host->tag->users;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen if (orig_src == NULL) {
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen orig_src = dir->self_host;
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen orig_src->last_seq++;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_update_send(dir, src, t_strdup_printf(
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "HOST-FLUSH\t%s\t%u\t%u\t%s\n",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq,
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen net_ip2addr(&host->ip)));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user_directory_remove_host(users, host);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_sync(dir);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenvoid director_update_user(struct director *dir, struct director_host *src,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct user *user)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(src != NULL);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(!user->weak);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_update_send(dir, src, t_strdup_printf("USER\t%u\t%s\n",
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen user->username_hash, net_ip2addr(&user->host->ip)));
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenvoid director_update_user_weak(struct director *dir, struct director_host *src,
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen struct director_connection *src_conn,
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen struct director_host *orig_src,
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen struct user *user)
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen{
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen const char *cmd;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(src != NULL);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen i_assert(user->weak);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen if (orig_src == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen orig_src = dir->self_host;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen orig_src->last_seq++;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen cmd = t_strdup_printf("USER-WEAK\t%s\t%u\t%u\t%u\t%s\n",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user->username_hash, net_ip2addr(&user->host->ip));
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (src != dir->self_host && dir->left != NULL && dir->right != NULL &&
8e57335924f5ff57cbd1929ec99764dc267c3312Timo Sirainen director_connection_get_host(dir->left) ==
8e57335924f5ff57cbd1929ec99764dc267c3312Timo Sirainen director_connection_get_host(dir->right)) {
8e57335924f5ff57cbd1929ec99764dc267c3312Timo Sirainen /* only two directors in this ring and we're forwarding
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen USER-WEAK from one director back to itself via another
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen so it sees we've received it. we can't use
8e57335924f5ff57cbd1929ec99764dc267c3312Timo Sirainen director_update_send() for this, because it doesn't send
8e57335924f5ff57cbd1929ec99764dc267c3312Timo Sirainen data back to the source. */
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen if (dir->right == src_conn)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_connection_send(dir->left, cmd);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen else if (dir->left == src_conn)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_connection_send(dir->right, cmd);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen else
5c7aa03f959b8b9cab3eba8a585a90f4b50a4cdfTimo Sirainen i_unreached();
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen } else {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen director_update_send(dir, src, cmd);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
5c7aa03f959b8b9cab3eba8a585a90f4b50a4cdfTimo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainendirector_flush_user_continue(int result, struct director_kill_context *ctx)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director *dir = ctx->dir;
5c7aa03f959b8b9cab3eba8a585a90f4b50a4cdfTimo Sirainen ctx->callback_pending = FALSE;
5c7aa03f959b8b9cab3eba8a585a90f4b50a4cdfTimo Sirainen
5c7aa03f959b8b9cab3eba8a585a90f4b50a4cdfTimo Sirainen struct user *user = user_directory_lookup(ctx->tag->users,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ctx->username_hash);
38efb29cb9453917c1b642095a37d0e901f56eeeTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (result == 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct istream *is = iostream_temp_finish(&ctx->reply, (size_t)-1);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen char *data;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_stream_set_return_partial_line(is, TRUE);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen data = i_stream_read_next_line(is);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_error("%s: Failed to flush user hash %u in host %s: %s",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ctx->socket_path,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ctx->username_hash,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen net_ip2addr(&ctx->host_ip),
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen data == NULL ? "(no output to stdout)" : data);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen while((data = i_stream_read_next_line(is)) != NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_error("%s: Failed to flush user hash %u in host %s: %s",
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen ctx->socket_path,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ctx->username_hash,
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen net_ip2addr(&ctx->host_ip),
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen data);
171f1d1a6e3205447e83cd3984e76b1e8628de43Timo Sirainen }
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen i_stream_unref(&is);
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen } else {
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen o_stream_unref(&ctx->reply);
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen program_client_destroy(&ctx->pclient);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (!DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx)) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* user was already freed - ignore */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir_debug("User %u freed while flushing, result=%d",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ctx->username_hash, result);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_assert(ctx->to_move == NULL);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_free(ctx);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen } else {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen /* ctx is freed later via user->kill_ctx */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen dir_debug("Flushing user %u finished, result=%d",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->username_hash, result);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen director_user_kill_finish_delayed(dir, user, result == 1);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainendirector_flush_user(struct director *dir, struct user *user)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen{
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct director_kill_context *ctx = user->kill_ctx;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct var_expand_table tab[] = {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen { 'i', net_ip2addr(&user->host->ip), "ip" },
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen { 'h', user->host->hostname, "host" },
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen { '\0', NULL, NULL }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen };
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen const char *error;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen /* Execute flush script, if set. Only the director that started the
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen user moving will call the flush script. Having each director do it
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen would be redundant since they're all supposed to be performing the
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen same flush task to the same backend.
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen Flushing is also not triggered if we're moving a user that we just
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen created due to the user move. This means that the user doesn't have
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen an old host, so we couldn't really even perform any flushing on the
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen backend. */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (*dir->set->director_flush_socket == '\0' ||
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->old_host_ip.family == 0 ||
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen !ctx->kill_is_self_initiated) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen director_user_kill_finish_delayed(dir, user, FALSE);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen return;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ctx->host_ip = user->host->ip;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen string_t *s_sock = str_new(default_pool, 32);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (var_expand(s_sock, dir->set->director_flush_socket, tab, &error) <= 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_error("Failed to expand director_flush_socket=%s: %s",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->set->director_flush_socket, error);
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen director_user_kill_finish_delayed(dir, user, FALSE);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen return;
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ctx->socket_path = str_free_without_data(&s_sock);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct program_client_settings set = {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen .client_connect_timeout_msecs = 10000,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen .dns_client_socket_path = DIRECTOR_DNS_SOCKET_PATH,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen };
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen restrict_access_init(&set.restrict_set);
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen const char *const args[] = {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen "FLUSH",
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen t_strdup_printf("%u", user->username_hash),
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen net_ip2addr(&ctx->old_host_ip),
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen net_ip2addr(&user->host->ip),
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen NULL
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen };
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->kill_state = USER_KILL_STATE_FLUSHING;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen dir_debug("Flushing user %u via %s", user->username_hash,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->socket_path);
bf91bed88d4e294b4577ba2a3b14d87cf35ae135Timo Sirainen
bf91bed88d4e294b4577ba2a3b14d87cf35ae135Timo Sirainen if ((program_client_create(ctx->socket_path, args, &set, FALSE,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen &ctx->pclient, &error)) != 0) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_error("%s: Failed to flush user hash %u in host %s: %s",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->socket_path,
bf91bed88d4e294b4577ba2a3b14d87cf35ae135Timo Sirainen user->username_hash,
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen net_ip2addr(&user->host->ip),
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen error);
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen director_flush_user_continue(0, ctx);
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen return;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->reply =
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen iostream_temp_create_named("/tmp", 0,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen t_strdup_printf("flush response from %s",
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen net_ip2addr(&user->host->ip)));
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen o_stream_set_no_error_handling(ctx->reply, TRUE);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen program_client_set_output(ctx->pclient, ctx->reply);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen ctx->callback_pending = TRUE;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen program_client_run_async(ctx->pclient, director_flush_user_continue, ctx);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void director_user_move_free(struct user *user)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen{
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct director *dir = user->kill_ctx->dir;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct director_kill_context *kill_ctx = user->kill_ctx;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(kill_ctx != NULL);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir_debug("User %u move finished at state=%s", user->username_hash,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen user_kill_state_names[kill_ctx->kill_state]);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (kill_ctx->to_move != NULL)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen timeout_remove(&kill_ctx->to_move);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_free(kill_ctx->socket_path);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_free(kill_ctx);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen user->kill_ctx = NULL;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_assert(dir->users_moving_count > 0);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen dir->users_moving_count--;
19779377be72c9fe8365bb9ba7a2e0d06dc99c3bTimo Sirainen
19779377be72c9fe8365bb9ba7a2e0d06dc99c3bTimo Sirainen dir->state_change_callback(dir);
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainen}
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainen
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainenstatic void
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainendirector_user_kill_finish_delayed_to(struct user *user)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainen i_assert(user->kill_ctx != NULL);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(user->kill_ctx->kill_state == USER_KILL_STATE_DELAY);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_user_move_free(user);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
19779377be72c9fe8365bb9ba7a2e0d06dc99c3bTimo Sirainen
19779377be72c9fe8365bb9ba7a2e0d06dc99c3bTimo Sirainenstatic void
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainendirector_user_kill_finish_delayed(struct director *dir, struct user *user,
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen bool skip_delay)
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen{
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen if (skip_delay) {
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen user->kill_ctx->kill_state = USER_KILL_STATE_NONE;
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen director_user_move_free(user);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return;
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainen }
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen user->kill_ctx->kill_state = USER_KILL_STATE_DELAY;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen /* wait for a while for the kills to finish in the backend server,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen so there are no longer any processes running for the user before we
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen start letting new in connections to the new server. */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen timeout_remove(&user->kill_ctx->to_move);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen user->kill_ctx->to_move =
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen timeout_add(dir->set->director_user_kick_delay * 1000,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen director_user_kill_finish_delayed_to, user);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainendirector_finish_user_kill(struct director *dir, struct user *user, bool self)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_kill_context *kill_ctx = user->kill_ctx;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(kill_ctx != NULL);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(kill_ctx->kill_state != USER_KILL_STATE_FLUSHING);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(kill_ctx->kill_state != USER_KILL_STATE_DELAY);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (dir->right == NULL) {
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen /* we're alone */
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen director_flush_user(dir, user);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen } else if (self ||
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen kill_ctx->kill_state == USER_KILL_STATE_KILLING_NOTIFY_RECEIVED) {
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen director_connection_send(dir->right, t_strdup_printf(
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen "USER-KILLED\t%u\n", user->username_hash));
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen kill_ctx->kill_state = USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE;
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen } else {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(kill_ctx->kill_state == USER_KILL_STATE_KILLING);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen kill_ctx->kill_state = USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void director_user_kill_fail_throttled(unsigned int new_events_count,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen void *context ATTR_UNUSED)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_error("Failed to kill %u users' connections", new_events_count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void director_kill_user_callback(enum ipc_client_cmd_state state,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen const char *data, void *context)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_kill_context *ctx = context;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct user *user;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* this is an asynchronous notification about user being killed.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen there are no guarantees about what might have happened to the user
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen in the mean time. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen switch (state) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen case IPC_CLIENT_CMD_STATE_REPLY:
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* shouldn't get here. the command reply isn't finished yet. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen case IPC_CLIENT_CMD_STATE_OK:
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen break;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen case IPC_CLIENT_CMD_STATE_ERROR:
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (log_throttle_accept(user_kill_fail_throttle)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_error("Failed to kill user %u connections: %s",
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen ctx->username_hash, data);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen }
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen /* we can't really do anything but continue anyway */
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen break;
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen }
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen ctx->callback_pending = FALSE;
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen user = user_directory_lookup(ctx->tag->users, ctx->username_hash);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen if (!DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx)) {
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen /* user was already freed - ignore */
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen i_assert(ctx->to_move == NULL);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen i_free(ctx);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen } else {
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen i_assert(ctx->kill_state == USER_KILL_STATE_KILLING ||
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen ctx->kill_state == USER_KILL_STATE_KILLING_NOTIFY_RECEIVED);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* we were still waiting for the kill notification */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen director_finish_user_kill(ctx->dir, user, ctx->kill_is_self_initiated);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void director_user_move_throttled(unsigned int new_events_count,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen void *context ATTR_UNUSED)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen{
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen i_error("%u users' move timed out, their state may now be inconsistent",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen new_events_count);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen}
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void director_user_move_timeout(struct user *user)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen{
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_assert(user->kill_ctx != NULL);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(user->kill_ctx->kill_state != USER_KILL_STATE_DELAY);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (log_throttle_accept(user_move_throttle)) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_error("Finishing user %u move timed out, "
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen "its state may now be inconsistent (state=%s)",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen user->username_hash,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen user_kill_state_names[user->kill_ctx->kill_state]);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen }
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (user->kill_ctx->kill_state == USER_KILL_STATE_FLUSHING) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen o_stream_unref(&user->kill_ctx->reply);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen program_client_destroy(&user->kill_ctx->pclient);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
12053b7b4b57dbd2790057426d1633988eedad56Timo Sirainen director_user_move_free(user);
12053b7b4b57dbd2790057426d1633988eedad56Timo Sirainen}
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainendirector_kill_user(struct director *dir, struct director_host *src,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct user *user, struct mail_tag *tag,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct mail_host *old_host)
12053b7b4b57dbd2790057426d1633988eedad56Timo Sirainen{
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen struct director_kill_context *ctx;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen const char *cmd;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (USER_IS_BEING_KILLED(user)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* User is being moved again before the previous move
finished. We'll just continue wherever we left off
earlier. */
dir_debug("User %u move restarted - previous kill_state=%s",
user->username_hash,
user_kill_state_names[user->kill_ctx->kill_state]);
return;
}
user->kill_ctx = ctx = i_new(struct director_kill_context, 1);
ctx->dir = dir;
ctx->tag = tag;
ctx->username_hash = user->username_hash;
ctx->kill_is_self_initiated = src->self;
if (old_host != NULL)
ctx->old_host_ip = old_host->ip;
dir->users_moving_count++;
ctx->to_move = timeout_add(DIRECTOR_USER_MOVE_TIMEOUT_MSECS,
director_user_move_timeout, user);
ctx->kill_state = USER_KILL_STATE_KILLING;
if (old_host != NULL) {
cmd = t_strdup_printf("proxy\t*\tKICK-DIRECTOR-HASH\t%u",
user->username_hash);
ctx->callback_pending = TRUE;
ipc_client_cmd(dir->ipc_proxy, cmd,
director_kill_user_callback, ctx);
} else {
/* we didn't even know about the user before now.
don't bother performing a local kick, since it wouldn't
kick anything. */
director_finish_user_kill(ctx->dir, user,
ctx->kill_is_self_initiated);
}
}
void director_move_user(struct director *dir, struct director_host *src,
struct director_host *orig_src,
unsigned int username_hash, struct mail_host *host)
{
struct user_directory *users = host->tag->users;
struct user *user;
/* 1. move this user's host, and set its "killing" flag to delay all of
its future connections until all directors have killed the
connections and notified us about it.
2. tell the other directors about the move
3. once user kill callback is called, tell the other directors
with USER-KILLED that we're done killing the user.
4. when some director gets a duplicate USER-KILLED, it's
responsible for notifying all directors that user is completely
killed.
5. after receiving USER-KILLED-EVERYWHERE notification,
new connections are again allowed for the user.
*/
user = user_directory_lookup(users, username_hash);
if (user == NULL) {
user = user_directory_add(users, username_hash,
host, ioloop_time);
director_kill_user(dir, src, user, host->tag, NULL);
} else {
struct mail_host *old_host = user->host;
if (user->host == host) {
/* user is already in this host */
return;
}
/* user is looked up via the new host's tag, so if it's found
the old tag has to be the same. */
i_assert(user->host->tag == host->tag);
user->host->user_count--;
user->host = host;
user->host->user_count++;
user->timestamp = ioloop_time;
director_kill_user(dir, src, user, old_host->tag, old_host);
}
if (orig_src == NULL) {
orig_src = dir->self_host;
orig_src->last_seq++;
}
director_update_send(dir, src, t_strdup_printf(
"USER-MOVE\t%s\t%u\t%u\t%u\t%s\n",
net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq,
user->username_hash, net_ip2addr(&user->host->ip)));
}
static void
director_kick_user_callback(enum ipc_client_cmd_state state ATTR_UNUSED,
const char *data ATTR_UNUSED,
void *context ATTR_UNUSED)
{
}
void director_kick_user(struct director *dir, struct director_host *src,
struct director_host *orig_src, const char *username)
{
string_t *cmd = t_str_new(64);
str_append(cmd, "proxy\t*\tKICK\t");
str_append_tabescaped(cmd, username);
ipc_client_cmd(dir->ipc_proxy, str_c(cmd),
director_kick_user_callback, (void *)NULL);
if (orig_src == NULL) {
orig_src = dir->self_host;
orig_src->last_seq++;
}
str_truncate(cmd, 0);
str_printfa(cmd, "USER-KICK\t%s\t%u\t%u\t",
net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq);
str_append_tabescaped(cmd, username);
str_append_c(cmd, '\n');
director_update_send_version(dir, src, DIRECTOR_VERSION_USER_KICK, str_c(cmd));
}
void director_kick_user_alt(struct director *dir, struct director_host *src,
struct director_host *orig_src,
const char *field, const char *value)
{
string_t *cmd = t_str_new(64);
str_append(cmd, "proxy\t*\tKICK-ALT\t");
str_append_tabescaped(cmd, field);
str_append_c(cmd, '\t');
str_append_tabescaped(cmd, value);
ipc_client_cmd(dir->ipc_proxy, str_c(cmd),
director_kick_user_callback, (void *)NULL);
if (orig_src == NULL) {
orig_src = dir->self_host;
orig_src->last_seq++;
}
str_truncate(cmd, 0);
str_printfa(cmd, "USER-KICK-ALT\t%s\t%u\t%u\t",
net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq);
str_append_tabescaped(cmd, field);
str_append_c(cmd, '\t');
str_append_tabescaped(cmd, value);
str_append_c(cmd, '\n');
director_update_send_version(dir, src, DIRECTOR_VERSION_USER_KICK_ALT, str_c(cmd));
}
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)
{
const char *cmd;
cmd = t_strdup_printf("proxy\t*\tKICK-DIRECTOR-HASH\t%u\t%s",
username_hash, net_ip2addr(except_ip));
ipc_client_cmd(dir->ipc_proxy, cmd,
director_kick_user_callback, (void *)NULL);
if (orig_src == NULL) {
orig_src = dir->self_host;
orig_src->last_seq++;
}
cmd = t_strdup_printf("USER-KICK-HASH\t%s\t%u\t%u\t%u\t%s\n",
net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq,
username_hash, net_ip2addr(except_ip));
director_update_send_version(dir, src, DIRECTOR_VERSION_USER_KICK, cmd);
}
static void
director_send_user_killed_everywhere(struct director *dir,
struct director_host *src,
struct director_host *orig_src,
unsigned int username_hash)
{
if (orig_src == NULL) {
orig_src = dir->self_host;
orig_src->last_seq++;
}
director_update_send(dir, src, t_strdup_printf(
"USER-KILLED-EVERYWHERE\t%s\t%u\t%u\t%u\n",
net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq,
username_hash));
}
static void
director_user_tag_killed(struct director *dir, struct mail_tag *tag,
unsigned int username_hash)
{
struct user *user;
user = user_directory_lookup(tag->users, username_hash);
if (user == NULL || !USER_IS_BEING_KILLED(user))
return;
switch (user->kill_ctx->kill_state) {
case USER_KILL_STATE_KILLING:
user->kill_ctx->kill_state = USER_KILL_STATE_KILLING_NOTIFY_RECEIVED;
break;
case USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY:
director_finish_user_kill(dir, user, TRUE);
break;
case USER_KILL_STATE_KILLING_NOTIFY_RECEIVED:
dir_debug("User %u kill_state=%s - ignoring USER-KILLED",
username_hash, user_kill_state_names[user->kill_ctx->kill_state]);
break;
case USER_KILL_STATE_NONE:
case USER_KILL_STATE_FLUSHING:
case USER_KILL_STATE_DELAY:
/* move restarted. state=none can also happen if USER-MOVE was
sent while we were still moving. send back
USER-KILLED-EVERYWHERE to avoid hangs. */
director_send_user_killed_everywhere(dir, dir->self_host, NULL,
username_hash);
break;
case USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE:
director_user_killed_everywhere(dir, dir->self_host,
NULL, username_hash);
break;
}
}
void director_user_killed(struct director *dir, unsigned int username_hash)
{
struct mail_tag *const *tagp;
array_foreach(mail_hosts_get_tags(dir->mail_hosts), tagp)
director_user_tag_killed(dir, *tagp, username_hash);
}
static void
director_user_tag_killed_everywhere(struct director *dir,
struct mail_tag *tag,
struct director_host *src,
struct director_host *orig_src,
unsigned int username_hash)
{
struct user *user;
user = user_directory_lookup(tag->users, username_hash);
if (user == NULL) {
dir_debug("User %u no longer exists - ignoring USER-KILLED-EVERYWHERE",
username_hash);
return;
}
if (!USER_IS_BEING_KILLED(user)) {
dir_debug("User %u is no longer being killed - ignoring USER-KILLED-EVERYWHERE",
username_hash);
return;
}
if (user->kill_ctx->kill_state != USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE) {
dir_debug("User %u kill_state=%s - ignoring USER-KILLED-EVERYWHERE",
username_hash, user_kill_state_names[user->kill_ctx->kill_state]);
return;
}
director_flush_user(dir, user);
director_send_user_killed_everywhere(dir, src, orig_src, username_hash);
}
void director_user_killed_everywhere(struct director *dir,
struct director_host *src,
struct director_host *orig_src,
unsigned int username_hash)
{
struct mail_tag *const *tagp;
array_foreach(mail_hosts_get_tags(dir->mail_hosts), tagp) {
director_user_tag_killed_everywhere(dir, *tagp, src, orig_src,
username_hash);
}
}
static void director_state_callback_timeout(struct director *dir)
{
timeout_remove(&dir->to_callback);
dir->state_change_callback(dir);
}
void director_set_state_changed(struct director *dir)
{
/* we may get called to here from various places. use a timeout to
make sure the state callback is called with a clean state. */
if (dir->to_callback == NULL) {
dir->to_callback =
timeout_add(0, director_state_callback_timeout, dir);
}
}
void director_update_send(struct director *dir, struct director_host *src,
const char *cmd)
{
director_update_send_version(dir, src, 0, cmd);
}
void director_update_send_version(struct director *dir,
struct director_host *src,
unsigned int min_version, const char *cmd)
{
struct director_connection *const *connp;
i_assert(src != NULL);
array_foreach(&dir->connections, connp) {
if (director_connection_get_host(*connp) != src &&
director_connection_get_minor_version(*connp) >= min_version)
director_connection_send(*connp, cmd);
}
}
static void director_user_freed(struct user *user)
{
if (user->kill_ctx != NULL) {
/* director_user_expire is very short. user expired before
moving the user finished or timed out. */
if (user->kill_ctx->callback_pending) {
/* kill_ctx is used as a callback parameter.
only remove the timeout and finish the free later. */
if (user->kill_ctx->to_move != NULL)
timeout_remove(&user->kill_ctx->to_move);
} else {
director_user_move_free(user);
}
}
}
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)
{
struct director *dir;
dir = i_new(struct director, 1);
dir->set = set;
dir->self_port = listen_port;
dir->self_ip = *listen_ip;
dir->state_change_callback = callback;
i_array_init(&dir->dir_hosts, 16);
i_array_init(&dir->pending_requests, 16);
i_array_init(&dir->connections, 8);
dir->mail_hosts = mail_hosts_init(set->director_user_expire,
set->director_consistent_hashing,
director_user_freed);
dir->ipc_proxy = ipc_client_init(DIRECTOR_IPC_PROXY_PATH);
dir->ring_min_version = DIRECTOR_VERSION_MINOR;
return dir;
}
void director_deinit(struct director **_dir)
{
struct director *dir = *_dir;
struct director_host *const *hostp, *host;
struct director_connection *conn, *const *connp;
*_dir = NULL;
while (array_count(&dir->connections) > 0) {
connp = array_idx(&dir->connections, 0);
conn = *connp;
director_connection_deinit(&conn, "Shutting down");
}
mail_hosts_deinit(&dir->mail_hosts);
mail_hosts_deinit(&dir->orig_config_hosts);
ipc_client_deinit(&dir->ipc_proxy);
if (dir->to_reconnect != NULL)
timeout_remove(&dir->to_reconnect);
if (dir->to_handshake_warning != NULL)
timeout_remove(&dir->to_handshake_warning);
if (dir->to_request != NULL)
timeout_remove(&dir->to_request);
if (dir->to_sync != NULL)
timeout_remove(&dir->to_sync);
if (dir->to_remove_dirs != NULL)
timeout_remove(&dir->to_remove_dirs);
if (dir->to_callback != NULL)
timeout_remove(&dir->to_callback);
while (array_count(&dir->dir_hosts) > 0) {
hostp = array_idx(&dir->dir_hosts, 0);
host = *hostp;
director_host_free(&host);
}
array_free(&dir->pending_requests);
array_free(&dir->dir_hosts);
array_free(&dir->connections);
i_free(dir);
}
void dir_debug(const char *fmt, ...)
{
va_list args;
if (!director_debug)
return;
va_start(args, fmt);
T_BEGIN {
i_debug("%s", t_strdup_vprintf(fmt, args));
} T_END;
va_end(args);
}
struct director_user_iter {
struct director *dir;
unsigned int tag_idx;
struct user_directory_iter *user_iter;
};
struct director_user_iter *director_iterate_users_init(struct director *dir)
{
struct director_user_iter *iter = i_new(struct director_user_iter, 1);
iter->dir = dir;
return iter;
}
struct user *director_iterate_users_next(struct director_user_iter *iter)
{
const ARRAY_TYPE(mail_tag) *tags;
struct user *user;
i_assert(iter != NULL);
if (iter->user_iter == NULL) {
tags = mail_hosts_get_tags(iter->dir->mail_hosts);
if (iter->tag_idx >= array_count(tags))
return NULL;
struct mail_tag *const *tagp = array_idx(tags, iter->tag_idx);
iter->user_iter = user_directory_iter_init((*tagp)->users);
}
user = user_directory_iter_next(iter->user_iter);
if (user == NULL) {
user_directory_iter_deinit(&iter->user_iter);
iter->tag_idx++;
return director_iterate_users_next(iter);
} else
return user;
}
void director_iterate_users_deinit(struct director_user_iter **_iter)
{
i_assert(_iter != NULL && *_iter != NULL);
struct director_user_iter *iter = *_iter;
*_iter = NULL;
if (iter->user_iter != NULL)
user_directory_iter_deinit(&iter->user_iter);
i_free(iter);
}
bool
director_get_username_hash(struct director *dir, const char *username,
unsigned int *hash_r)
{
const char *error;
if (mail_user_hash(username, dir->set->director_username_hash, hash_r,
&error))
return TRUE;
i_error("Failed to expand director_user_expire=%s: %s",
dir->set->director_username_hash, error);
return FALSE;
}
void directors_init(void)
{
user_move_throttle =
log_throttle_init(&director_log_throttle_settings,
director_user_move_throttled, NULL);
user_kill_fail_throttle =
log_throttle_init(&director_log_throttle_settings,
director_user_kill_fail_throttled, NULL);
}
void directors_deinit(void)
{
log_throttle_deinit(&user_move_throttle);
log_throttle_deinit(&user_kill_fail_throttle);
}