summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2013-01-11 18:43:28 +0100
committerantirez <antirez@gmail.com>2013-01-15 13:34:34 +0100
commitc5f23ca79bf203c021fdd32d531487d9d15ab3b8 (patch)
treeea7a78c5db14a2066221483b5263a2d0af04dcaf
parent27abaa238ff2b9d71a7d4f6aee0b6cd6fc615ed0 (diff)
downloadredis-c5f23ca79bf203c021fdd32d531487d9d15ab3b8.tar.gz
CLIENT GETNAME and CLIENT SETNAME introduced.
Sometimes it is much simpler to debug complex Redis installations if it is possible to assign clients a name that is displayed in the CLIENT LIST output. This is the case, for example, for "leaked" connections. The ability to provide a name to the client makes it quite trivial to understand what is the part of the code implementing the client not releasing the resources appropriately. Behavior: CLIENT SETNAME: set a name for the client, or remove the current name if an empty name is set. CLIENT GETNAME: get the current name, or a nil. CLIENT LIST: now displays the client name if any. Thanks to Mark Gravell for pushing this idea forward.
-rw-r--r--src/aof.c1
-rw-r--r--src/networking.c41
-rw-r--r--src/redis.h1
3 files changed, 41 insertions, 2 deletions
diff --git a/src/aof.c b/src/aof.c
index 6289e4dd5..fe5c64972 100644
--- a/src/aof.c
+++ b/src/aof.c
@@ -442,6 +442,7 @@ struct redisClient *createFakeClient(void) {
selectDb(c,0);
c->fd = -1;
+ c->name = NULL;
c->querybuf = sdsempty();
c->querybuf_peak = 0;
c->argc = 0;
diff --git a/src/networking.c b/src/networking.c
index c23939c5c..d935eaa82 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -70,6 +70,7 @@ redisClient *createClient(int fd) {
selectDb(c,0);
c->fd = fd;
+ c->name = NULL;
c->bufpos = 0;
c->querybuf = sdsempty();
c->querybuf_peak = 0;
@@ -668,6 +669,7 @@ void freeClient(redisClient *c) {
}
/* Release memory */
+ if (c->name) decrRefCount(c->name);
zfree(c->argv);
freeClientMultiState(c);
zfree(c);
@@ -1123,9 +1125,11 @@ sds getClientInfoString(redisClient *client) {
if (emask & AE_WRITABLE) *p++ = 'w';
*p = '\0';
return sdscatprintf(sdsempty(),
- "addr=%s:%d fd=%d age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
+ "addr=%s:%d fd=%d name=%s age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
(client->flags & REDIS_UNIX_SOCKET) ? server.unixsocket : ip,
- port,client->fd,
+ port,
+ client->fd,
+ client->name ? (char*)client->name->ptr : "",
(long)(server.unixtime - client->ctime),
(long)(server.unixtime - client->lastinteraction),
flags,
@@ -1190,6 +1194,39 @@ void clientCommand(redisClient *c) {
}
}
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;
+
+ /* Setting the client name to an empty string actually removes
+ * the current name. */
+ if (len == 0) {
+ if (c->name) decrRefCount(c->name);
+ c->name = NULL;
+ addReply(c,shared.ok);
+ return;
+ }
+
+ /* Otherwise check if the charset is ok. We need to do this otherwise
+ * CLIENT LIST format will break. You should always be able to
+ * split by space to get the different fields. */
+ for (j = 0; j < len; j++) {
+ if (p[j] < '!' || p[j] > '~') { /* ASCI is assumed. */
+ addReplyError(c,
+ "Client names cannot contain spaces, "
+ "newlines or special characters.");
+ return;
+ }
+ }
+ if (c->name) decrRefCount(c->name);
+ c->name = c->argv[2];
+ incrRefCount(c->name);
+ addReply(c,shared.ok);
+ } else if (!strcasecmp(c->argv[1]->ptr,"getname") && c->argc == 2) {
+ if (c->name)
+ addReplyBulk(c,c->name);
+ else
+ addReply(c,shared.nullbulk);
} else {
addReplyError(c, "Syntax error, try CLIENT (LIST | KILL ip:port)");
}
diff --git a/src/redis.h b/src/redis.h
index 071cb534e..1133fed5b 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -380,6 +380,7 @@ typedef struct redisClient {
int fd;
redisDb *db;
int dictid;
+ robj *name; /* As set by CLIENT SETNAME */
sds querybuf;
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */
int argc;