changeset 22506:3afbfedcdd31

director: Fix crash when flush is run and all backends are down. Instead of moving the users elsewhere, just kill them and flush the backend.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 22 Aug 2017 16:32:32 +0300
parents 3bf3e3364e14
children 633b90217c62
files src/director/director.c src/director/director.h src/director/doveadm-connection.c
diffstat 3 files changed, 23 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/director/director.c	Wed Sep 20 15:03:55 2017 +0300
+++ b/src/director/director.c	Tue Aug 22 16:32:32 2017 +0300
@@ -984,10 +984,9 @@
 	director_user_move_free(user);
 }
 
-static void
-director_kill_user(struct director *dir, struct director_host *src,
-		   struct user *user, struct mail_tag *tag,
-		   struct mail_host *old_host)
+void director_kill_user(struct director *dir, struct director_host *src,
+			struct user *user, struct mail_tag *tag,
+			struct mail_host *old_host)
 {
 	struct director_kill_context *ctx;
 	const char *cmd;
--- a/src/director/director.h	Wed Sep 20 15:03:55 2017 +0300
+++ b/src/director/director.h	Tue Aug 22 16:32:32 2017 +0300
@@ -201,6 +201,9 @@
 			       struct director_connection *src_conn,
 			       struct director_host *orig_src,
 			       struct user *user) ATTR_NULL(3);
+void director_kill_user(struct director *dir, struct director_host *src,
+			struct user *user, struct mail_tag *tag,
+			struct mail_host *old_host);
 void director_move_user(struct director *dir, struct director_host *src,
 			struct director_host *orig_src,
 			unsigned int username_hash, struct mail_host *host)
--- a/src/director/doveadm-connection.c	Wed Sep 20 15:03:55 2017 +0300
+++ b/src/director/doveadm-connection.c	Tue Aug 22 16:32:32 2017 +0300
@@ -443,6 +443,7 @@
 	struct director *dir = cmd->dir;
 	struct user *user;
 	struct mail_host *new_host;
+	bool users_killed = FALSE;
 
 	if (dir->users_moving_count >= cmd->max_moving_users)
 		return FALSE;
@@ -456,18 +457,32 @@
 	while ((user = director_iterate_users_next(cmd->iter)) != NULL) {
 		if (user->host != host)
 			continue;
+
 		new_host = mail_host_get_by_hash(dir->mail_hosts,
 						 user->username_hash,
 						 mail_host_get_tag(host));
 		if (new_host != host) T_BEGIN {
-			director_move_user(dir, dir->self_host, NULL,
-					   user->username_hash, new_host);
+			if (new_host != NULL) {
+				director_move_user(dir, dir->self_host, NULL,
+					user->username_hash, new_host);
+			} else {
+				/* there are no more available backends.
+				   kick the user instead. */
+				director_kill_user(dir, dir->self_host, user,
+						   user->host->tag, user->host);
+				users_killed = TRUE;
+			}
 		} T_END;
 		if (dir->users_moving_count >= cmd->max_moving_users)
 			break;
 	}
 	if (user == NULL)
 		director_iterate_users_deinit(&cmd->iter);
+	if (users_killed) {
+		/* no more backends. we already sent kills. now remove the
+		   users entirely from the host. */
+		director_flush_host(dir, dir->self_host, NULL, host);
+	}
 	if (dir->right != NULL)
 		director_connection_uncork(dir->right);
 	return user == NULL;