summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/protocol.txt6
-rw-r--r--proto_text.c16
-rw-r--r--t/shutdown.t49
3 files changed, 67 insertions, 4 deletions
diff --git a/doc/protocol.txt b/doc/protocol.txt
index 625db25..2788253 100644
--- a/doc/protocol.txt
+++ b/doc/protocol.txt
@@ -1695,6 +1695,12 @@ be released back to the OS asynchronously.
The argument is in megabytes, not bytes. Input gets multiplied out into
megabytes internally.
+"shutdown" is a command with an optional argument used to stop memcached with
+a kill signal. By default, "shutdown" alone raises SIGINT, though "graceful"
+may be specified as the single argument to instead trigger a graceful shutdown
+with SIGUSR1. The shutdown command is disabled by default, and can be enabled
+with the -A/--enable-shutdown flag.
+
"version" is a command with no arguments:
version\r\n
diff --git a/proto_text.c b/proto_text.c
index 1101c4e..ecc6f92 100644
--- a/proto_text.c
+++ b/proto_text.c
@@ -2348,12 +2348,20 @@ static void process_quit_command(conn *c) {
c->close_after_write = true;
}
-static void process_shutdown_command(conn *c) {
- if (settings.shutdown_command) {
+static void process_shutdown_command(conn *c, token_t *tokens, const size_t ntokens) {
+ if (!settings.shutdown_command) {
+ out_string(c, "ERROR: shutdown not enabled");
+ return;
+ }
+
+ if (ntokens == 2) {
conn_set_state(c, conn_closing);
raise(SIGINT);
+ } else if (ntokens == 3 && strcmp(tokens[SUBCOMMAND_TOKEN].value, "graceful") == 0) {
+ conn_set_state(c, conn_closing);
+ raise(SIGUSR1);
} else {
- out_string(c, "ERROR: shutdown not enabled");
+ out_string(c, "CLIENT_ERROR invalid shutdown mode");
}
}
@@ -2620,7 +2628,7 @@ static void process_command(conn *c, char *command) {
process_stat(c, tokens, ntokens);
} else if (strcmp(tokens[COMMAND_TOKEN].value, "shutdown") == 0) {
- process_shutdown_command(c);
+ process_shutdown_command(c, tokens, ntokens);
} else if (strcmp(tokens[COMMAND_TOKEN].value, "slabs") == 0) {
process_slabs_command(c, tokens, ntokens);
diff --git a/t/shutdown.t b/t/shutdown.t
new file mode 100644
index 0000000..6dfbd77
--- /dev/null
+++ b/t/shutdown.t
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use MemcachedTest;
+
+# Disabled shutdown (default)
+{
+ my $server = new_memcached();
+ my $sock = $server->sock;
+ print $sock "shutdown\r\n";
+ is(scalar <$sock>, "ERROR: shutdown not enabled\r\n",
+ "error when shutdown is not enabled");
+}
+
+# Shutdown command error
+{
+ my$server = new_memcached("-A");
+ my $sock = $server->sock;
+ print $sock "shutdown foo\r\n";
+ like(scalar <$sock>, qr/CLIENT_ERROR/, "rejected invalid shutdown mode");
+}
+
+# Normal shutdown
+{
+ my $server = new_memcached("-A");
+ my $sock = $server->sock;
+ print $sock "version\r\n";
+ like(scalar <$sock>, qr/VERSION/, "server is initially alive");
+ print $sock "shutdown\r\n";
+ print $sock "version\r\n";
+ is(scalar <$sock>, undef, "server has been normally shut down");
+}
+
+# Graceful shutdown
+{
+ my $server = new_memcached("-A");
+ my $sock = $server->sock;
+ print $sock "version\r\n";
+ like(scalar <$sock>, qr/VERSION/, "server is initially alive");
+ print $sock "shutdown graceful\r\n";
+ print $sock "version\r\n";
+ is(scalar <$sock>, undef, "server has been gracefully shut down");
+}
+
+done_testing();