From 1971740f0c8c4bf9a484188f5966e8c2a9369490 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 11 Jan 2013 18:43:28 +0100 Subject: [PATCH] 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. --- src/aof.c | 1 + src/networking.c | 41 +++++++++++++++++++++++++++++++++++++++-- src/redis.h | 1 + 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 be8f6a5d5..b8ee62cb7 100644 --- a/src/redis.h +++ b/src/redis.h @@ -382,6 +382,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; -- GitLab