Skip to content

Commit

Permalink
adding missing filters to CLIENT LIST and de-dup parsing code
Browse files Browse the repository at this point in the history
Signed-off-by: Sarthak Aggarwal <[email protected]>
  • Loading branch information
sarthakaggarwal97 committed Dec 6, 2024
1 parent a401e37 commit 54adfa6
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 82 deletions.
180 changes: 98 additions & 82 deletions src/networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -3438,6 +3438,28 @@ sds getAllClientsInfoString(int type, int hide_user_data) {
return o;
}

sds getAllFilteredClientsInfoString(clientFilter *client_filter, int hide_user_data) {
listNode *ln;
listIter li;
client *client;
sds o = sdsnewlen(SDS_NOINIT, 200 * listLength(server.clients));
sdsclear(o);
listRewind(server.clients, &li);
while ((ln = listNext(&li)) != NULL) {
client = listNodeValue(ln);
if (client_filter->addr && strcmp(getClientPeerId(client), client_filter->addr) != 0) continue;
if (client_filter->laddr && strcmp(getClientSockname(client), client_filter->laddr) != 0) continue;
if (client_filter->type != -1 && getClientType(client) != client_filter->type) continue;
if (client_filter->id != 0 && client->id != client_filter->id) continue;
if (client_filter->user && client->user != client_filter->user) continue;
if (client_filter->skipme) continue;
if (client_filter->max_age != 0 && (long long)(commandTimeSnapshot() / 1000 - client->ctime) < client_filter->max_age) continue;
o = catClientInfoString(o, client, hide_user_data);
o = sdscatlen(o, "\n", 1);
}
return o;
}

