From e7affd266c4884ce4fc60e567e3aae55cac49828 Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Mon, 16 Jun 2014 14:24:28 +0200
Subject: [PATCH] New features for CLIENT KILL.

---
 src/networking.c | 71 +++++++++++++++++++++++++++++++++++++++---------
 src/redis.h      |  1 +
 2 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/src/networking.c b/src/networking.c
index 99e61de3..4d2b1054 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -1346,27 +1346,72 @@ void clientCommand(redisClient *c) {
     redisClient *client;
 
     if (!strcasecmp(c->argv[1]->ptr,"list") && c->argc == 2) {
+        /* CLIENT LIST */
         sds o = getAllClientsInfoString();
         addReplyBulkCBuffer(c,o,sdslen(o));
         sdsfree(o);
-    } else if (!strcasecmp(c->argv[1]->ptr,"kill") && c->argc == 3) {
-        listRewind(server.clients,&li);
-        while ((ln = listNext(&li)) != NULL) {
-            char *peerid;
+    } else if (!strcasecmp(c->argv[1]->ptr,"kill")) {
+        /* CLIENT KILL <ip:port>
+         * CLIENT KILL <attrib> <value> */
+        char *addr = NULL;
+        int type = -1;
+        uint64_t id = 0;
+        int killed = 0;
 
-            client = listNodeValue(ln);
-            peerid = getClientPeerId(client);
-            if (strcmp(peerid,c->argv[2]->ptr) == 0) {
-                addReply(c,shared.ok);
-                if (c == client) {
-                    client->flags |= REDIS_CLOSE_AFTER_REPLY;
-                } else {
-                    freeClient(client);
+        /* Parse arguments. */
+        if (c->argc == 3) {
+            addr = c->argv[2]->ptr;
+        } else if (c->argc == 4) {
+            if (!strcasecmp(c->argv[2]->ptr,"id")) {
+                long long tmp;
+
+                if (getLongLongFromObjectOrReply(c,c->argv[3],&tmp,NULL)
+                    != REDIS_OK) return;
+                id = tmp;
+            } else if (!strcasecmp(c->argv[2]->ptr,"type")) {
+                type = getClientTypeByName(c->argv[3]->ptr);
+                if (type == -1) {
+                    addReplyErrorFormat(c,"Unknown client type '%s'",
+                        (char*) c->argv[3]->ptr);
+                    return;
                 }
+            } else if (!strcasecmp(c->argv[2]->ptr,"addr")) {
+                addr = c->argv[3]->ptr;
+            } else {
+                addReply(c,shared.syntaxerr);
                 return;
             }
+        } else {
+            addReply(c,shared.syntaxerr);
+            return;
+        }
+
+        /* Iterate clients killing all the matching clients. */
+        listRewind(server.clients,&li);
+        while ((ln = listNext(&li)) != NULL) {
+            client = listNodeValue(ln);
+            if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;
+            if (type != -1 && getClientType(client) != type) continue;
+            if (id != 0 && client->id != id) continue;
+
+            /* Kill it. */
+            if (c == client) {
+                client->flags |= REDIS_CLOSE_AFTER_REPLY;
+            } else {
+                freeClient(client);
+            }
+            killed++;
+        }
+
+        /* Reply according to old/new format. */
+        if (c->argc == 3) {
+            if (killed == 0)
+                addReplyError(c,"No such client");
+            else
+                addReply(c,shared.ok);
+        } else {
+            addReplyLongLong(c,killed);
         }
-        addReplyError(c,"No such client");
     } else if (!strcasecmp(c->argv[1]->ptr,"setname") && c->argc == 3) {
         int j, len = sdslen(c->argv[2]->ptr);
         char *p = c->argv[2]->ptr;
diff --git a/src/redis.h b/src/redis.h
index d16933d5..02def27d 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -1004,6 +1004,7 @@ void rewriteClientCommandArgument(redisClient *c, int i, robj *newval);
 unsigned long getClientOutputBufferMemoryUsage(redisClient *c);
 void freeClientsInAsyncFreeQueue(void);
 void asyncCloseClientOnOutputBufferLimitReached(redisClient *c);
+int getClientType(redisClient *c);
 int getClientTypeByName(char *name);
 char *getClientTypeName(int class);
 void flushSlavesOutputBuffers(void);