]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
director: Make sure a long-delayed kill reply for user doesn't mess up the state.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 24 Mar 2016 01:00:00 +0000 (10:00 +0900)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 29 Mar 2016 08:21:34 +0000 (11:21 +0300)
This should fix assert-crash:

director: Panic: file director.c: line 690 (director_user_kill_finish_delayed_to): assertion failed: (ctx->user->kill_state == USER_KILL_STATE_DELAY)

src/director/director.c

index 10896c20395a209a894290661e2011681dd0a31c..a07c2e8c110a622b70b66b1e665b9fc325333ed6 100644 (file)
@@ -728,6 +728,8 @@ struct director_kill_context {
 static void
 director_finish_user_kill(struct director *dir, struct user *user, bool self)
 {
+       i_assert(user->kill_state != USER_KILL_STATE_DELAY);
+
        if (dir->right == NULL) {
                /* we're alone */
                director_user_kill_finish_delayed(dir, user);
@@ -748,8 +750,12 @@ static void director_kill_user_callback(enum ipc_client_cmd_state state,
        struct director_kill_context *ctx = context;
        struct user *user;
 
+       /* this is an asynchronous notification about user being killed.
+          there are no guarantees about what might have happened to the user
+          in the mean time. */
        switch (state) {
        case IPC_CLIENT_CMD_STATE_REPLY:
+               /* shouldn't get here. the command reply isn't finished yet. */
                return;
        case IPC_CLIENT_CMD_STATE_OK:
                break;
@@ -761,14 +767,21 @@ static void director_kill_user_callback(enum ipc_client_cmd_state state,
        }
 
        user = user_directory_lookup(ctx->dir->users, ctx->username_hash);
-       if (user == NULL || user->kill_state == USER_KILL_STATE_NONE)
-               return;
-
-       director_finish_user_kill(ctx->dir, user, ctx->self);
+       if (user == NULL) {
+               /* user was already freed - ignore */
+       } else if (user->kill_state == USER_KILL_STATE_KILLING ||
+                  user->kill_state == USER_KILL_STATE_KILLING_NOTIFY_RECEIVED) {
+               /* we were still waiting for the kill notification */
+               director_finish_user_kill(ctx->dir, user, ctx->self);
+       } else {
+               /* we don't currently want to kill the user */
+       }
 }
 
 static void director_user_move_timeout(struct user *user)
 {
+       i_assert(user->kill_state != USER_KILL_STATE_DELAY);
+
        i_error("Finishing user %u move timed out, "
                "its state may now be inconsistent", user->username_hash);