/* Check validity of an attribute that's gonna be shown in CLIENT LIST. */
int validateClientAttr(const char *val) {
/* Check if the charset is ok. We need to do this otherwise
Expand Down Expand Up @@ -3557,6 +3579,63 @@ void quitCommand(client *c) {
c->flag.close_after_reply = 1;
}

int parseClientFilters(client *c, int i, clientFilter *filter) {
while (i < c->argc) {
int moreargs = c->argc > i + 1;

if (!strcasecmp(c->argv[i]->ptr, "id") && moreargs) {
long tmp;

if (getRangeLongFromObjectOrReply(c, c->argv[i + 1], 1, LONG_MAX, &tmp,
"client-id should be greater than 0") != C_OK)
return C_ERR;
filter->id = tmp;
} else if (!strcasecmp(c->argv[i]->ptr, "maxage") && moreargs) {
long long tmp;

if (getLongLongFromObjectOrReply(c, c->argv[i + 1], &tmp,
"maxage is not an integer or out of range") != C_OK)
return C_ERR;
if (tmp <= 0) {
addReplyError(c, "maxage should be greater than 0");
return C_ERR;
}

filter->max_age = tmp;
} else if (!strcasecmp(c->argv[i]->ptr, "type") && moreargs) {
filter->type = getClientTypeByName(c->argv[i + 1]->ptr);
if (filter->type == -1) {
addReplyErrorFormat(c, "Unknown client type '%s'", (char *) c->argv[i + 1]->ptr);
return C_ERR;
}
} else if (!strcasecmp(c->argv[i]->ptr, "addr") && moreargs) {
filter->addr = c->argv[i + 1]->ptr;
} else if (!strcasecmp(c->argv[i]->ptr, "laddr") && moreargs) {
filter->laddr = c->argv[i + 1]->ptr;
} else if (!strcasecmp(c->argv[i]->ptr, "user") && moreargs) {
filter->user = ACLGetUserByName(c->argv[i + 1]->ptr, sdslen(c->argv[i + 1]->ptr));
if (filter->user == NULL) {
addReplyErrorFormat(c, "No such user '%s'", (char *) c->argv[i + 1]->ptr);
return C_ERR;
}
} else if (!strcasecmp(c->argv[i]->ptr, "skipme") && moreargs) {
if (!strcasecmp(c->argv[i + 1]->ptr, "yes")) {
filter->skipme = 1;
} else if (!strcasecmp(c->argv[i + 1]->ptr, "no")) {
filter->skipme = 0;
} else {
addReplyErrorObject(c, shared.syntaxerr);
return C_ERR;
}
} else {
addReplyErrorObject(c, shared.syntaxerr);
return C_ERR;
}
i += 2;
}
return C_OK;
}

void clientCommand(client *c) {
listNode *ln;
listIter li;
Expand Down Expand Up @@ -3647,21 +3726,14 @@ void clientCommand(client *c) {
addReplyErrorFormat(c, "Unknown client type '%s'", (char *)c->argv[3]->ptr);
return;
}
} else if (c->argc > 3 && !strcasecmp(c->argv[2]->ptr, "id")) {
int j;
o = sdsempty();
for (j = 3; j < c->argc; j++) {
long long cid;
if (getLongLongFromObjectOrReply(c, c->argv[j], &cid, "Invalid client ID")) {
sdsfree(o);
return;
}
client *cl = lookupClientByID(cid);
if (cl) {
o = catClientInfoString(o, cl, 0);
o = sdscatlen(o, "\n", 1);
}
} else if (c->argc > 3) {
clientFilter client_filter = {0, 0, NULL, NULL, NULL, -1, 0};
int i = 2;

if(parseClientFilters(c, i, &client_filter) != C_OK) {
return;
}
o = getAllFilteredClientsInfoString(&client_filter, 0);
} else if (c->argc != 2) {
addReplyErrorObject(c, shared.syntaxerr);
return;
Expand Down Expand Up @@ -3701,75 +3773,19 @@ void clientCommand(client *c) {
} else if (!strcasecmp(c->argv[1]->ptr, "kill")) {
/* CLIENT KILL <ip:port>
* CLIENT KILL <option> [value] ... <option> [value] */
char *addr = NULL;
char *laddr = NULL;
user *user = NULL;
int type = -1;
uint64_t id = 0;
long long max_age = 0;
int skipme = 1;
clientFilter client_filter = {0, 0, NULL, NULL, NULL, -1, 1};
int killed = 0, close_this_client = 0;

if (c->argc == 3) {
/* Old style syntax: CLIENT KILL <addr> */
addr = c->argv[2]->ptr;
skipme = 0; /* With the old form, you can kill yourself. */
client_filter.addr = c->argv[2]->ptr;
client_filter.skipme = 0; /* With the old form, you can kill yourself. */
} else if (c->argc > 3) {
int i = 2; /* Next option index. */

/* New style syntax: parse options. */
while (i < c->argc) {
int moreargs = c->argc > i + 1;

if (!strcasecmp(c->argv[i]->ptr, "id") && moreargs) {
long tmp;

if (getRangeLongFromObjectOrReply(c, c->argv[i + 1], 1, LONG_MAX, &tmp,
"client-id should be greater than 0") != C_OK)
return;
id = tmp;
} else if (!strcasecmp(c->argv[i]->ptr, "maxage") && moreargs) {
long long tmp;

if (getLongLongFromObjectOrReply(c, c->argv[i + 1], &tmp,
"maxage is not an integer or out of range") != C_OK)
return;
if (tmp <= 0) {
addReplyError(c, "maxage should be greater than 0");
return;
}

max_age = tmp;
} else if (!strcasecmp(c->argv[i]->ptr, "type") && moreargs) {
type = getClientTypeByName(c->argv[i + 1]->ptr);
if (type == -1) {
addReplyErrorFormat(c, "Unknown client type '%s'", (char *)c->argv[i + 1]->ptr);
return;
}
} else if (!strcasecmp(c->argv[i]->ptr, "addr") && moreargs) {
addr = c->argv[i + 1]->ptr;
} else if (!strcasecmp(c->argv[i]->ptr, "laddr") && moreargs) {
laddr = c->argv[i + 1]->ptr;
} else if (!strcasecmp(c->argv[i]->ptr, "user") && moreargs) {
user = ACLGetUserByName(c->argv[i + 1]->ptr, sdslen(c->argv[i + 1]->ptr));
if (user == NULL) {
addReplyErrorFormat(c, "No such user '%s'", (char *)c->argv[i + 1]->ptr);
return;
}
} else if (!strcasecmp(c->argv[i]->ptr, "skipme") && moreargs) {
if (!strcasecmp(c->argv[i + 1]->ptr, "yes")) {
skipme = 1;
} else if (!strcasecmp(c->argv[i + 1]->ptr, "no")) {
skipme = 0;
} else {
addReplyErrorObject(c, shared.syntaxerr);
return;
}
} else {
addReplyErrorObject(c, shared.syntaxerr);
return;
}
i += 2;
if(parseClientFilters(c, i, &client_filter) != C_OK) {
return;
}
} else {
addReplyErrorObject(c, shared.syntaxerr);
Expand All @@ -3780,13 +3796,13 @@ void clientCommand(client *c) {
listRewind(server.clients, &li);
while ((ln = listNext(&li)) != NULL) {
client *client = listNodeValue(ln);
if (addr && strcmp(getClientPeerId(client), addr) != 0) continue;
if (laddr && strcmp(getClientSockname(client), laddr) != 0) continue;
if (type != -1 && getClientType(client) != type) continue;
if (id != 0 && client->id != id) continue;
if (user && client->user != user) continue;
if (c == client && skipme) continue;
if (max_age != 0 && (long long)(commandTimeSnapshot() / 1000 - client->ctime) < max_age) continue;
if (client_filter.addr && strcmp(getClientPeerId(client), client_filter.addr) != 0) continue;
if (client_filter.laddr && strcmp(getClientSockname(client), client_filter.laddr) != 0) continue;
if (client_filter.type != -1 && getClientType(client) != client_filter.type) continue;
if (client_filter.id != 0 && client->id != client_filter.id) continue;
if (client_filter.user && client->user != client_filter.user) continue;
if (c == client && client_filter.skipme) continue;
if (client_filter.max_age != 0 && (long long)(commandTimeSnapshot() / 1000 - client->ctime) < client_filter.max_age) continue;

/* Kill it. */
if (c == client) {
Expand Down
12 changes: 12 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,16 @@ typedef struct client {
net_output_bytes_curr_cmd; /* Total network output bytes sent to this client, by the current command. */
} client;

typedef struct {
uint64_t id;
long long max_age;
char *addr;
char *laddr;
user *user;
int type;
int skipme;
} clientFilter;

/* When a command generates a lot of discrete elements to the client output buffer, it is much faster to
* skip certain types of initialization. This type is used to indicate a client that has been initialized
* and can be used with addWritePreparedReply* functions. A client can be cast into this type with
Expand Down Expand Up @@ -2868,6 +2878,7 @@ int isClientConnIpV6(client *c);
sds catClientInfoString(sds s, client *client, int hide_user_data);
sds catClientInfoShortString(sds s, client *client, int hide_user_data);
sds getAllClientsInfoString(int type, int hide_user_data);
sds getAllFilteredClientsInfoString(clientFilter *client_filter, int hide_user_data);
int clientSetName(client *c, robj *name, const char **err);
void rewriteClientCommandVector(client *c, int argc, ...);
void rewriteClientCommandArgument(client *c, int i, robj *newval);
Expand Down Expand Up @@ -3946,6 +3957,7 @@ void dumpCommand(client *c);
void objectCommand(client *c);
void memoryCommand(client *c);
void clientCommand(client *c);
int parseClientFilters(client *c, int i, clientFilter *filter);
void helloCommand(client *c);
void clientSetinfoCommand(client *c);
void evalCommand(client *c);
Expand Down

0 comments on commit 54adfa6

Please sign in to comment.