|
|
|
@@ -75,6 +75,8 @@ void linkClient(client *c) {
|
|
|
|
|
* this way removing the client in unlinkClient() will not require
|
|
|
|
|
* a linear scan, but just a constant time operation. */
|
|
|
|
|
c->client_list_node = listLast(server.clients);
|
|
|
|
|
uint64_t id = htonu64(c->id);
|
|
|
|
|
raxInsert(server.clients_index,(unsigned char*)&id,sizeof(id),c,NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client *createClient(int fd) {
|
|
|
|
@@ -720,6 +722,8 @@ void unlinkClient(client *c) {
|
|
|
|
|
if (c->fd != -1) {
|
|
|
|
|
/* Remove from the list of active clients. */
|
|
|
|
|
if (c->client_list_node) {
|
|
|
|
|
uint64_t id = htonu64(c->id);
|
|
|
|
|
raxRemove(server.clients_index,(unsigned char*)&id,sizeof(id),NULL);
|
|
|
|
|
listDelNode(server.clients,c->client_list_node);
|
|
|
|
|
c->client_list_node = NULL;
|
|
|
|
|
}
|
|
|
|
@@ -864,6 +868,15 @@ void freeClientsInAsyncFreeQueue(void) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a client by ID, or NULL if the client ID is not in the set
|
|
|
|
|
* of registered clients. Note that "fake clients", created with -1 as FD,
|
|
|
|
|
* are not registered clients. */
|
|
|
|
|
client *lookupClientByID(uint64_t id) {
|
|
|
|
|
id = htonu64(id);
|
|
|
|
|
client *c = raxFind(server.clients_index,(unsigned char*)&id,sizeof(id));
|
|
|
|
|
return (c == raxNotFound) ? NULL : c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write data in output buffers to client. Return C_OK if the client
|
|
|
|
|
* is still valid after the call, C_ERR if it was freed. */
|
|
|
|
|
int writeToClient(int fd, client *c, int handler_installed) {
|
|
|
|
@@ -1553,6 +1566,7 @@ void clientCommand(client *c) {
|
|
|
|
|
|
|
|
|
|
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
|
|
|
|
|
const char *help[] = {
|
|
|
|
|
"id -- Return the ID of the current connection.",
|
|
|
|
|
"getname -- Return the name of the current connection.",
|
|
|
|
|
"kill <ip:port> -- Kill connection made from <ip:port>.",
|
|
|
|
|
"kill <option> <value> [option value ...] -- Kill connections. Options are:",
|
|
|
|
@@ -1566,6 +1580,9 @@ void clientCommand(client *c) {
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
addReplyHelp(c, help);
|
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"id") && c->argc == 2) {
|
|
|
|
|
/* CLIENT ID */
|
|
|
|
|
addReplyLongLong(c,c->id);
|
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"list") && c->argc == 2) {
|
|
|
|
|
/* CLIENT LIST */
|
|
|
|
|
sds o = getAllClientsInfoString();
|
|
|
|
@@ -1671,6 +1688,19 @@ NULL
|
|
|
|
|
/* If this client has to be closed, flag it as CLOSE_AFTER_REPLY
|
|
|
|
|
* only after we queued the reply to its output buffers. */
|
|
|
|
|
if (close_this_client) c->flags |= CLIENT_CLOSE_AFTER_REPLY;
|
|
|
|
|
} else if (!strcasecmp(c->argv[1]->ptr,"unblock") && c->argc == 3) {
|
|
|
|
|
/* CLIENT UNBLOCK <id> */
|
|
|
|
|
long long id;
|
|
|
|
|
if (getLongLongFromObjectOrReply(c,c->argv[2],&id,NULL)
|
|
|
|
|
!= C_OK) return;
|
|
|
|
|
struct client *target = lookupClientByID(id);
|
|
|
|
|
if (target && target->flags & CLIENT_BLOCKED) {
|
|
|
|
|
replyToBlockedClientTimedOut(target);
|
|
|
|
|
unblockClient(target);
|
|
|
|
|
addReply(c,shared.cone);
|
|
|
|
|
} else {
|
|
|
|
|
addReply(c,shared.czero);
|
|
|
|
|
}
|
|
|
|
|
} 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;
|
|
|
|
|