struct director *dir = conn->dir;
struct user *user;
bool ret = FALSE, unset_weak_user = FALSE;
- struct user_directory *users = dir->users;
+ struct user_directory *users = host->tag->users;
*forced_r = FALSE;
if (conn->users_unsorted && conn->user_iter == NULL) {
/* we sent our user list before receiving remote's */
conn->users_unsorted = FALSE;
- user_directory_sort(conn->dir->users);
+ mail_hosts_sort_users(conn->dir->mail_hosts);
}
str = t_str_new(128);
if (conn->users_unsorted && conn->handshake_received) {
/* we received remote's list of users before sending ours */
conn->users_unsorted = FALSE;
- user_directory_sort(conn->dir->users);
+ mail_hosts_sort_users(conn->dir->mail_hosts);
}
ret = o_stream_flush(conn->output);
DIRECTOR_REQUEST_TIMEOUT_SECS > ioloop_time)
break;
- user = user_directory_lookup(request->dir->users,
- request->username_hash);
+ const char *tag_name = request->username_tag == NULL ? "" :
+ request->username_tag;
+ struct mail_tag *tag = mail_tag_find(dir->mail_hosts, tag_name);
+ user = tag == NULL ? NULL :
+ user_directory_lookup(tag->users, request->username_hash);
+
errormsg = director_request_get_timeout_error(request,
user, str);
if (user != NULL &&
}
static bool
-director_request_existing(struct director_request *request, struct user *user,
- const char *tag)
+director_request_existing(struct director_request *request, struct user *user)
{
struct director *dir = request->dir;
- struct user_directory *users = dir->users;
struct mail_host *host;
if (USER_IS_BEING_KILLED(user)) {
request->username_hash);
return FALSE;
}
- if (!user_directory_user_is_near_expiring(users, user))
+ if (!user_directory_user_is_near_expiring(user->host->tag->users, user))
return TRUE;
/* user is close to being expired. another director may have
already expired it. */
- host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash, tag);
+ host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash,
+ user->host->tag->name);
if (!dir->ring_synced) {
/* try again later once ring is synced */
request->delay_reason = REQUEST_DELAY_RINGNOTSYNCED;
bool director_request_continue(struct director_request *request)
{
struct director *dir = request->dir;
- struct user_directory *users = dir->users;
struct mail_host *host;
struct user *user;
const char *tag;
+ struct mail_tag *mail_tag;
if (!dir->ring_handshaked) {
/* delay requests until ring handshaking is complete */
return FALSE;
}
- user = user_directory_lookup(users, request->username_hash);
tag = request->username_tag == NULL ? "" : request->username_tag;
+ mail_tag = mail_tag_find(dir->mail_hosts, tag);
+ user = mail_tag == NULL ? NULL :
+ user_directory_lookup(mail_tag->users, request->username_hash);
+
if (user != NULL) {
- if (!director_request_existing(request, user, tag))
+ i_assert(user->host->tag == mail_tag);
+ if (!director_request_existing(request, user))
return FALSE;
- user_directory_refresh(users, user);
+ user_directory_refresh(mail_tag->users, user);
dir_debug("request: %u refreshed timeout to %u",
request->username_hash, user->timestamp);
} else {
request->username_hash);
return FALSE;
}
- user = user_directory_add(users, request->username_hash,
+ user = user_directory_add(host->tag->users,
+ request->username_hash,
host, ioloop_time);
dir_debug("request: %u added timeout to %u (hosts_hash=%u)",
request->username_hash, user->timestamp,
struct director_host *orig_src,
struct mail_host *host)
{
- struct user_directory *users = dir->users;
+ struct user_directory *users = host->tag->users;
if (src != NULL) {
if (orig_src == NULL) {
struct director_host *orig_src,
struct mail_host *host)
{
- struct user_directory *users = dir->users;
+ struct user_directory *users = host->tag->users;
if (orig_src == NULL) {
orig_src = dir->self_host;
director_flush_user_continue(int result, struct director_kill_context *ctx)
{
struct director *dir = ctx->dir;
- struct user *user =
- user_directory_lookup(dir->users, ctx->username_hash);
-
ctx->callback_pending = FALSE;
+ struct user *user = user_directory_lookup(ctx->tag->users,
+ ctx->username_hash);
+ if (user != NULL)
+ director_user_kill_finish_delayed(dir, user,result == 1);
+
if (result == 0) {
struct istream *is = iostream_temp_finish(&ctx->reply, (size_t)-1);
char *data;
const char *data, void *context)
{
struct director_kill_context *ctx = context;
- struct user_directory *users = ctx->dir->users;
struct user *user;
/* this is an asynchronous notification about user being killed.
ctx->callback_pending = FALSE;
- user = user_directory_lookup(users, ctx->username_hash);
+ user = user_directory_lookup(ctx->tag->users, ctx->username_hash);
if (!DIRECTOR_KILL_CONTEXT_IS_VALID(user, ctx)) {
/* user was already freed - ignore */
i_assert(ctx->to_move == NULL);
user->kill_ctx = ctx = i_new(struct director_kill_context, 1);
ctx->dir = dir;
+ ctx->tag = old_host->tag;
ctx->username_hash = user->username_hash;
ctx->kill_is_self_initiated = src->self;
if (old_host != NULL)
struct director_host *orig_src,
unsigned int username_hash, struct mail_host *host)
{
- struct user_directory *users = dir->users;
+ 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
username_hash));
}
-void director_user_killed(struct director *dir, unsigned int username_hash)
+static void
+director_user_tag_killed(struct director *dir, struct mail_tag *tag,
+ unsigned int username_hash)
{
- struct user_directory *users = dir->users;
struct user *user;
- user = user_directory_lookup(users, username_hash);
+ user = user_directory_lookup(tag->users, username_hash);
if (user == NULL || !USER_IS_BEING_KILLED(user))
return;
}
}
-void director_user_killed_everywhere(struct director *dir,
- struct director_host *src,
- struct director_host *orig_src,
- unsigned int username_hash)
+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(dir->users, username_hash);
+ user = user_directory_lookup(tag->users, username_hash);
if (user == NULL) {
dir_debug("User %u no longer exists - ignoring USER-KILLED-EVERYWHERE",
username_hash);
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);
i_array_init(&dir->dir_hosts, 16);
i_array_init(&dir->pending_requests, 16);
i_array_init(&dir->connections, 8);
- dir->users = user_directory_init(set->director_user_expire,
- director_user_freed);
- dir->mail_hosts = mail_hosts_init(set->director_consistent_hashing);
+ 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;
director_connection_deinit(&conn, "Shutting down");
}
- user_directory_deinit(&dir->users);
mail_hosts_deinit(&dir->mail_hosts);
mail_hosts_deinit(&dir->orig_config_hosts);
}
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->user_iter = user_directory_iter_init(dir->users);
+ iter->dir = dir;
return iter;
}
struct user *director_iterate_users_next(struct director_user_iter *iter)
{
- return user_directory_iter_next(iter->user_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;
- user_directory_iter_deinit(&iter->user_iter);
+ if (iter->user_iter != NULL)
+ user_directory_iter_deinit(&iter->user_iter);
i_free(iter);
}
struct director;
struct mail_host;
struct user;
+struct director_user_init;
enum user_kill_state {
/* User isn't being killed */
struct director_kill_context {
struct director *dir;
+ struct mail_tag *tag;
unsigned int username_hash;
struct ip_addr old_host_ip;
bool kill_is_self_initiated;
/* original mail hosts configured in config file.
this is used only for doveadm lookups */
struct mail_host_list *orig_config_hosts;
- /* temporary user -> host associations */
- struct user_directory *users;
/* Number of users currently being moved */
unsigned int users_moving_count;
string_t *str = t_str_new(1024);
int ret;
- orig_hosts_list = mail_hosts_init(conn->dir->set->director_consistent_hashing);
+ orig_hosts_list = mail_hosts_init(conn->dir->set->director_user_expire,
+ conn->dir->set->director_consistent_hashing,
+ NULL);
(void)mail_hosts_parse_and_add(orig_hosts_list,
conn->dir->set->director_mail_servers);
if (cmd->iter == NULL)
cmd->iter = director_iterate_users_init(dir);
+
while ((user = director_iterate_users_next(cmd->iter)) != NULL) {
if (user->host != host)
continue;
doveadm_cmd_user_lookup(struct doveadm_connection *conn,
const char *const *args)
{
- struct user_directory *users = conn->dir->users;
struct user *user;
struct mail_host *host;
const char *username, *tag;
unsigned int username_hash;
+ struct mail_tag *mail_tag;
string_t *str = t_str_new(256);
if (args[0] == NULL) {
username_hash = director_get_username_hash(conn->dir, username);
/* get user's current host */
- user = user_directory_lookup(users, username_hash);
+ mail_tag = mail_tag_find(conn->dir->mail_hosts, tag);
+ user = mail_tag == NULL ? NULL :
+ user_directory_lookup(mail_tag->users, username_hash);
if (user == NULL)
str_append(str, "\t0");
else {
static int
doveadm_cmd_user_move(struct doveadm_connection *conn, const char *const *args)
{
- struct user_directory *users = conn->dir->users;
unsigned int username_hash;
struct user *user;
struct mail_host *host;
if (str_to_uint(args[0], &username_hash) < 0)
username_hash = director_get_username_hash(conn->dir, args[0]);
- user = user_directory_lookup(users, username_hash);
+ user = user_directory_lookup(host->tag->users, username_hash);
if (user != NULL && USER_IS_BEING_KILLED(user)) {
o_stream_nsend_str(conn->output, "TRYAGAIN\n");
return 1;
#include "bsearch-insert-pos.h"
#include "crc32.h"
#include "md5.h"
+#include "user-directory.h"
#include "mail-host.h"
#define VHOST_MULTIPLIER 100
-struct mail_vhost {
- unsigned int hash;
- struct mail_host *host;
-};
-
-struct mail_tag {
- /* "" = no tag */
- char *name;
- ARRAY(struct mail_vhost) vhosts;
-};
-
struct mail_host_list {
- ARRAY(struct mail_tag *) tags;
+ ARRAY_TYPE(mail_tag) tags;
ARRAY_TYPE(mail_host) hosts;
+ user_free_hook_t *user_free_hook;
unsigned int hosts_hash;
+ unsigned int user_expire_secs;
bool consistent_hashing;
bool vhosts_unsorted;
bool have_vhosts;
}
}
-static struct mail_tag *
+struct mail_tag *
mail_tag_find(struct mail_host_list *list, const char *tag_name)
{
struct mail_tag *const *tagp;
tag = i_new(struct mail_tag, 1);
tag->name = i_strdup(tag_name);
i_array_init(&tag->vhosts, 16*VHOST_MULTIPLIER);
+ tag->users = user_directory_init(list->user_expire_secs,
+ list->user_free_hook);
array_append(&list->tags, &tag, 1);
}
return tag;
static void mail_tag_free(struct mail_tag *tag)
{
+ user_directory_deinit(&tag->users);
array_free(&tag->vhosts);
i_free(tag->name);
i_free(tag);
return FALSE;
}
-struct mail_host_list *mail_hosts_init(bool consistent_hashing)
+const ARRAY_TYPE(mail_tag) *mail_hosts_get_tags(struct mail_host_list *list)
+{
+ return &list->tags;
+}
+
+struct mail_host_list *
+mail_hosts_init(unsigned int user_expire_secs, bool consistent_hashing,
+ user_free_hook_t *user_free_hook)
{
struct mail_host_list *list;
list = i_new(struct mail_host_list, 1);
+ list->user_expire_secs = user_expire_secs;
list->consistent_hashing = consistent_hashing;
+ list->user_free_hook = user_free_hook;
+
i_array_init(&list->hosts, 16);
i_array_init(&list->tags, 4);
return list;
struct mail_host_list *dest;
struct mail_host *const *hostp, *dest_host;
- dest = mail_hosts_init(src->consistent_hashing);
+ dest = mail_hosts_init(src->user_expire_secs, src->consistent_hashing,
+ src->user_free_hook);
array_foreach(&src->hosts, hostp) {
dest_host = mail_host_dup(*hostp);
array_append(&dest->hosts, &dest_host, 1);
mail_hosts_sort(dest);
return dest;
}
+
+void mail_hosts_sort_users(struct mail_host_list *list)
+{
+ struct mail_tag *const *tagp;
+
+ array_foreach(&list->tags, tagp)
+ user_directory_sort((*tagp)->users);
+}
struct mail_host_list;
+struct mail_vhost {
+ unsigned int hash;
+ struct mail_host *host;
+};
+
+/* mail_tags aren't removed/freed before mail_hosts_deinit(), so it's safe
+ to add pointers to them. */
+struct mail_tag {
+ /* "" = no tag */
+ char *name;
+ ARRAY(struct mail_vhost) vhosts;
+ /* temporary user -> host associations */
+ struct user_directory *users;
+};
+ARRAY_DEFINE_TYPE(mail_tag, struct mail_tag *);
+
struct mail_host {
struct mail_host_list *list;
const ARRAY_TYPE(mail_host) *mail_hosts_get(struct mail_host_list *list);
bool mail_hosts_have_tags(struct mail_host_list *list);
-struct mail_host_list *mail_hosts_init(bool consistent_hashing);
+const ARRAY_TYPE(mail_tag) *mail_hosts_get_tags(struct mail_host_list *list);
+struct mail_tag *
+mail_tag_find(struct mail_host_list *list, const char *tag_name);
+struct user *
+mail_hosts_find_user(struct mail_host_list *list, const char *tag_name,
+ unsigned int username_hash);
+
+struct mail_host_list *
+mail_hosts_init(unsigned int user_expire_secs, bool consistent_hashing,
+ user_free_hook_t *user_free_hook);
void mail_hosts_deinit(struct mail_host_list **list);
struct mail_host_list *mail_hosts_dup(const struct mail_host_list *src);
+void mail_hosts_sort_users(struct mail_host_list *list);
#endif
static struct timeout *to_proctitle_refresh;
static ARRAY(enum director_socket_type) listener_socket_types;
+static unsigned int director_total_users_count(void)
+{
+ struct mail_tag *const *tagp;
+ unsigned int count = 0;
+
+ array_foreach(mail_hosts_get_tags(director->mail_hosts), tagp)
+ count += user_directory_count((*tagp)->users);
+ return count;
+}
+
static void director_refresh_proctitle_timeout(void *context ATTR_UNUSED)
{
static uint64_t prev_requests = 0, prev_input = 0, prev_output;
string_t *str;
str = t_str_new(64);
- str_printfa(str, "[%u users", user_directory_count(director->users));
+ str_printfa(str, "[%u users", director_total_users_count());
if (director->users_moving_count > 0)
str_printfa(str, ", %u moving", director->users_moving_count);
str_printfa(str, ", %lu req/s",
/* Copyright (c) 2010-2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "array.h"
#include "ioloop.h"
#include "istream.h"
#include "master-service.h"
struct director *dir;
};
-static void notify_update_user(struct director *dir, const char *username,
- unsigned int username_hash)
+static void notify_update_user(struct director *dir, struct mail_tag *tag,
+ const char *username, unsigned int username_hash)
{
struct user *user;
int diff;
- user = user_directory_lookup(dir->users, username_hash);
+ user = user_directory_lookup(tag->users, username_hash);
if (user == NULL)
return;
i_warning("notify: User %s refreshed too late (%d secs)",
username, diff);
}
- user_directory_refresh(dir->users, user);
+ user_directory_refresh(tag->users, user);
director_update_user(dir, dir->self_host, user);
}
static void notify_connection_input(struct notify_connection *conn)
{
+ struct mail_tag *const *tagp;
const char *line;
unsigned int hash;
while ((line = i_stream_read_next_line(conn->input)) != NULL) {
- hash = director_get_username_hash(conn->dir->users, line);
- notify_update_user(conn->dir, line, hash);
+ hash = director_get_username_hash(conn->dir, line);
+ array_foreach(mail_hosts_get_tags(conn->dir->mail_hosts), tagp)
+ notify_update_user(conn->dir, *tagp, line, hash);
}
if (conn->input->eof) {
i_error("notify: read() unexpectedly returned EOF");