Change login command kill to kill less ruthlessly
authorMarkus Armbruster <armbru@pond.sub.org>
Sun, 18 Mar 2012 17:30:39 +0000 (18:30 +0100)
committerMarkus Armbruster <armbru@pond.sub.org>
Thu, 26 Apr 2012 17:57:19 +0000 (19:57 +0200)
The victim's connection closes without any explanation.  Output may be
lost.  This is because kill_cmd() kills by calling io_shutdown(),
which shuts down the socket and drains the I/O queues.

How this makes the victim's thread terminate is a bit subtle: shutting
down the socket makes it ready.  If the victim's thread is waiting for
I/O, it wakes up.  Since all further reads return EOF, and all further
writes fail, the command terminates quickly (short of inifinite loop
bugs), then the command loop, and finally the thread.

To make kill behave more nicely, change kill_cmd() to work exactly
like server shutdown: send a flash message to the victim, set his EOF
indicator, abort the command, forbid sleeping on I/O, wake up the
victim's thread.  Just as reliable, but doesn't lose output.

If the victim's client fails to close his connection, the victim's
thread may still linger in state PS_SHUTDOWN for up to
login_grace_time (default 120s).  An attacker could try to use that to
make the server run out of file descriptors or memory, but simply
connecting achieves the same effect more cheaply.

src/lib/player/login.c

index aa0033bee9d44e8024e382105112ac2673547b9c..743739658c3942e418208e920f4cf99fec98f7e0 100644 (file)
@@ -381,8 +381,12 @@ kill_cmd(void)
        return RET_FAIL;
     }
     logerror("%s killed country #%d", praddr(player), player->cnum);
-    io_shutdown(other->iop, IO_READ | IO_WRITE);
-    pr_id(player, C_EXIT, "closed socket of offending job\n");
+    pr_flash(other, "Disconnected by %s\n", praddr(player));
+    io_set_eof(other->iop);
+    other->aborted = 1;
+    other->may_sleep = PLAYER_SLEEP_NEVER;
+    empth_wakeup(other->proc);
+    pr_id(player, C_EXIT, "terminated %s's connection\n", praddr(other));
     return RET_OK;
 }