From: Salvatore Sanfilippo Date: Tue, 22 Nov 2011 10:43:28 +0000 (-0800) Subject: Merge pull request #199 from jerem/patch-1 X-Git-Url: https://git.saurik.com/redis.git/commitdiff_plain/4e5f63a60c36f58bdecedc43262e7f89e28656d7?hp=92a157eae5553553b4259d984e47b99ab82a03d0 Merge pull request #199 from jerem/patch-1 Fixed a typo in comments. --- diff --git a/.gitignore b/.gitignore index 1f480bd4..5f262c46 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ deps/lua/src/lua deps/lua/src/luac deps/lua/src/liblua.a .make-* +.prerequisites diff --git a/src/ae.c b/src/ae.c index c7918ee1..05802890 100644 --- a/src/ae.c +++ b/src/ae.c @@ -118,6 +118,13 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) aeApiDelEvent(eventLoop, fd, mask); } +int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { + if (fd >= AE_SETSIZE) return 0; + aeFileEvent *fe = &eventLoop->events[fd]; + + return fe->mask; +} + static void aeGetTime(long *seconds, long *milliseconds) { struct timeval tv; diff --git a/src/ae.h b/src/ae.h index a9db18ed..9e23a6fd 100644 --- a/src/ae.h +++ b/src/ae.h @@ -104,6 +104,7 @@ void aeStop(aeEventLoop *eventLoop); int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData); void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask); +int aeGetFileEvents(aeEventLoop *eventLoop, int fd); long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, aeTimeProc *proc, void *clientData, aeEventFinalizerProc *finalizerProc); diff --git a/src/networking.c b/src/networking.c index edd7891d..537ebaba 100644 --- a/src/networking.c +++ b/src/networking.c @@ -903,6 +903,13 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { } else { return; } + if (sdslen(c->querybuf) > server.client_max_querybuf_len) { + sds ci = getClientInfoString(c); + redisLog(REDIS_WARNING,"Closing client that reached max query buffer length: %s", ci); + sdsfree(ci); + freeClient(c); + return; + } processInputBuffer(c); } @@ -924,6 +931,53 @@ void getClientsMaxBuffers(unsigned long *longest_output_list, *biggest_input_buffer = bib; } +/* Turn a Redis client into an sds string representing its state. */ +sds getClientInfoString(redisClient *client) { + char ip[32], flags[16], events[3], *p; + int port; + time_t now = time(NULL); + int emask; + + if (anetPeerToString(client->fd,ip,&port) == -1) { + ip[0] = '?'; + ip[1] = '\0'; + port = 0; + } + p = flags; + if (client->flags & REDIS_SLAVE) { + if (client->flags & REDIS_MONITOR) + *p++ = 'O'; + else + *p++ = 'S'; + } + if (client->flags & REDIS_MASTER) *p++ = 'M'; + if (client->flags & REDIS_MULTI) *p++ = 'x'; + if (client->flags & REDIS_BLOCKED) *p++ = 'b'; + if (client->flags & REDIS_DIRTY_CAS) *p++ = 'd'; + if (client->flags & REDIS_CLOSE_AFTER_REPLY) *p++ = 'c'; + if (client->flags & REDIS_UNBLOCKED) *p++ = 'u'; + if (p == flags) *p++ = 'N'; + *p++ = '\0'; + + emask = client->fd == -1 ? 0 : aeGetFileEvents(server.el,client->fd); + p = events; + if (emask & AE_READABLE) *p++ = 'r'; + if (emask & AE_WRITABLE) *p++ = 'w'; + *p = '\0'; + return sdscatprintf(sdsempty(), + "addr=%s:%d fd=%d idle=%ld flags=%s db=%d sub=%d psub=%d qbuf=%lu obl=%lu oll=%lu events=%s", + ip,port,client->fd, + (long)(now - client->lastinteraction), + flags, + client->db->id, + (int) dictSize(client->pubsub_channels), + (int) listLength(client->pubsub_patterns), + (unsigned long) sdslen(client->querybuf), + (unsigned long) client->bufpos, + (unsigned long) listLength(client->reply), + events); +} + void clientCommand(redisClient *c) { listNode *ln; listIter li; @@ -931,38 +985,12 @@ void clientCommand(redisClient *c) { if (!strcasecmp(c->argv[1]->ptr,"list") && c->argc == 2) { sds o = sdsempty(); - time_t now = time(NULL); listRewind(server.clients,&li); while ((ln = listNext(&li)) != NULL) { - char ip[32], flags[16], *p; - int port; - client = listNodeValue(ln); - if (anetPeerToString(client->fd,ip,&port) == -1) continue; - p = flags; - if (client->flags & REDIS_SLAVE) { - if (client->flags & REDIS_MONITOR) - *p++ = 'O'; - else - *p++ = 'S'; - } - if (client->flags & REDIS_MASTER) *p++ = 'M'; - if (p == flags) *p++ = 'N'; - if (client->flags & REDIS_MULTI) *p++ = 'x'; - if (client->flags & REDIS_BLOCKED) *p++ = 'b'; - if (client->flags & REDIS_DIRTY_CAS) *p++ = 'd'; - if (client->flags & REDIS_CLOSE_AFTER_REPLY) *p++ = 'c'; - if (client->flags & REDIS_UNBLOCKED) *p++ = 'u'; - *p++ = '\0'; - o = sdscatprintf(o, - "addr=%s:%d fd=%d idle=%ld flags=%s db=%d sub=%d psub=%d\n", - ip,port,client->fd, - (long)(now - client->lastinteraction), - flags, - client->db->id, - (int) dictSize(client->pubsub_channels), - (int) listLength(client->pubsub_patterns)); + o = sdscatsds(o,getClientInfoString(client)); + o = sdscatlen(o,"\n",1); } addReplyBulkCBuffer(c,o,sdslen(o)); sdsfree(o); diff --git a/src/redis.c b/src/redis.c index 91e1e10c..7e324aa1 100644 --- a/src/redis.c +++ b/src/redis.c @@ -70,9 +70,31 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan; struct redisServer server; /* server global state */ struct redisCommand *commandTable; -/* Our command table. Command flags are expressed using strings where every - * character represents a flag. Later the populateCommandTable() function will - * take care of populating the real 'flags' field using this characters. +/* Our command table. + * + * Every entry is composed of the following fields: + * + * name: a string representing the command name. + * function: pointer to the C function implementing the command. + * arity: number of arguments, it is possible to use -N to say >= N + * sflags: command flags as string. See below for a table of flags. + * flags: flags as bitmask. Computed by Redis using the 'sflags' field. + * get_keys_proc: an optional function to get key arguments from a command. + * This is only used when the following three fields are not + * enough to specify what arguments are keys. + * first_key_index: first argument that is a key + * last_key_index: last argument that is a key + * key_step: step to get all the keys from first to last argument. For instance + * in MSET the step is two since arguments are key,val,key,val,... + * microseconds: microseconds of total execution time for this command. + * calls: total number of calls of this command. + * + * The flags, microseconds and calls fields are computed by Redis and should + * always be set to zero. + * + * Command flags are expressed using strings where every character represents + * a flag. Later the populateCommandTable() function will take care of + * populating the real 'flags' field using this characters. * * This is the meaning of the flags: * @@ -83,15 +105,15 @@ struct redisCommand *commandTable; * p: Pub/Sub related command. * f: force replication of this command, regarless of server.dirty. * s: command not allowed in scripts. - * r: random command. Command is not deterministic, that is, the same command + * R: random command. Command is not deterministic, that is, the same command * with the same arguments, with the same key space, may have different * results. For instance SPOP and RANDOMKEY are two random commands. */ struct redisCommand redisCommandTable[] = { {"get",getCommand,2,"r",0,NULL,1,1,1,0,0}, {"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0}, {"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0}, - {"setex",setexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0}, - {"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0}, + {"setex",setexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0}, + {"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0}, {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0}, {"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0}, {"del",delCommand,-2,"w",0,noPreloadGetKeys,1,-1,1,0,0}, @@ -843,6 +865,7 @@ void initServerConfig() { server.dbnum = REDIS_DEFAULT_DBNUM; server.verbosity = REDIS_VERBOSE; server.maxidletime = REDIS_MAXIDLETIME; + server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN; server.saveparams = NULL; server.loading = 0; server.logfile = NULL; /* NULL = log on standard output */ diff --git a/src/redis.h b/src/redis.h index d532e385..40d0d39a 100644 --- a/src/redis.h +++ b/src/redis.h @@ -40,6 +40,7 @@ /* Static server configuration */ #define REDIS_SERVERPORT 6379 /* TCP port */ #define REDIS_MAXIDLETIME 0 /* default client timeout: infinite */ +#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */ #define REDIS_IOBUF_LEN (1024*16) #define REDIS_LOADBUF_LEN 1024 #define REDIS_DEFAULT_DBNUM 16 @@ -533,6 +534,7 @@ struct redisServer { /* Configuration */ int verbosity; int maxidletime; + size_t client_max_querybuf_len; int dbnum; int daemonize; int appendonly; @@ -765,6 +767,7 @@ void addReplyMultiBulkLen(redisClient *c, long length); void *dupClientReplyValue(void *o); void getClientsMaxBuffers(unsigned long *longest_output_list, unsigned long *biggest_input_buffer); +sds getClientInfoString(redisClient *client); void rewriteClientCommandVector(redisClient *c, int argc, ...); void rewriteClientCommandArgument(redisClient *c, int i, robj *newval); diff --git a/src/sds.c b/src/sds.c index c3a0ccb9..b6c2aaf3 100644 --- a/src/sds.c +++ b/src/sds.c @@ -196,6 +196,10 @@ sds sdscat(sds s, char *t) { return sdscatlen(s, t, strlen(t)); } +sds sdscatsds(sds s, sds t) { + return sdscatlen(s, t, sdslen(t)); +} + sds sdscpylen(sds s, char *t, size_t len) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); size_t totlen = sh->free+sh->len; diff --git a/src/sds.h b/src/sds.h index eff1b03e..ee336d1d 100644 --- a/src/sds.h +++ b/src/sds.h @@ -62,6 +62,7 @@ size_t sdsavail(sds s); sds sdsgrowzero(sds s, size_t len); sds sdscatlen(sds s, void *t, size_t len); sds sdscat(sds s, char *t); +sds sdscatsds(sds s, sds t); sds sdscpylen(sds s, char *t, size_t len); sds sdscpy(sds s, char *t);