]> git.saurik.com Git - redis.git/blame - redis.c
MGET tests added
[redis.git] / redis.c
CommitLineData
ed9b544e 1/*
2 * Copyright (c) 2006-2009, Salvatore Sanfilippo <antirez at gmail dot com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#define REDIS_VERSION "0.07"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <time.h>
36#include <unistd.h>
37#include <signal.h>
38#include <sys/wait.h>
39#include <errno.h>
40#include <assert.h>
41#include <ctype.h>
42#include <stdarg.h>
43#include <inttypes.h>
44#include <arpa/inet.h>
45#include <sys/stat.h>
46#include <fcntl.h>
47#include <sys/time.h>
48#include <sys/resource.h>
49
50#include "ae.h" /* Event driven programming library */
51#include "sds.h" /* Dynamic safe strings */
52#include "anet.h" /* Networking the easy way */
53#include "dict.h" /* Hash tables */
54#include "adlist.h" /* Linked lists */
55#include "zmalloc.h" /* total memory usage aware version of malloc/free */
56
57/* Error codes */
58#define REDIS_OK 0
59#define REDIS_ERR -1
60
61/* Static server configuration */
62#define REDIS_SERVERPORT 6379 /* TCP port */
63#define REDIS_MAXIDLETIME (60*5) /* default client timeout */
64#define REDIS_QUERYBUF_LEN 1024
65#define REDIS_LOADBUF_LEN 1024
66#define REDIS_MAX_ARGS 16
67#define REDIS_DEFAULT_DBNUM 16
68#define REDIS_CONFIGLINE_MAX 1024
69#define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */
70#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */
71
72/* Hash table parameters */
73#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
74#define REDIS_HT_MINSLOTS 16384 /* Never resize the HT under this */
75
76/* Command flags */
77#define REDIS_CMD_BULK 1
78#define REDIS_CMD_INLINE 2
79
80/* Object types */
81#define REDIS_STRING 0
82#define REDIS_LIST 1
83#define REDIS_SET 2
84#define REDIS_HASH 3
85#define REDIS_SELECTDB 254
86#define REDIS_EOF 255
87
88/* Client flags */
89#define REDIS_CLOSE 1 /* This client connection should be closed ASAP */
90#define REDIS_SLAVE 2 /* This client is a slave server */
91#define REDIS_MASTER 4 /* This client is a master server */
92
93/* Server replication state */
94#define REDIS_REPL_NONE 0 /* No active replication */
95#define REDIS_REPL_CONNECT 1 /* Must connect to master */
96#define REDIS_REPL_CONNECTED 2 /* Connected to master */
97
98/* List related stuff */
99#define REDIS_HEAD 0
100#define REDIS_TAIL 1
101
102/* Sort operations */
103#define REDIS_SORT_GET 0
104#define REDIS_SORT_DEL 1
105#define REDIS_SORT_INCR 2
106#define REDIS_SORT_DECR 3
107#define REDIS_SORT_ASC 4
108#define REDIS_SORT_DESC 5
109#define REDIS_SORTKEY_MAX 1024
110
111/* Log levels */
112#define REDIS_DEBUG 0
113#define REDIS_NOTICE 1
114#define REDIS_WARNING 2
115
116/* Anti-warning macro... */
117#define REDIS_NOTUSED(V) ((void) V)
118
119/*================================= Data types ============================== */
120
121/* A redis object, that is a type able to hold a string / list / set */
122typedef struct redisObject {
123 int type;
124 void *ptr;
125 int refcount;
126} robj;
127
128/* With multiplexing we need to take per-clinet state.
129 * Clients are taken in a liked list. */
130typedef struct redisClient {
131 int fd;
132 dict *dict;
133 int dictid;
134 sds querybuf;
135 robj *argv[REDIS_MAX_ARGS];
136 int argc;
137 int bulklen; /* bulk read len. -1 if not in bulk read mode */
138 list *reply;
139 int sentlen;
140 time_t lastinteraction; /* time of the last interaction, used for timeout */
141 int flags; /* REDIS_CLOSE | REDIS_SLAVE */
142 int slaveseldb; /* slave selected db, if this client is a slave */
143} redisClient;
144
145struct saveparam {
146 time_t seconds;
147 int changes;
148};
149
150/* Global server state structure */
151struct redisServer {
152 int port;
153 int fd;
154 dict **dict;
155 long long dirty; /* changes to DB from the last save */
156 list *clients;
157 list *slaves;
158 char neterr[ANET_ERR_LEN];
159 aeEventLoop *el;
160 int cronloops; /* number of times the cron function run */
161 list *objfreelist; /* A list of freed objects to avoid malloc() */
162 time_t lastsave; /* Unix time of last save succeeede */
163 int usedmemory; /* Used memory in megabytes */
164 /* Fields used only for stats */
165 time_t stat_starttime; /* server start time */
166 long long stat_numcommands; /* number of processed commands */
167 long long stat_numconnections; /* number of connections received */
168 /* Configuration */
169 int verbosity;
170 int glueoutputbuf;
171 int maxidletime;
172 int dbnum;
173 int daemonize;
174 int bgsaveinprogress;
175 struct saveparam *saveparams;
176 int saveparamslen;
177 char *logfile;
178 char *bindaddr;
179 char *dbfilename;
180 /* Replication related */
181 int isslave;
182 char *masterhost;
183 int masterport;
184 redisClient *master;
185 int replstate;
186 /* Sort parameters - qsort_r() is only available under BSD so we
187 * have to take this state global, in order to pass it to sortCompare() */
188 int sort_desc;
189 int sort_alpha;
190 int sort_bypattern;
191};
192
193typedef void redisCommandProc(redisClient *c);
194struct redisCommand {
195 char *name;
196 redisCommandProc *proc;
197 int arity;
198 int flags;
199};
200
201typedef struct _redisSortObject {
202 robj *obj;
203 union {
204 double score;
205 robj *cmpobj;
206 } u;
207} redisSortObject;
208
209typedef struct _redisSortOperation {
210 int type;
211 robj *pattern;
212} redisSortOperation;
213
214struct sharedObjectsStruct {
215 robj *crlf, *ok, *err, *zerobulk, *nil, *zero, *one, *pong, *space,
216 *minus1, *minus2, *minus3, *minus4,
217 *wrongtypeerr, *nokeyerr, *wrongtypeerrbulk, *nokeyerrbulk,
218 *syntaxerr, *syntaxerrbulk,
219 *select0, *select1, *select2, *select3, *select4,
220 *select5, *select6, *select7, *select8, *select9;
221} shared;
222
223/*================================ Prototypes =============================== */
224
225static void freeStringObject(robj *o);
226static void freeListObject(robj *o);
227static void freeSetObject(robj *o);
228static void decrRefCount(void *o);
229static robj *createObject(int type, void *ptr);
230static void freeClient(redisClient *c);
231static int loadDb(char *filename);
232static void addReply(redisClient *c, robj *obj);
233static void addReplySds(redisClient *c, sds s);
234static void incrRefCount(robj *o);
235static int saveDbBackground(char *filename);
236static robj *createStringObject(char *ptr, size_t len);
237static void replicationFeedSlaves(struct redisCommand *cmd, int dictid, robj **argv, int argc);
238static int syncWithMaster(void);
239
240static void pingCommand(redisClient *c);
241static void echoCommand(redisClient *c);
242static void setCommand(redisClient *c);
243static void setnxCommand(redisClient *c);
244static void getCommand(redisClient *c);
245static void delCommand(redisClient *c);
246static void existsCommand(redisClient *c);
247static void incrCommand(redisClient *c);
248static void decrCommand(redisClient *c);
249static void incrbyCommand(redisClient *c);
250static void decrbyCommand(redisClient *c);
251static void selectCommand(redisClient *c);
252static void randomkeyCommand(redisClient *c);
253static void keysCommand(redisClient *c);
254static void dbsizeCommand(redisClient *c);
255static void lastsaveCommand(redisClient *c);
256static void saveCommand(redisClient *c);
257static void bgsaveCommand(redisClient *c);
258static void shutdownCommand(redisClient *c);
259static void moveCommand(redisClient *c);
260static void renameCommand(redisClient *c);
261static void renamenxCommand(redisClient *c);
262static void lpushCommand(redisClient *c);
263static void rpushCommand(redisClient *c);
264static void lpopCommand(redisClient *c);
265static void rpopCommand(redisClient *c);
266static void llenCommand(redisClient *c);
267static void lindexCommand(redisClient *c);
268static void lrangeCommand(redisClient *c);
269static void ltrimCommand(redisClient *c);
270static void typeCommand(redisClient *c);
271static void lsetCommand(redisClient *c);
272static void saddCommand(redisClient *c);
273static void sremCommand(redisClient *c);
274static void sismemberCommand(redisClient *c);
275static void scardCommand(redisClient *c);
276static void sinterCommand(redisClient *c);
277static void sinterstoreCommand(redisClient *c);
278static void syncCommand(redisClient *c);
279static void flushdbCommand(redisClient *c);
280static void flushallCommand(redisClient *c);
281static void sortCommand(redisClient *c);
282static void lremCommand(redisClient *c);
283static void infoCommand(redisClient *c);
70003d28 284static void mgetCommand(redisClient *c);
ed9b544e 285
286/*================================= Globals ================================= */
287
288/* Global vars */
289static struct redisServer server; /* server global state */
290static struct redisCommand cmdTable[] = {
291 {"get",getCommand,2,REDIS_CMD_INLINE},
292 {"set",setCommand,3,REDIS_CMD_BULK},
293 {"setnx",setnxCommand,3,REDIS_CMD_BULK},
294 {"del",delCommand,2,REDIS_CMD_INLINE},
295 {"exists",existsCommand,2,REDIS_CMD_INLINE},
296 {"incr",incrCommand,2,REDIS_CMD_INLINE},
297 {"decr",decrCommand,2,REDIS_CMD_INLINE},
70003d28 298 {"mget",mgetCommand,-2,REDIS_CMD_INLINE},
ed9b544e 299 {"rpush",rpushCommand,3,REDIS_CMD_BULK},
300 {"lpush",lpushCommand,3,REDIS_CMD_BULK},
301 {"rpop",rpopCommand,2,REDIS_CMD_INLINE},
302 {"lpop",lpopCommand,2,REDIS_CMD_INLINE},
303 {"llen",llenCommand,2,REDIS_CMD_INLINE},
304 {"lindex",lindexCommand,3,REDIS_CMD_INLINE},
305 {"lset",lsetCommand,4,REDIS_CMD_BULK},
306 {"lrange",lrangeCommand,4,REDIS_CMD_INLINE},
307 {"ltrim",ltrimCommand,4,REDIS_CMD_INLINE},
308 {"lrem",lremCommand,4,REDIS_CMD_BULK},
309 {"sadd",saddCommand,3,REDIS_CMD_BULK},
310 {"srem",sremCommand,3,REDIS_CMD_BULK},
311 {"sismember",sismemberCommand,3,REDIS_CMD_BULK},
312 {"scard",scardCommand,2,REDIS_CMD_INLINE},
313 {"sinter",sinterCommand,-2,REDIS_CMD_INLINE},
314 {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE},
315 {"smembers",sinterCommand,2,REDIS_CMD_INLINE},
316 {"incrby",incrbyCommand,3,REDIS_CMD_INLINE},
317 {"decrby",decrbyCommand,3,REDIS_CMD_INLINE},
318 {"randomkey",randomkeyCommand,1,REDIS_CMD_INLINE},
319 {"select",selectCommand,2,REDIS_CMD_INLINE},
320 {"move",moveCommand,3,REDIS_CMD_INLINE},
321 {"rename",renameCommand,3,REDIS_CMD_INLINE},
322 {"renamenx",renamenxCommand,3,REDIS_CMD_INLINE},
323 {"keys",keysCommand,2,REDIS_CMD_INLINE},
324 {"dbsize",dbsizeCommand,1,REDIS_CMD_INLINE},
325 {"ping",pingCommand,1,REDIS_CMD_INLINE},
326 {"echo",echoCommand,2,REDIS_CMD_BULK},
327 {"save",saveCommand,1,REDIS_CMD_INLINE},
328 {"bgsave",bgsaveCommand,1,REDIS_CMD_INLINE},
329 {"shutdown",shutdownCommand,1,REDIS_CMD_INLINE},
330 {"lastsave",lastsaveCommand,1,REDIS_CMD_INLINE},
331 {"type",typeCommand,2,REDIS_CMD_INLINE},
332 {"sync",syncCommand,1,REDIS_CMD_INLINE},
333 {"flushdb",flushdbCommand,1,REDIS_CMD_INLINE},
334 {"flushall",flushallCommand,1,REDIS_CMD_INLINE},
335 {"sort",sortCommand,-2,REDIS_CMD_INLINE},
336 {"info",infoCommand,1,REDIS_CMD_INLINE},
337 {NULL,NULL,0,0}
338};
339
340/*============================ Utility functions ============================ */
341
342/* Glob-style pattern matching. */
343int stringmatchlen(const char *pattern, int patternLen,
344 const char *string, int stringLen, int nocase)
345{
346 while(patternLen) {
347 switch(pattern[0]) {
348 case '*':
349 while (pattern[1] == '*') {
350 pattern++;
351 patternLen--;
352 }
353 if (patternLen == 1)
354 return 1; /* match */
355 while(stringLen) {
356 if (stringmatchlen(pattern+1, patternLen-1,
357 string, stringLen, nocase))
358 return 1; /* match */
359 string++;
360 stringLen--;
361 }
362 return 0; /* no match */
363 break;
364 case '?':
365 if (stringLen == 0)
366 return 0; /* no match */
367 string++;
368 stringLen--;
369 break;
370 case '[':
371 {
372 int not, match;
373
374 pattern++;
375 patternLen--;
376 not = pattern[0] == '^';
377 if (not) {
378 pattern++;
379 patternLen--;
380 }
381 match = 0;
382 while(1) {
383 if (pattern[0] == '\\') {
384 pattern++;
385 patternLen--;
386 if (pattern[0] == string[0])
387 match = 1;
388 } else if (pattern[0] == ']') {
389 break;
390 } else if (patternLen == 0) {
391 pattern--;
392 patternLen++;
393 break;
394 } else if (pattern[1] == '-' && patternLen >= 3) {
395 int start = pattern[0];
396 int end = pattern[2];
397 int c = string[0];
398 if (start > end) {
399 int t = start;
400 start = end;
401 end = t;
402 }
403 if (nocase) {
404 start = tolower(start);
405 end = tolower(end);
406 c = tolower(c);
407 }
408 pattern += 2;
409 patternLen -= 2;
410 if (c >= start && c <= end)
411 match = 1;
412 } else {
413 if (!nocase) {
414 if (pattern[0] == string[0])
415 match = 1;
416 } else {
417 if (tolower((int)pattern[0]) == tolower((int)string[0]))
418 match = 1;
419 }
420 }
421 pattern++;
422 patternLen--;
423 }
424 if (not)
425 match = !match;
426 if (!match)
427 return 0; /* no match */
428 string++;
429 stringLen--;
430 break;
431 }
432 case '\\':
433 if (patternLen >= 2) {
434 pattern++;
435 patternLen--;
436 }
437 /* fall through */
438 default:
439 if (!nocase) {
440 if (pattern[0] != string[0])
441 return 0; /* no match */
442 } else {
443 if (tolower((int)pattern[0]) != tolower((int)string[0]))
444 return 0; /* no match */
445 }
446 string++;
447 stringLen--;
448 break;
449 }
450 pattern++;
451 patternLen--;
452 if (stringLen == 0) {
453 while(*pattern == '*') {
454 pattern++;
455 patternLen--;
456 }
457 break;
458 }
459 }
460 if (patternLen == 0 && stringLen == 0)
461 return 1;
462 return 0;
463}
464
465void redisLog(int level, const char *fmt, ...)
466{
467 va_list ap;
468 FILE *fp;
469
470 fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a");
471 if (!fp) return;
472
473 va_start(ap, fmt);
474 if (level >= server.verbosity) {
475 char *c = ".-*";
476 fprintf(fp,"%c ",c[level]);
477 vfprintf(fp, fmt, ap);
478 fprintf(fp,"\n");
479 fflush(fp);
480 }
481 va_end(ap);
482
483 if (server.logfile) fclose(fp);
484}
485
486/*====================== Hash table type implementation ==================== */
487
488/* This is an hash table type that uses the SDS dynamic strings libary as
489 * keys and radis objects as values (objects can hold SDS strings,
490 * lists, sets). */
491
492static int sdsDictKeyCompare(void *privdata, const void *key1,
493 const void *key2)
494{
495 int l1,l2;
496 DICT_NOTUSED(privdata);
497
498 l1 = sdslen((sds)key1);
499 l2 = sdslen((sds)key2);
500 if (l1 != l2) return 0;
501 return memcmp(key1, key2, l1) == 0;
502}
503
504static void dictRedisObjectDestructor(void *privdata, void *val)
505{
506 DICT_NOTUSED(privdata);
507
508 decrRefCount(val);
509}
510
511static int dictSdsKeyCompare(void *privdata, const void *key1,
512 const void *key2)
513{
514 const robj *o1 = key1, *o2 = key2;
515 return sdsDictKeyCompare(privdata,o1->ptr,o2->ptr);
516}
517
518static unsigned int dictSdsHash(const void *key) {
519 const robj *o = key;
520 return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
521}
522
523static dictType setDictType = {
524 dictSdsHash, /* hash function */
525 NULL, /* key dup */
526 NULL, /* val dup */
527 dictSdsKeyCompare, /* key compare */
528 dictRedisObjectDestructor, /* key destructor */
529 NULL /* val destructor */
530};
531
532static dictType hashDictType = {
533 dictSdsHash, /* hash function */
534 NULL, /* key dup */
535 NULL, /* val dup */
536 dictSdsKeyCompare, /* key compare */
537 dictRedisObjectDestructor, /* key destructor */
538 dictRedisObjectDestructor /* val destructor */
539};
540
541/* ========================= Random utility functions ======================= */
542
543/* Redis generally does not try to recover from out of memory conditions
544 * when allocating objects or strings, it is not clear if it will be possible
545 * to report this condition to the client since the networking layer itself
546 * is based on heap allocation for send buffers, so we simply abort.
547 * At least the code will be simpler to read... */
548static void oom(const char *msg) {
549 fprintf(stderr, "%s: Out of memory\n",msg);
550 fflush(stderr);
551 sleep(1);
552 abort();
553}
554
555/* ====================== Redis server networking stuff ===================== */
556void closeTimedoutClients(void) {
557 redisClient *c;
558 listIter *li;
559 listNode *ln;
560 time_t now = time(NULL);
561
562 li = listGetIterator(server.clients,AL_START_HEAD);
563 if (!li) return;
564 while ((ln = listNextElement(li)) != NULL) {
565 c = listNodeValue(ln);
566 if (!(c->flags & REDIS_SLAVE) && /* no timeout for slaves */
567 (now - c->lastinteraction > server.maxidletime)) {
568 redisLog(REDIS_DEBUG,"Closing idle client");
569 freeClient(c);
570 }
571 }
572 listReleaseIterator(li);
573}
574
575int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
576 int j, size, used, loops = server.cronloops++;
577 REDIS_NOTUSED(eventLoop);
578 REDIS_NOTUSED(id);
579 REDIS_NOTUSED(clientData);
580
581 /* Update the global state with the amount of used memory */
582 server.usedmemory = zmalloc_used_memory();
583
584 /* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
585 * we resize the hash table to save memory */
586 for (j = 0; j < server.dbnum; j++) {
587 size = dictGetHashTableSize(server.dict[j]);
588 used = dictGetHashTableUsed(server.dict[j]);
589 if (!(loops % 5) && used > 0) {
590 redisLog(REDIS_DEBUG,"DB %d: %d keys in %d slots HT.",j,used,size);
591 // dictPrintStats(server.dict);
592 }
593 if (size && used && size > REDIS_HT_MINSLOTS &&
594 (used*100/size < REDIS_HT_MINFILL)) {
595 redisLog(REDIS_NOTICE,"The hash table %d is too sparse, resize it...",j);
596 dictResize(server.dict[j]);
597 redisLog(REDIS_NOTICE,"Hash table %d resized.",j);
598 }
599 }
600
601 /* Show information about connected clients */
602 if (!(loops % 5)) {
603 redisLog(REDIS_DEBUG,"%d clients connected (%d slaves), %d bytes in use",
604 listLength(server.clients)-listLength(server.slaves),
605 listLength(server.slaves),
606 server.usedmemory);
607 }
608
609 /* Close connections of timedout clients */
610 if (!(loops % 10))
611 closeTimedoutClients();
612
613 /* Check if a background saving in progress terminated */
614 if (server.bgsaveinprogress) {
615 int statloc;
616 if (wait4(-1,&statloc,WNOHANG,NULL)) {
617 int exitcode = WEXITSTATUS(statloc);
618 if (exitcode == 0) {
619 redisLog(REDIS_NOTICE,
620 "Background saving terminated with success");
621 server.dirty = 0;
622 server.lastsave = time(NULL);
623 } else {
624 redisLog(REDIS_WARNING,
625 "Background saving error");
626 }
627 server.bgsaveinprogress = 0;
628 }
629 } else {
630 /* If there is not a background saving in progress check if
631 * we have to save now */
632 time_t now = time(NULL);
633 for (j = 0; j < server.saveparamslen; j++) {
634 struct saveparam *sp = server.saveparams+j;
635
636 if (server.dirty >= sp->changes &&
637 now-server.lastsave > sp->seconds) {
638 redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
639 sp->changes, sp->seconds);
640 saveDbBackground(server.dbfilename);
641 break;
642 }
643 }
644 }
645 /* Check if we should connect to a MASTER */
646 if (server.replstate == REDIS_REPL_CONNECT) {
647 redisLog(REDIS_NOTICE,"Connecting to MASTER...");
648 if (syncWithMaster() == REDIS_OK) {
649 redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync succeeded");
650 }
651 }
652 return 1000;
653}
654
655static void createSharedObjects(void) {
656 shared.crlf = createObject(REDIS_STRING,sdsnew("\r\n"));
657 shared.ok = createObject(REDIS_STRING,sdsnew("+OK\r\n"));
658 shared.err = createObject(REDIS_STRING,sdsnew("-ERR\r\n"));
659 shared.zerobulk = createObject(REDIS_STRING,sdsnew("0\r\n\r\n"));
660 shared.nil = createObject(REDIS_STRING,sdsnew("nil\r\n"));
661 shared.zero = createObject(REDIS_STRING,sdsnew("0\r\n"));
662 shared.one = createObject(REDIS_STRING,sdsnew("1\r\n"));
663 /* no such key */
664 shared.minus1 = createObject(REDIS_STRING,sdsnew("-1\r\n"));
665 /* operation against key holding a value of the wrong type */
666 shared.minus2 = createObject(REDIS_STRING,sdsnew("-2\r\n"));
667 /* src and dest objects are the same */
668 shared.minus3 = createObject(REDIS_STRING,sdsnew("-3\r\n"));
669 /* out of range argument */
670 shared.minus4 = createObject(REDIS_STRING,sdsnew("-4\r\n"));
671 shared.pong = createObject(REDIS_STRING,sdsnew("+PONG\r\n"));
672 shared.wrongtypeerr = createObject(REDIS_STRING,sdsnew(
673 "-ERR Operation against a key holding the wrong kind of value\r\n"));
674 shared.wrongtypeerrbulk = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%d\r\n%s",-sdslen(shared.wrongtypeerr->ptr)+2,shared.wrongtypeerr->ptr));
675 shared.nokeyerr = createObject(REDIS_STRING,sdsnew(
676 "-ERR no such key\r\n"));
677 shared.nokeyerrbulk = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%d\r\n%s",-sdslen(shared.nokeyerr->ptr)+2,shared.nokeyerr->ptr));
678 shared.syntaxerr = createObject(REDIS_STRING,sdsnew(
679 "-ERR syntax error\r\n"));
680 shared.syntaxerrbulk = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%d\r\n%s",-sdslen(shared.syntaxerr->ptr)+2,shared.syntaxerr->ptr));
681 shared.space = createObject(REDIS_STRING,sdsnew(" "));
682 shared.select0 = createStringObject("select 0\r\n",10);
683 shared.select1 = createStringObject("select 1\r\n",10);
684 shared.select2 = createStringObject("select 2\r\n",10);
685 shared.select3 = createStringObject("select 3\r\n",10);
686 shared.select4 = createStringObject("select 4\r\n",10);
687 shared.select5 = createStringObject("select 5\r\n",10);
688 shared.select6 = createStringObject("select 6\r\n",10);
689 shared.select7 = createStringObject("select 7\r\n",10);
690 shared.select8 = createStringObject("select 8\r\n",10);
691 shared.select9 = createStringObject("select 9\r\n",10);
692}
693
694static void appendServerSaveParams(time_t seconds, int changes) {
695 server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
696 if (server.saveparams == NULL) oom("appendServerSaveParams");
697 server.saveparams[server.saveparamslen].seconds = seconds;
698 server.saveparams[server.saveparamslen].changes = changes;
699 server.saveparamslen++;
700}
701
702static void ResetServerSaveParams() {
703 zfree(server.saveparams);
704 server.saveparams = NULL;
705 server.saveparamslen = 0;
706}
707
708static void initServerConfig() {
709 server.dbnum = REDIS_DEFAULT_DBNUM;
710 server.port = REDIS_SERVERPORT;
711 server.verbosity = REDIS_DEBUG;
712 server.maxidletime = REDIS_MAXIDLETIME;
713 server.saveparams = NULL;
714 server.logfile = NULL; /* NULL = log on standard output */
715 server.bindaddr = NULL;
716 server.glueoutputbuf = 1;
717 server.daemonize = 0;
718 server.dbfilename = "dump.rdb";
719 ResetServerSaveParams();
720
721 appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
722 appendServerSaveParams(300,100); /* save after 5 minutes and 100 changes */
723 appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */
724 /* Replication related */
725 server.isslave = 0;
726 server.masterhost = NULL;
727 server.masterport = 6379;
728 server.master = NULL;
729 server.replstate = REDIS_REPL_NONE;
730}
731
732static void initServer() {
733 int j;
734
735 signal(SIGHUP, SIG_IGN);
736 signal(SIGPIPE, SIG_IGN);
737
738 server.clients = listCreate();
739 server.slaves = listCreate();
740 server.objfreelist = listCreate();
741 createSharedObjects();
742 server.el = aeCreateEventLoop();
743 server.dict = zmalloc(sizeof(dict*)*server.dbnum);
744 if (!server.dict || !server.clients || !server.slaves || !server.el || !server.objfreelist)
745 oom("server initialization"); /* Fatal OOM */
746 server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
747 if (server.fd == -1) {
748 redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr);
749 exit(1);
750 }
751 for (j = 0; j < server.dbnum; j++) {
752 server.dict[j] = dictCreate(&hashDictType,NULL);
753 if (!server.dict[j])
754 oom("dictCreate"); /* Fatal OOM */
755 }
756 server.cronloops = 0;
757 server.bgsaveinprogress = 0;
758 server.lastsave = time(NULL);
759 server.dirty = 0;
760 server.usedmemory = 0;
761 server.stat_numcommands = 0;
762 server.stat_numconnections = 0;
763 server.stat_starttime = time(NULL);
764 aeCreateTimeEvent(server.el, 1000, serverCron, NULL, NULL);
765}
766
767/* Empty the whole database */
768static void emptyDb() {
769 int j;
770
771 for (j = 0; j < server.dbnum; j++)
772 dictEmpty(server.dict[j]);
773}
774
775/* I agree, this is a very rudimental way to load a configuration...
776 will improve later if the config gets more complex */
777static void loadServerConfig(char *filename) {
778 FILE *fp = fopen(filename,"r");
779 char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
780 int linenum = 0;
781 sds line = NULL;
782
783 if (!fp) {
784 redisLog(REDIS_WARNING,"Fatal error, can't open config file");
785 exit(1);
786 }
787 while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) {
788 sds *argv;
789 int argc, j;
790
791 linenum++;
792 line = sdsnew(buf);
793 line = sdstrim(line," \t\r\n");
794
795 /* Skip comments and blank lines*/
796 if (line[0] == '#' || line[0] == '\0') {
797 sdsfree(line);
798 continue;
799 }
800
801 /* Split into arguments */
802 argv = sdssplitlen(line,sdslen(line)," ",1,&argc);
803 sdstolower(argv[0]);
804
805 /* Execute config directives */
806 if (!strcmp(argv[0],"timeout") && argc == 2) {
807 server.maxidletime = atoi(argv[1]);
808 if (server.maxidletime < 1) {
809 err = "Invalid timeout value"; goto loaderr;
810 }
811 } else if (!strcmp(argv[0],"port") && argc == 2) {
812 server.port = atoi(argv[1]);
813 if (server.port < 1 || server.port > 65535) {
814 err = "Invalid port"; goto loaderr;
815 }
816 } else if (!strcmp(argv[0],"bind") && argc == 2) {
817 server.bindaddr = zstrdup(argv[1]);
818 } else if (!strcmp(argv[0],"save") && argc == 3) {
819 int seconds = atoi(argv[1]);
820 int changes = atoi(argv[2]);
821 if (seconds < 1 || changes < 0) {
822 err = "Invalid save parameters"; goto loaderr;
823 }
824 appendServerSaveParams(seconds,changes);
825 } else if (!strcmp(argv[0],"dir") && argc == 2) {
826 if (chdir(argv[1]) == -1) {
827 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s",
828 argv[1], strerror(errno));
829 exit(1);
830 }
831 } else if (!strcmp(argv[0],"loglevel") && argc == 2) {
832 if (!strcmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
833 else if (!strcmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
834 else if (!strcmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
835 else {
836 err = "Invalid log level. Must be one of debug, notice, warning";
837 goto loaderr;
838 }
839 } else if (!strcmp(argv[0],"logfile") && argc == 2) {
840 FILE *fp;
841
842 server.logfile = zstrdup(argv[1]);
843 if (!strcmp(server.logfile,"stdout")) {
844 zfree(server.logfile);
845 server.logfile = NULL;
846 }
847 if (server.logfile) {
848 /* Test if we are able to open the file. The server will not
849 * be able to abort just for this problem later... */
850 fp = fopen(server.logfile,"a");
851 if (fp == NULL) {
852 err = sdscatprintf(sdsempty(),
853 "Can't open the log file: %s", strerror(errno));
854 goto loaderr;
855 }
856 fclose(fp);
857 }
858 } else if (!strcmp(argv[0],"databases") && argc == 2) {
859 server.dbnum = atoi(argv[1]);
860 if (server.dbnum < 1) {
861 err = "Invalid number of databases"; goto loaderr;
862 }
863 } else if (!strcmp(argv[0],"slaveof") && argc == 3) {
864 server.masterhost = sdsnew(argv[1]);
865 server.masterport = atoi(argv[2]);
866 server.replstate = REDIS_REPL_CONNECT;
867 } else if (!strcmp(argv[0],"glueoutputbuf") && argc == 2) {
868 sdstolower(argv[1]);
869 if (!strcmp(argv[1],"yes")) server.glueoutputbuf = 1;
870 else if (!strcmp(argv[1],"no")) server.glueoutputbuf = 0;
871 else {
872 err = "argument must be 'yes' or 'no'"; goto loaderr;
873 }
874 } else if (!strcmp(argv[0],"daemonize") && argc == 2) {
875 sdstolower(argv[1]);
876 if (!strcmp(argv[1],"yes")) server.daemonize = 1;
877 else if (!strcmp(argv[1],"no")) server.daemonize = 0;
878 else {
879 err = "argument must be 'yes' or 'no'"; goto loaderr;
880 }
881 } else {
882 err = "Bad directive or wrong number of arguments"; goto loaderr;
883 }
884 for (j = 0; j < argc; j++)
885 sdsfree(argv[j]);
886 zfree(argv);
887 sdsfree(line);
888 }
889 fclose(fp);
890 return;
891
892loaderr:
893 fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
894 fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
895 fprintf(stderr, ">>> '%s'\n", line);
896 fprintf(stderr, "%s\n", err);
897 exit(1);
898}
899
900static void freeClientArgv(redisClient *c) {
901 int j;
902
903 for (j = 0; j < c->argc; j++)
904 decrRefCount(c->argv[j]);
905 c->argc = 0;
906}
907
908static void freeClient(redisClient *c) {
909 listNode *ln;
910
911 aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
912 aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
913 sdsfree(c->querybuf);
914 listRelease(c->reply);
915 freeClientArgv(c);
916 close(c->fd);
917 ln = listSearchKey(server.clients,c);
918 assert(ln != NULL);
919 listDelNode(server.clients,ln);
920 if (c->flags & REDIS_SLAVE) {
921 ln = listSearchKey(server.slaves,c);
922 assert(ln != NULL);
923 listDelNode(server.slaves,ln);
924 }
925 if (c->flags & REDIS_MASTER) {
926 server.master = NULL;
927 server.replstate = REDIS_REPL_CONNECT;
928 }
929 zfree(c);
930}
931
932static void glueReplyBuffersIfNeeded(redisClient *c) {
933 int totlen = 0;
934 listNode *ln = c->reply->head, *next;
935 robj *o;
936
937 while(ln) {
938 o = ln->value;
939 totlen += sdslen(o->ptr);
940 ln = ln->next;
941 /* This optimization makes more sense if we don't have to copy
942 * too much data */
943 if (totlen > 1024) return;
944 }
945 if (totlen > 0) {
946 char buf[1024];
947 int copylen = 0;
948
949 ln = c->reply->head;
950 while(ln) {
951 next = ln->next;
952 o = ln->value;
953 memcpy(buf+copylen,o->ptr,sdslen(o->ptr));
954 copylen += sdslen(o->ptr);
955 listDelNode(c->reply,ln);
956 ln = next;
957 }
958 /* Now the output buffer is empty, add the new single element */
959 addReplySds(c,sdsnewlen(buf,totlen));
960 }
961}
962
963static void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
964 redisClient *c = privdata;
965 int nwritten = 0, totwritten = 0, objlen;
966 robj *o;
967 REDIS_NOTUSED(el);
968 REDIS_NOTUSED(mask);
969
970 if (server.glueoutputbuf && listLength(c->reply) > 1)
971 glueReplyBuffersIfNeeded(c);
972 while(listLength(c->reply)) {
973 o = listNodeValue(listFirst(c->reply));
974 objlen = sdslen(o->ptr);
975
976 if (objlen == 0) {
977 listDelNode(c->reply,listFirst(c->reply));
978 continue;
979 }
980
981 if (c->flags & REDIS_MASTER) {
982 nwritten = objlen - c->sentlen;
983 } else {
984 nwritten = write(fd, o->ptr+c->sentlen, objlen - c->sentlen);
985 if (nwritten <= 0) break;
986 }
987 c->sentlen += nwritten;
988 totwritten += nwritten;
989 /* If we fully sent the object on head go to the next one */
990 if (c->sentlen == objlen) {
991 listDelNode(c->reply,listFirst(c->reply));
992 c->sentlen = 0;
993 }
994 }
995 if (nwritten == -1) {
996 if (errno == EAGAIN) {
997 nwritten = 0;
998 } else {
999 redisLog(REDIS_DEBUG,
1000 "Error writing to client: %s", strerror(errno));
1001 freeClient(c);
1002 return;
1003 }
1004 }
1005 if (totwritten > 0) c->lastinteraction = time(NULL);
1006 if (listLength(c->reply) == 0) {
1007 c->sentlen = 0;
1008 aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
1009 }
1010}
1011
1012static struct redisCommand *lookupCommand(char *name) {
1013 int j = 0;
1014 while(cmdTable[j].name != NULL) {
1015 if (!strcmp(name,cmdTable[j].name)) return &cmdTable[j];
1016 j++;
1017 }
1018 return NULL;
1019}
1020
1021/* resetClient prepare the client to process the next command */
1022static void resetClient(redisClient *c) {
1023 freeClientArgv(c);
1024 c->bulklen = -1;
1025}
1026
1027/* If this function gets called we already read a whole
1028 * command, argments are in the client argv/argc fields.
1029 * processCommand() execute the command or prepare the
1030 * server for a bulk read from the client.
1031 *
1032 * If 1 is returned the client is still alive and valid and
1033 * and other operations can be performed by the caller. Otherwise
1034 * if 0 is returned the client was destroied (i.e. after QUIT). */
1035static int processCommand(redisClient *c) {
1036 struct redisCommand *cmd;
1037 long long dirty;
1038
1039 sdstolower(c->argv[0]->ptr);
1040 /* The QUIT command is handled as a special case. Normal command
1041 * procs are unable to close the client connection safely */
1042 if (!strcmp(c->argv[0]->ptr,"quit")) {
1043 freeClient(c);
1044 return 0;
1045 }
1046 cmd = lookupCommand(c->argv[0]->ptr);
1047 if (!cmd) {
1048 addReplySds(c,sdsnew("-ERR unknown command\r\n"));
1049 resetClient(c);
1050 return 1;
1051 } else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
1052 (c->argc < -cmd->arity)) {
1053 addReplySds(c,sdsnew("-ERR wrong number of arguments\r\n"));
1054 resetClient(c);
1055 return 1;
1056 } else if (cmd->flags & REDIS_CMD_BULK && c->bulklen == -1) {
1057 int bulklen = atoi(c->argv[c->argc-1]->ptr);
1058
1059 decrRefCount(c->argv[c->argc-1]);
1060 if (bulklen < 0 || bulklen > 1024*1024*1024) {
1061 c->argc--;
1062 addReplySds(c,sdsnew("-ERR invalid bulk write count\r\n"));
1063 resetClient(c);
1064 return 1;
1065 }
1066 c->argc--;
1067 c->bulklen = bulklen+2; /* add two bytes for CR+LF */
1068 /* It is possible that the bulk read is already in the
1069 * buffer. Check this condition and handle it accordingly */
1070 if ((signed)sdslen(c->querybuf) >= c->bulklen) {
1071 c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2);
1072 c->argc++;
1073 c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
1074 } else {
1075 return 1;
1076 }
1077 }
1078 /* Exec the command */
1079 dirty = server.dirty;
1080 cmd->proc(c);
1081 if (server.dirty-dirty != 0 && listLength(server.slaves))
1082 replicationFeedSlaves(cmd,c->dictid,c->argv,c->argc);
1083 server.stat_numcommands++;
1084
1085 /* Prepare the client for the next command */
1086 if (c->flags & REDIS_CLOSE) {
1087 freeClient(c);
1088 return 0;
1089 }
1090 resetClient(c);
1091 return 1;
1092}
1093
1094static void replicationFeedSlaves(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
1095 listNode *ln = server.slaves->head;
1096 robj *outv[REDIS_MAX_ARGS*4]; /* enough room for args, spaces, newlines */
1097 int outc = 0, j;
1098
1099 for (j = 0; j < argc; j++) {
1100 if (j != 0) outv[outc++] = shared.space;
1101 if ((cmd->flags & REDIS_CMD_BULK) && j == argc-1) {
1102 robj *lenobj;
1103
1104 lenobj = createObject(REDIS_STRING,
1105 sdscatprintf(sdsempty(),"%d\r\n",sdslen(argv[j]->ptr)));
1106 lenobj->refcount = 0;
1107 outv[outc++] = lenobj;
1108 }
1109 outv[outc++] = argv[j];
1110 }
1111 outv[outc++] = shared.crlf;
1112
1113 while(ln) {
1114 redisClient *slave = ln->value;
1115 if (slave->slaveseldb != dictid) {
1116 robj *selectcmd;
1117
1118 switch(dictid) {
1119 case 0: selectcmd = shared.select0; break;
1120 case 1: selectcmd = shared.select1; break;
1121 case 2: selectcmd = shared.select2; break;
1122 case 3: selectcmd = shared.select3; break;
1123 case 4: selectcmd = shared.select4; break;
1124 case 5: selectcmd = shared.select5; break;
1125 case 6: selectcmd = shared.select6; break;
1126 case 7: selectcmd = shared.select7; break;
1127 case 8: selectcmd = shared.select8; break;
1128 case 9: selectcmd = shared.select9; break;
1129 default:
1130 selectcmd = createObject(REDIS_STRING,
1131 sdscatprintf(sdsempty(),"select %d\r\n",dictid));
1132 selectcmd->refcount = 0;
1133 break;
1134 }
1135 addReply(slave,selectcmd);
1136 slave->slaveseldb = dictid;
1137 }
1138 for (j = 0; j < outc; j++) addReply(slave,outv[j]);
1139 ln = ln->next;
1140 }
1141}
1142
1143static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
1144 redisClient *c = (redisClient*) privdata;
1145 char buf[REDIS_QUERYBUF_LEN];
1146 int nread;
1147 REDIS_NOTUSED(el);
1148 REDIS_NOTUSED(mask);
1149
1150 nread = read(fd, buf, REDIS_QUERYBUF_LEN);
1151 if (nread == -1) {
1152 if (errno == EAGAIN) {
1153 nread = 0;
1154 } else {
1155 redisLog(REDIS_DEBUG, "Reading from client: %s",strerror(errno));
1156 freeClient(c);
1157 return;
1158 }
1159 } else if (nread == 0) {
1160 redisLog(REDIS_DEBUG, "Client closed connection");
1161 freeClient(c);
1162 return;
1163 }
1164 if (nread) {
1165 c->querybuf = sdscatlen(c->querybuf, buf, nread);
1166 c->lastinteraction = time(NULL);
1167 } else {
1168 return;
1169 }
1170
1171again:
1172 if (c->bulklen == -1) {
1173 /* Read the first line of the query */
1174 char *p = strchr(c->querybuf,'\n');
1175 size_t querylen;
1176 if (p) {
1177 sds query, *argv;
1178 int argc, j;
1179
1180 query = c->querybuf;
1181 c->querybuf = sdsempty();
1182 querylen = 1+(p-(query));
1183 if (sdslen(query) > querylen) {
1184 /* leave data after the first line of the query in the buffer */
1185 c->querybuf = sdscatlen(c->querybuf,query+querylen,sdslen(query)-querylen);
1186 }
1187 *p = '\0'; /* remove "\n" */
1188 if (*(p-1) == '\r') *(p-1) = '\0'; /* and "\r" if any */
1189 sdsupdatelen(query);
1190
1191 /* Now we can split the query in arguments */
1192 if (sdslen(query) == 0) {
1193 /* Ignore empty query */
1194 sdsfree(query);
1195 return;
1196 }
1197 argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
1198 sdsfree(query);
1199 if (argv == NULL) oom("sdssplitlen");
1200 for (j = 0; j < argc && j < REDIS_MAX_ARGS; j++) {
1201 if (sdslen(argv[j])) {
1202 c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
1203 c->argc++;
1204 } else {
1205 sdsfree(argv[j]);
1206 }
1207 }
1208 zfree(argv);
1209 /* Execute the command. If the client is still valid
1210 * after processCommand() return and there is something
1211 * on the query buffer try to process the next command. */
1212 if (processCommand(c) && sdslen(c->querybuf)) goto again;
1213 return;
1214 } else if (sdslen(c->querybuf) >= 1024) {
1215 redisLog(REDIS_DEBUG, "Client protocol error");
1216 freeClient(c);
1217 return;
1218 }
1219 } else {
1220 /* Bulk read handling. Note that if we are at this point
1221 the client already sent a command terminated with a newline,
1222 we are reading the bulk data that is actually the last
1223 argument of the command. */
1224 int qbl = sdslen(c->querybuf);
1225
1226 if (c->bulklen <= qbl) {
1227 /* Copy everything but the final CRLF as final argument */
1228 c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2);
1229 c->argc++;
1230 c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
1231 processCommand(c);
1232 return;
1233 }
1234 }
1235}
1236
1237static int selectDb(redisClient *c, int id) {
1238 if (id < 0 || id >= server.dbnum)
1239 return REDIS_ERR;
1240 c->dict = server.dict[id];
1241 c->dictid = id;
1242 return REDIS_OK;
1243}
1244
1245static redisClient *createClient(int fd) {
1246 redisClient *c = zmalloc(sizeof(*c));
1247
1248 anetNonBlock(NULL,fd);
1249 anetTcpNoDelay(NULL,fd);
1250 if (!c) return NULL;
1251 selectDb(c,0);
1252 c->fd = fd;
1253 c->querybuf = sdsempty();
1254 c->argc = 0;
1255 c->bulklen = -1;
1256 c->sentlen = 0;
1257 c->flags = 0;
1258 c->lastinteraction = time(NULL);
1259 if ((c->reply = listCreate()) == NULL) oom("listCreate");
1260 listSetFreeMethod(c->reply,decrRefCount);
1261 if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,
1262 readQueryFromClient, c, NULL) == AE_ERR) {
1263 freeClient(c);
1264 return NULL;
1265 }
1266 if (!listAddNodeTail(server.clients,c)) oom("listAddNodeTail");
1267 return c;
1268}
1269
1270static void addReply(redisClient *c, robj *obj) {
1271 if (listLength(c->reply) == 0 &&
1272 aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
1273 sendReplyToClient, c, NULL) == AE_ERR) return;
1274 if (!listAddNodeTail(c->reply,obj)) oom("listAddNodeTail");
1275 incrRefCount(obj);
1276}
1277
1278static void addReplySds(redisClient *c, sds s) {
1279 robj *o = createObject(REDIS_STRING,s);
1280 addReply(c,o);
1281 decrRefCount(o);
1282}
1283
1284static void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
1285 int cport, cfd;
1286 char cip[128];
1287 REDIS_NOTUSED(el);
1288 REDIS_NOTUSED(mask);
1289 REDIS_NOTUSED(privdata);
1290
1291 cfd = anetAccept(server.neterr, fd, cip, &cport);
1292 if (cfd == AE_ERR) {
1293 redisLog(REDIS_DEBUG,"Accepting client connection: %s", server.neterr);
1294 return;
1295 }
1296 redisLog(REDIS_DEBUG,"Accepted %s:%d", cip, cport);
1297 if (createClient(cfd) == NULL) {
1298 redisLog(REDIS_WARNING,"Error allocating resoures for the client");
1299 close(cfd); /* May be already closed, just ingore errors */
1300 return;
1301 }
1302 server.stat_numconnections++;
1303}
1304
1305/* ======================= Redis objects implementation ===================== */
1306
1307static robj *createObject(int type, void *ptr) {
1308 robj *o;
1309
1310 if (listLength(server.objfreelist)) {
1311 listNode *head = listFirst(server.objfreelist);
1312 o = listNodeValue(head);
1313 listDelNode(server.objfreelist,head);
1314 } else {
1315 o = zmalloc(sizeof(*o));
1316 }
1317 if (!o) oom("createObject");
1318 o->type = type;
1319 o->ptr = ptr;
1320 o->refcount = 1;
1321 return o;
1322}
1323
1324static robj *createStringObject(char *ptr, size_t len) {
1325 return createObject(REDIS_STRING,sdsnewlen(ptr,len));
1326}
1327
1328static robj *createListObject(void) {
1329 list *l = listCreate();
1330
1331 if (!l) oom("listCreate");
1332 listSetFreeMethod(l,decrRefCount);
1333 return createObject(REDIS_LIST,l);
1334}
1335
1336static robj *createSetObject(void) {
1337 dict *d = dictCreate(&setDictType,NULL);
1338 if (!d) oom("dictCreate");
1339 return createObject(REDIS_SET,d);
1340}
1341
1342#if 0
1343static robj *createHashObject(void) {
1344 dict *d = dictCreate(&hashDictType,NULL);
1345 if (!d) oom("dictCreate");
1346 return createObject(REDIS_SET,d);
1347}
1348#endif
1349
1350static void freeStringObject(robj *o) {
1351 sdsfree(o->ptr);
1352}
1353
1354static void freeListObject(robj *o) {
1355 listRelease((list*) o->ptr);
1356}
1357
1358static void freeSetObject(robj *o) {
1359 dictRelease((dict*) o->ptr);
1360}
1361
1362static void freeHashObject(robj *o) {
1363 dictRelease((dict*) o->ptr);
1364}
1365
1366static void incrRefCount(robj *o) {
1367 o->refcount++;
1368}
1369
1370static void decrRefCount(void *obj) {
1371 robj *o = obj;
1372 if (--(o->refcount) == 0) {
1373 switch(o->type) {
1374 case REDIS_STRING: freeStringObject(o); break;
1375 case REDIS_LIST: freeListObject(o); break;
1376 case REDIS_SET: freeSetObject(o); break;
1377 case REDIS_HASH: freeHashObject(o); break;
1378 default: assert(0 != 0); break;
1379 }
1380 if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
1381 !listAddNodeHead(server.objfreelist,o))
1382 zfree(o);
1383 }
1384}
1385
1386/*============================ DB saving/loading ============================ */
1387
1388/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
1389static int saveDb(char *filename) {
1390 dictIterator *di = NULL;
1391 dictEntry *de;
1392 uint32_t len;
1393 uint8_t type;
1394 FILE *fp;
1395 char tmpfile[256];
1396 int j;
1397
1398 snprintf(tmpfile,256,"temp-%d.%ld.rdb",(int)time(NULL),(long int)random());
1399 fp = fopen(tmpfile,"w");
1400 if (!fp) {
1401 redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno));
1402 return REDIS_ERR;
1403 }
1404 if (fwrite("REDIS0000",9,1,fp) == 0) goto werr;
1405 for (j = 0; j < server.dbnum; j++) {
1406 dict *d = server.dict[j];
1407 if (dictGetHashTableUsed(d) == 0) continue;
1408 di = dictGetIterator(d);
1409 if (!di) {
1410 fclose(fp);
1411 return REDIS_ERR;
1412 }
1413
1414 /* Write the SELECT DB opcode */
1415 type = REDIS_SELECTDB;
1416 len = htonl(j);
1417 if (fwrite(&type,1,1,fp) == 0) goto werr;
1418 if (fwrite(&len,4,1,fp) == 0) goto werr;
1419
1420 /* Iterate this DB writing every entry */
1421 while((de = dictNext(di)) != NULL) {
1422 robj *key = dictGetEntryKey(de);
1423 robj *o = dictGetEntryVal(de);
1424
1425 type = o->type;
1426 len = htonl(sdslen(key->ptr));
1427 if (fwrite(&type,1,1,fp) == 0) goto werr;
1428 if (fwrite(&len,4,1,fp) == 0) goto werr;
1429 if (fwrite(key->ptr,sdslen(key->ptr),1,fp) == 0) goto werr;
1430 if (type == REDIS_STRING) {
1431 /* Save a string value */
1432 sds sval = o->ptr;
1433 len = htonl(sdslen(sval));
1434 if (fwrite(&len,4,1,fp) == 0) goto werr;
1435 if (sdslen(sval) &&
1436 fwrite(sval,sdslen(sval),1,fp) == 0) goto werr;
1437 } else if (type == REDIS_LIST) {
1438 /* Save a list value */
1439 list *list = o->ptr;
1440 listNode *ln = list->head;
1441
1442 len = htonl(listLength(list));
1443 if (fwrite(&len,4,1,fp) == 0) goto werr;
1444 while(ln) {
1445 robj *eleobj = listNodeValue(ln);
1446 len = htonl(sdslen(eleobj->ptr));
1447 if (fwrite(&len,4,1,fp) == 0) goto werr;
1448 if (sdslen(eleobj->ptr) && fwrite(eleobj->ptr,sdslen(eleobj->ptr),1,fp) == 0)
1449 goto werr;
1450 ln = ln->next;
1451 }
1452 } else if (type == REDIS_SET) {
1453 /* Save a set value */
1454 dict *set = o->ptr;
1455 dictIterator *di = dictGetIterator(set);
1456 dictEntry *de;
1457
1458 if (!set) oom("dictGetIteraotr");
1459 len = htonl(dictGetHashTableUsed(set));
1460 if (fwrite(&len,4,1,fp) == 0) goto werr;
1461 while((de = dictNext(di)) != NULL) {
1462 robj *eleobj;
1463
1464 eleobj = dictGetEntryKey(de);
1465 len = htonl(sdslen(eleobj->ptr));
1466 if (fwrite(&len,4,1,fp) == 0) goto werr;
1467 if (sdslen(eleobj->ptr) && fwrite(eleobj->ptr,sdslen(eleobj->ptr),1,fp) == 0)
1468 goto werr;
1469 }
1470 dictReleaseIterator(di);
1471 } else {
1472 assert(0 != 0);
1473 }
1474 }
1475 dictReleaseIterator(di);
1476 }
1477 /* EOF opcode */
1478 type = REDIS_EOF;
1479 if (fwrite(&type,1,1,fp) == 0) goto werr;
1480 fflush(fp);
1481 fsync(fileno(fp));
1482 fclose(fp);
1483
1484 /* Use RENAME to make sure the DB file is changed atomically only
1485 * if the generate DB file is ok. */
1486 if (rename(tmpfile,filename) == -1) {
1487 redisLog(REDIS_WARNING,"Error moving temp DB file on the final destionation: %s", strerror(errno));
1488 unlink(tmpfile);
1489 return REDIS_ERR;
1490 }
1491 redisLog(REDIS_NOTICE,"DB saved on disk");
1492 server.dirty = 0;
1493 server.lastsave = time(NULL);
1494 return REDIS_OK;
1495
1496werr:
1497 fclose(fp);
1498 unlink(tmpfile);
1499 redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
1500 if (di) dictReleaseIterator(di);
1501 return REDIS_ERR;
1502}
1503
1504static int saveDbBackground(char *filename) {
1505 pid_t childpid;
1506
1507 if (server.bgsaveinprogress) return REDIS_ERR;
1508 if ((childpid = fork()) == 0) {
1509 /* Child */
1510 close(server.fd);
1511 if (saveDb(filename) == REDIS_OK) {
1512 exit(0);
1513 } else {
1514 exit(1);
1515 }
1516 } else {
1517 /* Parent */
1518 redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);
1519 server.bgsaveinprogress = 1;
1520 return REDIS_OK;
1521 }
1522 return REDIS_OK; /* unreached */
1523}
1524
1525static int loadDb(char *filename) {
1526 FILE *fp;
1527 char buf[REDIS_LOADBUF_LEN]; /* Try to use this buffer instead of */
1528 char vbuf[REDIS_LOADBUF_LEN]; /* malloc() when the element is small */
1529 char *key = NULL, *val = NULL;
1530 uint32_t klen,vlen,dbid;
1531 uint8_t type;
1532 int retval;
1533 dict *d = server.dict[0];
1534
1535 fp = fopen(filename,"r");
1536 if (!fp) return REDIS_ERR;
1537 if (fread(buf,9,1,fp) == 0) goto eoferr;
1538 if (memcmp(buf,"REDIS0000",9) != 0) {
1539 fclose(fp);
1540 redisLog(REDIS_WARNING,"Wrong signature trying to load DB from file");
1541 return REDIS_ERR;
1542 }
1543 while(1) {
1544 robj *o;
1545
1546 /* Read type. */
1547 if (fread(&type,1,1,fp) == 0) goto eoferr;
1548 if (type == REDIS_EOF) break;
1549 /* Handle SELECT DB opcode as a special case */
1550 if (type == REDIS_SELECTDB) {
1551 if (fread(&dbid,4,1,fp) == 0) goto eoferr;
1552 dbid = ntohl(dbid);
1553 if (dbid >= (unsigned)server.dbnum) {
1554 redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server compiled to handle more than %d databases. Exiting\n", server.dbnum);
1555 exit(1);
1556 }
1557 d = server.dict[dbid];
1558 continue;
1559 }
1560 /* Read key */
1561 if (fread(&klen,4,1,fp) == 0) goto eoferr;
1562 klen = ntohl(klen);
1563 if (klen <= REDIS_LOADBUF_LEN) {
1564 key = buf;
1565 } else {
1566 key = zmalloc(klen);
1567 if (!key) oom("Loading DB from file");
1568 }
1569 if (fread(key,klen,1,fp) == 0) goto eoferr;
1570
1571 if (type == REDIS_STRING) {
1572 /* Read string value */
1573 if (fread(&vlen,4,1,fp) == 0) goto eoferr;
1574 vlen = ntohl(vlen);
1575 if (vlen <= REDIS_LOADBUF_LEN) {
1576 val = vbuf;
1577 } else {
1578 val = zmalloc(vlen);
1579 if (!val) oom("Loading DB from file");
1580 }
1581 if (vlen && fread(val,vlen,1,fp) == 0) goto eoferr;
1582 o = createObject(REDIS_STRING,sdsnewlen(val,vlen));
1583 } else if (type == REDIS_LIST || type == REDIS_SET) {
1584 /* Read list/set value */
1585 uint32_t listlen;
1586 if (fread(&listlen,4,1,fp) == 0) goto eoferr;
1587 listlen = ntohl(listlen);
1588 o = (type == REDIS_LIST) ? createListObject() : createSetObject();
1589 /* Load every single element of the list/set */
1590 while(listlen--) {
1591 robj *ele;
1592
1593 if (fread(&vlen,4,1,fp) == 0) goto eoferr;
1594 vlen = ntohl(vlen);
1595 if (vlen <= REDIS_LOADBUF_LEN) {
1596 val = vbuf;
1597 } else {
1598 val = zmalloc(vlen);
1599 if (!val) oom("Loading DB from file");
1600 }
1601 if (vlen && fread(val,vlen,1,fp) == 0) goto eoferr;
1602 ele = createObject(REDIS_STRING,sdsnewlen(val,vlen));
1603 if (type == REDIS_LIST) {
1604 if (!listAddNodeTail((list*)o->ptr,ele))
1605 oom("listAddNodeTail");
1606 } else {
1607 if (dictAdd((dict*)o->ptr,ele,NULL) == DICT_ERR)
1608 oom("dictAdd");
1609 }
1610 /* free the temp buffer if needed */
1611 if (val != vbuf) zfree(val);
1612 val = NULL;
1613 }
1614 } else {
1615 assert(0 != 0);
1616 }
1617 /* Add the new object in the hash table */
1618 retval = dictAdd(d,createStringObject(key,klen),o);
1619 if (retval == DICT_ERR) {
1620 redisLog(REDIS_WARNING,"Loading DB, duplicated key found! Unrecoverable error, exiting now.");
1621 exit(1);
1622 }
1623 /* Iteration cleanup */
1624 if (key != buf) zfree(key);
1625 if (val != vbuf) zfree(val);
1626 key = val = NULL;
1627 }
1628 fclose(fp);
1629 return REDIS_OK;
1630
1631eoferr: /* unexpected end of file is handled here with a fatal exit */
1632 if (key != buf) zfree(key);
1633 if (val != vbuf) zfree(val);
1634 redisLog(REDIS_WARNING,"Short read loading DB. Unrecoverable error, exiting now.");
1635 exit(1);
1636 return REDIS_ERR; /* Just to avoid warning */
1637}
1638
1639/*================================== Commands =============================== */
1640
1641static void pingCommand(redisClient *c) {
1642 addReply(c,shared.pong);
1643}
1644
1645static void echoCommand(redisClient *c) {
1646 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",
1647 (int)sdslen(c->argv[1]->ptr)));
1648 addReply(c,c->argv[1]);
1649 addReply(c,shared.crlf);
1650}
1651
1652/*=================================== Strings =============================== */
1653
1654static void setGenericCommand(redisClient *c, int nx) {
1655 int retval;
1656
1657 retval = dictAdd(c->dict,c->argv[1],c->argv[2]);
1658 if (retval == DICT_ERR) {
1659 if (!nx) {
1660 dictReplace(c->dict,c->argv[1],c->argv[2]);
1661 incrRefCount(c->argv[2]);
1662 } else {
1663 addReply(c,shared.zero);
1664 return;
1665 }
1666 } else {
1667 incrRefCount(c->argv[1]);
1668 incrRefCount(c->argv[2]);
1669 }
1670 server.dirty++;
1671 addReply(c, nx ? shared.one : shared.ok);
1672}
1673
1674static void setCommand(redisClient *c) {
1675 return setGenericCommand(c,0);
1676}
1677
1678static void setnxCommand(redisClient *c) {
1679 return setGenericCommand(c,1);
1680}
1681
1682static void getCommand(redisClient *c) {
1683 dictEntry *de;
1684
1685 de = dictFind(c->dict,c->argv[1]);
1686 if (de == NULL) {
1687 addReply(c,shared.nil);
1688 } else {
1689 robj *o = dictGetEntryVal(de);
1690
1691 if (o->type != REDIS_STRING) {
1692 addReply(c,shared.wrongtypeerrbulk);
1693 } else {
1694 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",(int)sdslen(o->ptr)));
1695 addReply(c,o);
1696 addReply(c,shared.crlf);
1697 }
1698 }
1699}
1700
70003d28 1701static void mgetCommand(redisClient *c) {
1702 dictEntry *de;
1703 int j;
1704
1705 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",c->argc-1));
1706 for (j = 1; j < c->argc; j++) {
1707 de = dictFind(c->dict,c->argv[j]);
1708 if (de == NULL) {
1709 addReply(c,shared.minus1);
1710 } else {
1711 robj *o = dictGetEntryVal(de);
1712
1713 if (o->type != REDIS_STRING) {
1714 addReply(c,shared.minus1);
1715 } else {
1716 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",(int)sdslen(o->ptr)));
1717 addReply(c,o);
1718 addReply(c,shared.crlf);
1719 }
1720 }
1721 }
1722}
1723
ed9b544e 1724static void incrDecrCommand(redisClient *c, int incr) {
1725 dictEntry *de;
1726 long long value;
1727 int retval;
1728 robj *o;
1729
1730 de = dictFind(c->dict,c->argv[1]);
1731 if (de == NULL) {
1732 value = 0;
1733 } else {
1734 robj *o = dictGetEntryVal(de);
1735
1736 if (o->type != REDIS_STRING) {
1737 value = 0;
1738 } else {
1739 char *eptr;
1740
1741 value = strtoll(o->ptr, &eptr, 10);
1742 }
1743 }
1744
1745 value += incr;
1746 o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
1747 retval = dictAdd(c->dict,c->argv[1],o);
1748 if (retval == DICT_ERR) {
1749 dictReplace(c->dict,c->argv[1],o);
1750 } else {
1751 incrRefCount(c->argv[1]);
1752 }
1753 server.dirty++;
1754 addReply(c,o);
1755 addReply(c,shared.crlf);
1756}
1757
1758static void incrCommand(redisClient *c) {
1759 return incrDecrCommand(c,1);
1760}
1761
1762static void decrCommand(redisClient *c) {
1763 return incrDecrCommand(c,-1);
1764}
1765
1766static void incrbyCommand(redisClient *c) {
1767 int incr = atoi(c->argv[2]->ptr);
1768 return incrDecrCommand(c,incr);
1769}
1770
1771static void decrbyCommand(redisClient *c) {
1772 int incr = atoi(c->argv[2]->ptr);
1773 return incrDecrCommand(c,-incr);
1774}
1775
1776/* ========================= Type agnostic commands ========================= */
1777
1778static void delCommand(redisClient *c) {
1779 if (dictDelete(c->dict,c->argv[1]) == DICT_OK) {
1780 server.dirty++;
1781 addReply(c,shared.one);
1782 } else {
1783 addReply(c,shared.zero);
1784 }
1785}
1786
1787static void existsCommand(redisClient *c) {
1788 dictEntry *de;
1789
1790 de = dictFind(c->dict,c->argv[1]);
1791 if (de == NULL)
1792 addReply(c,shared.zero);
1793 else
1794 addReply(c,shared.one);
1795}
1796
1797static void selectCommand(redisClient *c) {
1798 int id = atoi(c->argv[1]->ptr);
1799
1800 if (selectDb(c,id) == REDIS_ERR) {
1801 addReplySds(c,"-ERR invalid DB index\r\n");
1802 } else {
1803 addReply(c,shared.ok);
1804 }
1805}
1806
1807static void randomkeyCommand(redisClient *c) {
1808 dictEntry *de;
1809
1810 de = dictGetRandomKey(c->dict);
1811 if (de == NULL) {
1812 addReply(c,shared.crlf);
1813 } else {
1814 addReply(c,dictGetEntryKey(de));
1815 addReply(c,shared.crlf);
1816 }
1817}
1818
1819static void keysCommand(redisClient *c) {
1820 dictIterator *di;
1821 dictEntry *de;
1822 sds pattern = c->argv[1]->ptr;
1823 int plen = sdslen(pattern);
1824 int numkeys = 0, keyslen = 0;
1825 robj *lenobj = createObject(REDIS_STRING,NULL);
1826
1827 di = dictGetIterator(c->dict);
1828 if (!di) oom("dictGetIterator");
1829 addReply(c,lenobj);
1830 decrRefCount(lenobj);
1831 while((de = dictNext(di)) != NULL) {
1832 robj *keyobj = dictGetEntryKey(de);
1833 sds key = keyobj->ptr;
1834 if ((pattern[0] == '*' && pattern[1] == '\0') ||
1835 stringmatchlen(pattern,plen,key,sdslen(key),0)) {
1836 if (numkeys != 0)
1837 addReply(c,shared.space);
1838 addReply(c,keyobj);
1839 numkeys++;
1840 keyslen += sdslen(key);
1841 }
1842 }
1843 dictReleaseIterator(di);
1844 lenobj->ptr = sdscatprintf(sdsempty(),"%lu\r\n",keyslen+(numkeys ? (numkeys-1) : 0));
1845 addReply(c,shared.crlf);
1846}
1847
1848static void dbsizeCommand(redisClient *c) {
1849 addReplySds(c,
1850 sdscatprintf(sdsempty(),"%lu\r\n",dictGetHashTableUsed(c->dict)));
1851}
1852
1853static void lastsaveCommand(redisClient *c) {
1854 addReplySds(c,
1855 sdscatprintf(sdsempty(),"%lu\r\n",server.lastsave));
1856}
1857
1858static void typeCommand(redisClient *c) {
1859 dictEntry *de;
1860 char *type;
1861
1862 de = dictFind(c->dict,c->argv[1]);
1863 if (de == NULL) {
1864 type = "none";
1865 } else {
1866 robj *o = dictGetEntryVal(de);
1867
1868 switch(o->type) {
1869 case REDIS_STRING: type = "string"; break;
1870 case REDIS_LIST: type = "list"; break;
1871 case REDIS_SET: type = "set"; break;
1872 default: type = "unknown"; break;
1873 }
1874 }
1875 addReplySds(c,sdsnew(type));
1876 addReply(c,shared.crlf);
1877}
1878
1879static void saveCommand(redisClient *c) {
1880 if (saveDb(server.dbfilename) == REDIS_OK) {
1881 addReply(c,shared.ok);
1882 } else {
1883 addReply(c,shared.err);
1884 }
1885}
1886
1887static void bgsaveCommand(redisClient *c) {
1888 if (server.bgsaveinprogress) {
1889 addReplySds(c,sdsnew("-ERR background save already in progress\r\n"));
1890 return;
1891 }
1892 if (saveDbBackground(server.dbfilename) == REDIS_OK) {
1893 addReply(c,shared.ok);
1894 } else {
1895 addReply(c,shared.err);
1896 }
1897}
1898
1899static void shutdownCommand(redisClient *c) {
1900 redisLog(REDIS_WARNING,"User requested shutdown, saving DB...");
1901 if (saveDb(server.dbfilename) == REDIS_OK) {
1902 redisLog(REDIS_WARNING,"Server exit now, bye bye...");
1903 exit(1);
1904 } else {
1905 redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
1906 addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n"));
1907 }
1908}
1909
1910static void renameGenericCommand(redisClient *c, int nx) {
1911 dictEntry *de;
1912 robj *o;
1913
1914 /* To use the same key as src and dst is probably an error */
1915 if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) {
1916 if (nx)
1917 addReply(c,shared.minus3);
1918 else
1919 addReplySds(c,sdsnew("-ERR src and dest key are the same\r\n"));
1920 return;
1921 }
1922
1923 de = dictFind(c->dict,c->argv[1]);
1924 if (de == NULL) {
1925 if (nx)
1926 addReply(c,shared.minus1);
1927 else
1928 addReply(c,shared.nokeyerr);
1929 return;
1930 }
1931 o = dictGetEntryVal(de);
1932 incrRefCount(o);
1933 if (dictAdd(c->dict,c->argv[2],o) == DICT_ERR) {
1934 if (nx) {
1935 decrRefCount(o);
1936 addReply(c,shared.zero);
1937 return;
1938 }
1939 dictReplace(c->dict,c->argv[2],o);
1940 } else {
1941 incrRefCount(c->argv[2]);
1942 }
1943 dictDelete(c->dict,c->argv[1]);
1944 server.dirty++;
1945 addReply(c,nx ? shared.one : shared.ok);
1946}
1947
1948static void renameCommand(redisClient *c) {
1949 renameGenericCommand(c,0);
1950}
1951
1952static void renamenxCommand(redisClient *c) {
1953 renameGenericCommand(c,1);
1954}
1955
1956static void moveCommand(redisClient *c) {
1957 dictEntry *de;
1958 robj *o, *key;
1959 dict *src, *dst;
1960 int srcid;
1961
1962 /* Obtain source and target DB pointers */
1963 src = c->dict;
1964 srcid = c->dictid;
1965 if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
1966 addReply(c,shared.minus4);
1967 return;
1968 }
1969 dst = c->dict;
1970 c->dict = src;
1971 c->dictid = srcid;
1972
1973 /* If the user is moving using as target the same
1974 * DB as the source DB it is probably an error. */
1975 if (src == dst) {
1976 addReply(c,shared.minus3);
1977 return;
1978 }
1979
1980 /* Check if the element exists and get a reference */
1981 de = dictFind(c->dict,c->argv[1]);
1982 if (!de) {
1983 addReply(c,shared.zero);
1984 return;
1985 }
1986
1987 /* Try to add the element to the target DB */
1988 key = dictGetEntryKey(de);
1989 o = dictGetEntryVal(de);
1990 if (dictAdd(dst,key,o) == DICT_ERR) {
1991 addReply(c,shared.zero);
1992 return;
1993 }
1994 incrRefCount(key);
1995 incrRefCount(o);
1996
1997 /* OK! key moved, free the entry in the source DB */
1998 dictDelete(src,c->argv[1]);
1999 server.dirty++;
2000 addReply(c,shared.one);
2001}
2002
2003/* =================================== Lists ================================ */
2004static void pushGenericCommand(redisClient *c, int where) {
2005 robj *lobj;
2006 dictEntry *de;
2007 list *list;
2008
2009 de = dictFind(c->dict,c->argv[1]);
2010 if (de == NULL) {
2011 lobj = createListObject();
2012 list = lobj->ptr;
2013 if (where == REDIS_HEAD) {
2014 if (!listAddNodeHead(list,c->argv[2])) oom("listAddNodeHead");
2015 } else {
2016 if (!listAddNodeTail(list,c->argv[2])) oom("listAddNodeTail");
2017 }
2018 dictAdd(c->dict,c->argv[1],lobj);
2019 incrRefCount(c->argv[1]);
2020 incrRefCount(c->argv[2]);
2021 } else {
2022 lobj = dictGetEntryVal(de);
2023 if (lobj->type != REDIS_LIST) {
2024 addReply(c,shared.wrongtypeerr);
2025 return;
2026 }
2027 list = lobj->ptr;
2028 if (where == REDIS_HEAD) {
2029 if (!listAddNodeHead(list,c->argv[2])) oom("listAddNodeHead");
2030 } else {
2031 if (!listAddNodeTail(list,c->argv[2])) oom("listAddNodeTail");
2032 }
2033 incrRefCount(c->argv[2]);
2034 }
2035 server.dirty++;
2036 addReply(c,shared.ok);
2037}
2038
2039static void lpushCommand(redisClient *c) {
2040 pushGenericCommand(c,REDIS_HEAD);
2041}
2042
2043static void rpushCommand(redisClient *c) {
2044 pushGenericCommand(c,REDIS_TAIL);
2045}
2046
2047static void llenCommand(redisClient *c) {
2048 dictEntry *de;
2049 list *l;
2050
2051 de = dictFind(c->dict,c->argv[1]);
2052 if (de == NULL) {
2053 addReply(c,shared.zero);
2054 return;
2055 } else {
2056 robj *o = dictGetEntryVal(de);
2057 if (o->type != REDIS_LIST) {
2058 addReply(c,shared.minus2);
2059 } else {
2060 l = o->ptr;
2061 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",listLength(l)));
2062 }
2063 }
2064}
2065
2066static void lindexCommand(redisClient *c) {
2067 dictEntry *de;
2068 int index = atoi(c->argv[2]->ptr);
2069
2070 de = dictFind(c->dict,c->argv[1]);
2071 if (de == NULL) {
2072 addReply(c,shared.nil);
2073 } else {
2074 robj *o = dictGetEntryVal(de);
2075
2076 if (o->type != REDIS_LIST) {
2077 addReply(c,shared.wrongtypeerrbulk);
2078 } else {
2079 list *list = o->ptr;
2080 listNode *ln;
2081
2082 ln = listIndex(list, index);
2083 if (ln == NULL) {
2084 addReply(c,shared.nil);
2085 } else {
2086 robj *ele = listNodeValue(ln);
2087 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",(int)sdslen(ele->ptr)));
2088 addReply(c,ele);
2089 addReply(c,shared.crlf);
2090 }
2091 }
2092 }
2093}
2094
2095static void lsetCommand(redisClient *c) {
2096 dictEntry *de;
2097 int index = atoi(c->argv[2]->ptr);
2098
2099 de = dictFind(c->dict,c->argv[1]);
2100 if (de == NULL) {
2101 addReply(c,shared.nokeyerr);
2102 } else {
2103 robj *o = dictGetEntryVal(de);
2104
2105 if (o->type != REDIS_LIST) {
2106 addReply(c,shared.wrongtypeerr);
2107 } else {
2108 list *list = o->ptr;
2109 listNode *ln;
2110
2111 ln = listIndex(list, index);
2112 if (ln == NULL) {
2113 addReplySds(c,sdsnew("-ERR index out of range\r\n"));
2114 } else {
2115 robj *ele = listNodeValue(ln);
2116
2117 decrRefCount(ele);
2118 listNodeValue(ln) = c->argv[3];
2119 incrRefCount(c->argv[3]);
2120 addReply(c,shared.ok);
2121 server.dirty++;
2122 }
2123 }
2124 }
2125}
2126
2127static void popGenericCommand(redisClient *c, int where) {
2128 dictEntry *de;
2129
2130 de = dictFind(c->dict,c->argv[1]);
2131 if (de == NULL) {
2132 addReply(c,shared.nil);
2133 } else {
2134 robj *o = dictGetEntryVal(de);
2135
2136 if (o->type != REDIS_LIST) {
2137 addReply(c,shared.wrongtypeerrbulk);
2138 } else {
2139 list *list = o->ptr;
2140 listNode *ln;
2141
2142 if (where == REDIS_HEAD)
2143 ln = listFirst(list);
2144 else
2145 ln = listLast(list);
2146
2147 if (ln == NULL) {
2148 addReply(c,shared.nil);
2149 } else {
2150 robj *ele = listNodeValue(ln);
2151 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",(int)sdslen(ele->ptr)));
2152 addReply(c,ele);
2153 addReply(c,shared.crlf);
2154 listDelNode(list,ln);
2155 server.dirty++;
2156 }
2157 }
2158 }
2159}
2160
2161static void lpopCommand(redisClient *c) {
2162 popGenericCommand(c,REDIS_HEAD);
2163}
2164
2165static void rpopCommand(redisClient *c) {
2166 popGenericCommand(c,REDIS_TAIL);
2167}
2168
2169static void lrangeCommand(redisClient *c) {
2170 dictEntry *de;
2171 int start = atoi(c->argv[2]->ptr);
2172 int end = atoi(c->argv[3]->ptr);
2173
2174 de = dictFind(c->dict,c->argv[1]);
2175 if (de == NULL) {
2176 addReply(c,shared.nil);
2177 } else {
2178 robj *o = dictGetEntryVal(de);
2179
2180 if (o->type != REDIS_LIST) {
2181 addReply(c,shared.wrongtypeerrbulk);
2182 } else {
2183 list *list = o->ptr;
2184 listNode *ln;
2185 int llen = listLength(list);
2186 int rangelen, j;
2187 robj *ele;
2188
2189 /* convert negative indexes */
2190 if (start < 0) start = llen+start;
2191 if (end < 0) end = llen+end;
2192 if (start < 0) start = 0;
2193 if (end < 0) end = 0;
2194
2195 /* indexes sanity checks */
2196 if (start > end || start >= llen) {
2197 /* Out of range start or start > end result in empty list */
2198 addReply(c,shared.zero);
2199 return;
2200 }
2201 if (end >= llen) end = llen-1;
2202 rangelen = (end-start)+1;
2203
2204 /* Return the result in form of a multi-bulk reply */
2205 ln = listIndex(list, start);
2206 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",rangelen));
2207 for (j = 0; j < rangelen; j++) {
2208 ele = listNodeValue(ln);
2209 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",(int)sdslen(ele->ptr)));
2210 addReply(c,ele);
2211 addReply(c,shared.crlf);
2212 ln = ln->next;
2213 }
2214 }
2215 }
2216}
2217
2218static void ltrimCommand(redisClient *c) {
2219 dictEntry *de;
2220 int start = atoi(c->argv[2]->ptr);
2221 int end = atoi(c->argv[3]->ptr);
2222
2223 de = dictFind(c->dict,c->argv[1]);
2224 if (de == NULL) {
2225 addReply(c,shared.nokeyerr);
2226 } else {
2227 robj *o = dictGetEntryVal(de);
2228
2229 if (o->type != REDIS_LIST) {
2230 addReply(c,shared.wrongtypeerr);
2231 } else {
2232 list *list = o->ptr;
2233 listNode *ln;
2234 int llen = listLength(list);
2235 int j, ltrim, rtrim;
2236
2237 /* convert negative indexes */
2238 if (start < 0) start = llen+start;
2239 if (end < 0) end = llen+end;
2240 if (start < 0) start = 0;
2241 if (end < 0) end = 0;
2242
2243 /* indexes sanity checks */
2244 if (start > end || start >= llen) {
2245 /* Out of range start or start > end result in empty list */
2246 ltrim = llen;
2247 rtrim = 0;
2248 } else {
2249 if (end >= llen) end = llen-1;
2250 ltrim = start;
2251 rtrim = llen-end-1;
2252 }
2253
2254 /* Remove list elements to perform the trim */
2255 for (j = 0; j < ltrim; j++) {
2256 ln = listFirst(list);
2257 listDelNode(list,ln);
2258 }
2259 for (j = 0; j < rtrim; j++) {
2260 ln = listLast(list);
2261 listDelNode(list,ln);
2262 }
2263 addReply(c,shared.ok);
2264 server.dirty++;
2265 }
2266 }
2267}
2268
2269static void lremCommand(redisClient *c) {
2270 dictEntry *de;
2271
2272 de = dictFind(c->dict,c->argv[1]);
2273 if (de == NULL) {
2274 addReply(c,shared.minus1);
2275 } else {
2276 robj *o = dictGetEntryVal(de);
2277
2278 if (o->type != REDIS_LIST) {
2279 addReply(c,shared.minus2);
2280 } else {
2281 list *list = o->ptr;
2282 listNode *ln, *next;
2283 int toremove = atoi(c->argv[2]->ptr);
2284 int removed = 0;
2285 int fromtail = 0;
2286
2287 if (toremove < 0) {
2288 toremove = -toremove;
2289 fromtail = 1;
2290 }
2291 ln = fromtail ? list->tail : list->head;
2292 while (ln) {
2293 next = fromtail ? ln->prev : ln->next;
2294 robj *ele = listNodeValue(ln);
2295 if (sdscmp(ele->ptr,c->argv[3]->ptr) == 0) {
2296 listDelNode(list,ln);
2297 server.dirty++;
2298 removed++;
2299 if (toremove && removed == toremove) break;
2300 }
2301 ln = next;
2302 }
2303 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",removed));
2304 }
2305 }
2306}
2307
2308/* ==================================== Sets ================================ */
2309
2310static void saddCommand(redisClient *c) {
2311 dictEntry *de;
2312 robj *set;
2313
2314 de = dictFind(c->dict,c->argv[1]);
2315 if (de == NULL) {
2316 set = createSetObject();
2317 dictAdd(c->dict,c->argv[1],set);
2318 incrRefCount(c->argv[1]);
2319 } else {
2320 set = dictGetEntryVal(de);
2321 if (set->type != REDIS_SET) {
2322 addReply(c,shared.minus2);
2323 return;
2324 }
2325 }
2326 if (dictAdd(set->ptr,c->argv[2],NULL) == DICT_OK) {
2327 incrRefCount(c->argv[2]);
2328 server.dirty++;
2329 addReply(c,shared.one);
2330 } else {
2331 addReply(c,shared.zero);
2332 }
2333}
2334
2335static void sremCommand(redisClient *c) {
2336 dictEntry *de;
2337
2338 de = dictFind(c->dict,c->argv[1]);
2339 if (de == NULL) {
2340 addReply(c,shared.zero);
2341 } else {
2342 robj *set;
2343
2344 set = dictGetEntryVal(de);
2345 if (set->type != REDIS_SET) {
2346 addReply(c,shared.minus2);
2347 return;
2348 }
2349 if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) {
2350 server.dirty++;
2351 addReply(c,shared.one);
2352 } else {
2353 addReply(c,shared.zero);
2354 }
2355 }
2356}
2357
2358static void sismemberCommand(redisClient *c) {
2359 dictEntry *de;
2360
2361 de = dictFind(c->dict,c->argv[1]);
2362 if (de == NULL) {
2363 addReply(c,shared.zero);
2364 } else {
2365 robj *set;
2366
2367 set = dictGetEntryVal(de);
2368 if (set->type != REDIS_SET) {
2369 addReply(c,shared.minus2);
2370 return;
2371 }
2372 if (dictFind(set->ptr,c->argv[2]))
2373 addReply(c,shared.one);
2374 else
2375 addReply(c,shared.zero);
2376 }
2377}
2378
2379static void scardCommand(redisClient *c) {
2380 dictEntry *de;
2381 dict *s;
2382
2383 de = dictFind(c->dict,c->argv[1]);
2384 if (de == NULL) {
2385 addReply(c,shared.zero);
2386 return;
2387 } else {
2388 robj *o = dictGetEntryVal(de);
2389 if (o->type != REDIS_SET) {
2390 addReply(c,shared.minus2);
2391 } else {
2392 s = o->ptr;
2393 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",
2394 dictGetHashTableUsed(s)));
2395 }
2396 }
2397}
2398
2399static int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
2400 dict **d1 = (void*) s1, **d2 = (void*) s2;
2401
2402 return dictGetHashTableUsed(*d1)-dictGetHashTableUsed(*d2);
2403}
2404
2405static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, robj *dstkey) {
2406 dict **dv = zmalloc(sizeof(dict*)*setsnum);
2407 dictIterator *di;
2408 dictEntry *de;
2409 robj *lenobj = NULL, *dstset = NULL;
2410 int j, cardinality = 0;
2411
2412 if (!dv) oom("sinterCommand");
2413 for (j = 0; j < setsnum; j++) {
2414 robj *setobj;
2415 dictEntry *de;
2416
2417 de = dictFind(c->dict,setskeys[j]);
2418 if (!de) {
2419 zfree(dv);
2420 addReply(c,dstkey ? shared.nokeyerr : shared.nil);
2421 return;
2422 }
2423 setobj = dictGetEntryVal(de);
2424 if (setobj->type != REDIS_SET) {
2425 zfree(dv);
2426 addReply(c,dstkey ? shared.wrongtypeerr : shared.wrongtypeerrbulk);
2427 return;
2428 }
2429 dv[j] = setobj->ptr;
2430 }
2431 /* Sort sets from the smallest to largest, this will improve our
2432 * algorithm's performace */
2433 qsort(dv,setsnum,sizeof(dict*),qsortCompareSetsByCardinality);
2434
2435 /* The first thing we should output is the total number of elements...
2436 * since this is a multi-bulk write, but at this stage we don't know
2437 * the intersection set size, so we use a trick, append an empty object
2438 * to the output list and save the pointer to later modify it with the
2439 * right length */
2440 if (!dstkey) {
2441 lenobj = createObject(REDIS_STRING,NULL);
2442 addReply(c,lenobj);
2443 decrRefCount(lenobj);
2444 } else {
2445 /* If we have a target key where to store the resulting set
2446 * create this key with an empty set inside */
2447 dstset = createSetObject();
2448 dictDelete(c->dict,dstkey);
2449 dictAdd(c->dict,dstkey,dstset);
2450 incrRefCount(dstkey);
2451 }
2452
2453 /* Iterate all the elements of the first (smallest) set, and test
2454 * the element against all the other sets, if at least one set does
2455 * not include the element it is discarded */
2456 di = dictGetIterator(dv[0]);
2457 if (!di) oom("dictGetIterator");
2458
2459 while((de = dictNext(di)) != NULL) {
2460 robj *ele;
2461
2462 for (j = 1; j < setsnum; j++)
2463 if (dictFind(dv[j],dictGetEntryKey(de)) == NULL) break;
2464 if (j != setsnum)
2465 continue; /* at least one set does not contain the member */
2466 ele = dictGetEntryKey(de);
2467 if (!dstkey) {
2468 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",sdslen(ele->ptr)));
2469 addReply(c,ele);
2470 addReply(c,shared.crlf);
2471 cardinality++;
2472 } else {
2473 dictAdd(dstset->ptr,ele,NULL);
2474 incrRefCount(ele);
2475 }
2476 }
2477 dictReleaseIterator(di);
2478
2479 if (!dstkey)
2480 lenobj->ptr = sdscatprintf(sdsempty(),"%d\r\n",cardinality);
2481 else
2482 addReply(c,shared.ok);
2483 zfree(dv);
2484}
2485
2486static void sinterCommand(redisClient *c) {
2487 sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);
2488}
2489
2490static void sinterstoreCommand(redisClient *c) {
2491 sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
2492}
2493
2494static void flushdbCommand(redisClient *c) {
2495 dictEmpty(c->dict);
2496 addReply(c,shared.ok);
2497 saveDb(server.dbfilename);
2498}
2499
2500static void flushallCommand(redisClient *c) {
2501 emptyDb();
2502 addReply(c,shared.ok);
2503 saveDb(server.dbfilename);
2504}
2505
2506redisSortOperation *createSortOperation(int type, robj *pattern) {
2507 redisSortOperation *so = zmalloc(sizeof(*so));
2508 if (!so) oom("createSortOperation");
2509 so->type = type;
2510 so->pattern = pattern;
2511 return so;
2512}
2513
2514/* Return the value associated to the key with a name obtained
2515 * substituting the first occurence of '*' in 'pattern' with 'subst' */
2516robj *lookupKeyByPattern(dict *dict, robj *pattern, robj *subst) {
2517 char *p;
2518 sds spat, ssub;
2519 robj keyobj;
2520 int prefixlen, sublen, postfixlen;
2521 dictEntry *de;
2522 /* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
2523 struct {
2524 long len;
2525 long free;
2526 char buf[REDIS_SORTKEY_MAX+1];
2527 } keyname;
2528
2529
2530 spat = pattern->ptr;
2531 ssub = subst->ptr;
2532 if (sdslen(spat)+sdslen(ssub)-1 > REDIS_SORTKEY_MAX) return NULL;
2533 p = strchr(spat,'*');
2534 if (!p) return NULL;
2535
2536 prefixlen = p-spat;
2537 sublen = sdslen(ssub);
2538 postfixlen = sdslen(spat)-(prefixlen+1);
2539 memcpy(keyname.buf,spat,prefixlen);
2540 memcpy(keyname.buf+prefixlen,ssub,sublen);
2541 memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
2542 keyname.buf[prefixlen+sublen+postfixlen] = '\0';
2543 keyname.len = prefixlen+sublen+postfixlen;
2544
2545 keyobj.refcount = 1;
2546 keyobj.type = REDIS_STRING;
2547 keyobj.ptr = ((char*)&keyname)+(sizeof(long)*2);
2548
2549 de = dictFind(dict,&keyobj);
2550 // printf("lookup '%s' => %p\n", keyname.buf,de);
2551 if (!de) return NULL;
2552 return dictGetEntryVal(de);
2553}
2554
2555/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
2556 * the additional parameter is not standard but a BSD-specific we have to
2557 * pass sorting parameters via the global 'server' structure */
2558static int sortCompare(const void *s1, const void *s2) {
2559 const redisSortObject *so1 = s1, *so2 = s2;
2560 int cmp;
2561
2562 if (!server.sort_alpha) {
2563 /* Numeric sorting. Here it's trivial as we precomputed scores */
2564 if (so1->u.score > so2->u.score) {
2565 cmp = 1;
2566 } else if (so1->u.score < so2->u.score) {
2567 cmp = -1;
2568 } else {
2569 cmp = 0;
2570 }
2571 } else {
2572 /* Alphanumeric sorting */
2573 if (server.sort_bypattern) {
2574 if (!so1->u.cmpobj || !so2->u.cmpobj) {
2575 /* At least one compare object is NULL */
2576 if (so1->u.cmpobj == so2->u.cmpobj)
2577 cmp = 0;
2578 else if (so1->u.cmpobj == NULL)
2579 cmp = -1;
2580 else
2581 cmp = 1;
2582 } else {
2583 /* We have both the objects, use strcoll */
2584 cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
2585 }
2586 } else {
2587 /* Compare elements directly */
2588 cmp = strcoll(so1->obj->ptr,so2->obj->ptr);
2589 }
2590 }
2591 return server.sort_desc ? -cmp : cmp;
2592}
2593
2594/* The SORT command is the most complex command in Redis. Warning: this code
2595 * is optimized for speed and a bit less for readability */
2596static void sortCommand(redisClient *c) {
2597 dictEntry *de;
2598 list *operations;
2599 int outputlen = 0;
2600 int desc = 0, alpha = 0;
2601 int limit_start = 0, limit_count = -1, start, end;
2602 int j, dontsort = 0, vectorlen;
2603 int getop = 0; /* GET operation counter */
2604 robj *sortval, *sortby = NULL;
2605 redisSortObject *vector; /* Resulting vector to sort */
2606
2607 /* Lookup the key to sort. It must be of the right types */
2608 de = dictFind(c->dict,c->argv[1]);
2609 if (de == NULL) {
2610 addReply(c,shared.nokeyerrbulk);
2611 return;
2612 }
2613 sortval = dictGetEntryVal(de);
2614 if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST) {
2615 addReply(c,shared.wrongtypeerrbulk);
2616 return;
2617 }
2618
2619 /* Create a list of operations to perform for every sorted element.
2620 * Operations can be GET/DEL/INCR/DECR */
2621 operations = listCreate();
2622 listSetFreeMethod(operations,free);
2623 j = 2;
2624
2625 /* Now we need to protect sortval incrementing its count, in the future
2626 * SORT may have options able to overwrite/delete keys during the sorting
2627 * and the sorted key itself may get destroied */
2628 incrRefCount(sortval);
2629
2630 /* The SORT command has an SQL-alike syntax, parse it */
2631 while(j < c->argc) {
2632 int leftargs = c->argc-j-1;
2633 if (!strcasecmp(c->argv[j]->ptr,"asc")) {
2634 desc = 0;
2635 } else if (!strcasecmp(c->argv[j]->ptr,"desc")) {
2636 desc = 1;
2637 } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) {
2638 alpha = 1;
2639 } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) {
2640 limit_start = atoi(c->argv[j+1]->ptr);
2641 limit_count = atoi(c->argv[j+2]->ptr);
2642 j+=2;
2643 } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) {
2644 sortby = c->argv[j+1];
2645 /* If the BY pattern does not contain '*', i.e. it is constant,
2646 * we don't need to sort nor to lookup the weight keys. */
2647 if (strchr(c->argv[j+1]->ptr,'*') == NULL) dontsort = 1;
2648 j++;
2649 } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {
2650 listAddNodeTail(operations,createSortOperation(
2651 REDIS_SORT_GET,c->argv[j+1]));
2652 getop++;
2653 j++;
2654 } else if (!strcasecmp(c->argv[j]->ptr,"del") && leftargs >= 1) {
2655 listAddNodeTail(operations,createSortOperation(
2656 REDIS_SORT_DEL,c->argv[j+1]));
2657 j++;
2658 } else if (!strcasecmp(c->argv[j]->ptr,"incr") && leftargs >= 1) {
2659 listAddNodeTail(operations,createSortOperation(
2660 REDIS_SORT_INCR,c->argv[j+1]));
2661 j++;
2662 } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {
2663 listAddNodeTail(operations,createSortOperation(
2664 REDIS_SORT_DECR,c->argv[j+1]));
2665 j++;
2666 } else {
2667 decrRefCount(sortval);
2668 listRelease(operations);
2669 addReply(c,shared.syntaxerrbulk);
2670 return;
2671 }
2672 j++;
2673 }
2674
2675 /* Load the sorting vector with all the objects to sort */
2676 vectorlen = (sortval->type == REDIS_LIST) ?
2677 listLength((list*)sortval->ptr) :
2678 dictGetHashTableUsed((dict*)sortval->ptr);
2679 vector = zmalloc(sizeof(redisSortObject)*vectorlen);
2680 if (!vector) oom("allocating objects vector for SORT");
2681 j = 0;
2682 if (sortval->type == REDIS_LIST) {
2683 list *list = sortval->ptr;
2684 listNode *ln = list->head;
2685 while(ln) {
2686 robj *ele = ln->value;
2687 vector[j].obj = ele;
2688 vector[j].u.score = 0;
2689 vector[j].u.cmpobj = NULL;
2690 ln = ln->next;
2691 j++;
2692 }
2693 } else {
2694 dict *set = sortval->ptr;
2695 dictIterator *di;
2696 dictEntry *setele;
2697
2698 di = dictGetIterator(set);
2699 if (!di) oom("dictGetIterator");
2700 while((setele = dictNext(di)) != NULL) {
2701 vector[j].obj = dictGetEntryKey(setele);
2702 vector[j].u.score = 0;
2703 vector[j].u.cmpobj = NULL;
2704 j++;
2705 }
2706 dictReleaseIterator(di);
2707 }
2708 assert(j == vectorlen);
2709
2710 /* Now it's time to load the right scores in the sorting vector */
2711 if (dontsort == 0) {
2712 for (j = 0; j < vectorlen; j++) {
2713 if (sortby) {
2714 robj *byval;
2715
2716 byval = lookupKeyByPattern(c->dict,sortby,vector[j].obj);
2717 if (!byval || byval->type != REDIS_STRING) continue;
2718 if (alpha) {
2719 vector[j].u.cmpobj = byval;
2720 incrRefCount(byval);
2721 } else {
2722 vector[j].u.score = strtod(byval->ptr,NULL);
2723 }
2724 } else {
2725 if (!alpha) vector[j].u.score = strtod(vector[j].obj->ptr,NULL);
2726 }
2727 }
2728 }
2729
2730 /* We are ready to sort the vector... perform a bit of sanity check
2731 * on the LIMIT option too. We'll use a partial version of quicksort. */
2732 start = (limit_start < 0) ? 0 : limit_start;
2733 end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
2734 if (start >= vectorlen) {
2735 start = vectorlen-1;
2736 end = vectorlen-2;
2737 }
2738 if (end >= vectorlen) end = vectorlen-1;
2739
2740 if (dontsort == 0) {
2741 server.sort_desc = desc;
2742 server.sort_alpha = alpha;
2743 server.sort_bypattern = sortby ? 1 : 0;
2744 qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
2745 }
2746
2747 /* Send command output to the output buffer, performing the specified
2748 * GET/DEL/INCR/DECR operations if any. */
2749 outputlen = getop ? getop*(end-start+1) : end-start+1;
2750 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",outputlen));
2751 for (j = start; j <= end; j++) {
2752 listNode *ln = operations->head;
2753 if (!getop) {
2754 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",
2755 sdslen(vector[j].obj->ptr)));
2756 addReply(c,vector[j].obj);
2757 addReply(c,shared.crlf);
2758 }
2759 while(ln) {
2760 redisSortOperation *sop = ln->value;
2761 robj *val = lookupKeyByPattern(c->dict,sop->pattern,
2762 vector[j].obj);
2763
2764 if (sop->type == REDIS_SORT_GET) {
2765 if (!val || val->type != REDIS_STRING) {
2766 addReply(c,shared.minus1);
2767 } else {
2768 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",
2769 sdslen(val->ptr)));
2770 addReply(c,val);
2771 addReply(c,shared.crlf);
2772 }
2773 } else if (sop->type == REDIS_SORT_DEL) {
2774 /* TODO */
2775 }
2776 ln = ln->next;
2777 }
2778 }
2779
2780 /* Cleanup */
2781 decrRefCount(sortval);
2782 listRelease(operations);
2783 for (j = 0; j < vectorlen; j++) {
2784 if (sortby && alpha && vector[j].u.cmpobj)
2785 decrRefCount(vector[j].u.cmpobj);
2786 }
2787 zfree(vector);
2788}
2789
2790static void infoCommand(redisClient *c) {
2791 sds info;
2792 time_t uptime = time(NULL)-server.stat_starttime;
2793
2794 info = sdscatprintf(sdsempty(),
2795 "redis_version:%s\r\n"
2796 "connected_clients:%d\r\n"
2797 "connected_slaves:%d\r\n"
2798 "used_memory:%d\r\n"
2799 "changes_since_last_save:%lld\r\n"
2800 "last_save_time:%d\r\n"
2801 "total_connections_received:%lld\r\n"
2802 "total_commands_processed:%lld\r\n"
2803 "uptime_in_seconds:%d\r\n"
2804 "uptime_in_days:%d\r\n"
2805 ,REDIS_VERSION,
2806 listLength(server.clients)-listLength(server.slaves),
2807 listLength(server.slaves),
2808 server.usedmemory,
2809 server.dirty,
2810 server.lastsave,
2811 server.stat_numconnections,
2812 server.stat_numcommands,
2813 uptime,
2814 uptime/(3600*24)
2815 );
2816 addReplySds(c,sdscatprintf(sdsempty(),"%d\r\n",sdslen(info)));
2817 addReplySds(c,info);
70003d28 2818 addReply(c,shared.crlf);
ed9b544e 2819}
2820
2821/* =============================== Replication ============================= */
2822
2823/* Send the whole output buffer syncronously to the slave. This a general operation in theory, but it is actually useful only for replication. */
2824static int flushClientOutput(redisClient *c) {
2825 int retval;
2826 time_t start = time(NULL);
2827
2828 while(listLength(c->reply)) {
2829 if (time(NULL)-start > 5) return REDIS_ERR; /* 5 seconds timeout */
2830 retval = aeWait(c->fd,AE_WRITABLE,1000);
2831 if (retval == -1) {
2832 return REDIS_ERR;
2833 } else if (retval & AE_WRITABLE) {
2834 sendReplyToClient(NULL, c->fd, c, AE_WRITABLE);
2835 }
2836 }
2837 return REDIS_OK;
2838}
2839
2840static int syncWrite(int fd, void *ptr, ssize_t size, int timeout) {
2841 ssize_t nwritten, ret = size;
2842 time_t start = time(NULL);
2843
2844 timeout++;
2845 while(size) {
2846 if (aeWait(fd,AE_WRITABLE,1000) & AE_WRITABLE) {
2847 nwritten = write(fd,ptr,size);
2848 if (nwritten == -1) return -1;
2849 ptr += nwritten;
2850 size -= nwritten;
2851 }
2852 if ((time(NULL)-start) > timeout) {
2853 errno = ETIMEDOUT;
2854 return -1;
2855 }
2856 }
2857 return ret;
2858}
2859
2860static int syncRead(int fd, void *ptr, ssize_t size, int timeout) {
2861 ssize_t nread, totread = 0;
2862 time_t start = time(NULL);
2863
2864 timeout++;
2865 while(size) {
2866 if (aeWait(fd,AE_READABLE,1000) & AE_READABLE) {
2867 nread = read(fd,ptr,size);
2868 if (nread == -1) return -1;
2869 ptr += nread;
2870 size -= nread;
2871 totread += nread;
2872 }
2873 if ((time(NULL)-start) > timeout) {
2874 errno = ETIMEDOUT;
2875 return -1;
2876 }
2877 }
2878 return totread;
2879}
2880
2881static int syncReadLine(int fd, char *ptr, ssize_t size, int timeout) {
2882 ssize_t nread = 0;
2883
2884 size--;
2885 while(size) {
2886 char c;
2887
2888 if (syncRead(fd,&c,1,timeout) == -1) return -1;
2889 if (c == '\n') {
2890 *ptr = '\0';
2891 if (nread && *(ptr-1) == '\r') *(ptr-1) = '\0';
2892 return nread;
2893 } else {
2894 *ptr++ = c;
2895 *ptr = '\0';
2896 nread++;
2897 }
2898 }
2899 return nread;
2900}
2901
2902static void syncCommand(redisClient *c) {
2903 struct stat sb;
2904 int fd = -1, len;
2905 time_t start = time(NULL);
2906 char sizebuf[32];
2907
2908 redisLog(REDIS_NOTICE,"Slave ask for syncronization");
2909 if (flushClientOutput(c) == REDIS_ERR || saveDb(server.dbfilename) != REDIS_OK)
2910 goto closeconn;
2911
2912 fd = open(server.dbfilename, O_RDONLY);
2913 if (fd == -1 || fstat(fd,&sb) == -1) goto closeconn;
2914 len = sb.st_size;
2915
2916 snprintf(sizebuf,32,"%d\r\n",len);
2917 if (syncWrite(c->fd,sizebuf,strlen(sizebuf),5) == -1) goto closeconn;
2918 while(len) {
2919 char buf[1024];
2920 int nread;
2921
2922 if (time(NULL)-start > REDIS_MAX_SYNC_TIME) goto closeconn;
2923 nread = read(fd,buf,1024);
2924 if (nread == -1) goto closeconn;
2925 len -= nread;
2926 if (syncWrite(c->fd,buf,nread,5) == -1) goto closeconn;
2927 }
2928 if (syncWrite(c->fd,"\r\n",2,5) == -1) goto closeconn;
2929 close(fd);
2930 c->flags |= REDIS_SLAVE;
2931 c->slaveseldb = 0;
2932 if (!listAddNodeTail(server.slaves,c)) oom("listAddNodeTail");
2933 redisLog(REDIS_NOTICE,"Syncronization with slave succeeded");
2934 return;
2935
2936closeconn:
2937 if (fd != -1) close(fd);
2938 c->flags |= REDIS_CLOSE;
2939 redisLog(REDIS_WARNING,"Syncronization with slave failed");
2940 return;
2941}
2942
2943static int syncWithMaster(void) {
2944 char buf[1024], tmpfile[256];
2945 int dumpsize;
2946 int fd = anetTcpConnect(NULL,server.masterhost,server.masterport);
2947 int dfd;
2948
2949 if (fd == -1) {
2950 redisLog(REDIS_WARNING,"Unable to connect to MASTER: %s",
2951 strerror(errno));
2952 return REDIS_ERR;
2953 }
2954 /* Issue the SYNC command */
2955 if (syncWrite(fd,"SYNC \r\n",7,5) == -1) {
2956 close(fd);
2957 redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s",
2958 strerror(errno));
2959 return REDIS_ERR;
2960 }
2961 /* Read the bulk write count */
2962 if (syncReadLine(fd,buf,1024,5) == -1) {
2963 close(fd);
2964 redisLog(REDIS_WARNING,"I/O error reading bulk count from MASTER: %s",
2965 strerror(errno));
2966 return REDIS_ERR;
2967 }
2968 dumpsize = atoi(buf);
2969 redisLog(REDIS_NOTICE,"Receiving %d bytes data dump from MASTER",dumpsize);
2970 /* Read the bulk write data on a temp file */
2971 snprintf(tmpfile,256,"temp-%d.%ld.rdb",(int)time(NULL),(long int)random());
2972 dfd = open(tmpfile,O_CREAT|O_WRONLY,0644);
2973 if (dfd == -1) {
2974 close(fd);
2975 redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno));
2976 return REDIS_ERR;
2977 }
2978 while(dumpsize) {
2979 int nread, nwritten;
2980
2981 nread = read(fd,buf,(dumpsize < 1024)?dumpsize:1024);
2982 if (nread == -1) {
2983 redisLog(REDIS_WARNING,"I/O error trying to sync with MASTER: %s",
2984 strerror(errno));
2985 close(fd);
2986 close(dfd);
2987 return REDIS_ERR;
2988 }
2989 nwritten = write(dfd,buf,nread);
2990 if (nwritten == -1) {
2991 redisLog(REDIS_WARNING,"Write error writing to the DB dump file needed for MASTER <-> SLAVE synchrnonization: %s", strerror(errno));
2992 close(fd);
2993 close(dfd);
2994 return REDIS_ERR;
2995 }
2996 dumpsize -= nread;
2997 }
2998 close(dfd);
2999 if (rename(tmpfile,server.dbfilename) == -1) {
3000 redisLog(REDIS_WARNING,"Failed trying to rename the temp DB into dump.rdb in MASTER <-> SLAVE synchronization: %s", strerror(errno));
3001 unlink(tmpfile);
3002 close(fd);
3003 return REDIS_ERR;
3004 }
3005 emptyDb();
3006 if (loadDb(server.dbfilename) != REDIS_OK) {
3007 redisLog(REDIS_WARNING,"Failed trying to load the MASTER synchronization DB from disk");
3008 close(fd);
3009 return REDIS_ERR;
3010 }
3011 server.master = createClient(fd);
3012 server.master->flags |= REDIS_MASTER;
3013 server.replstate = REDIS_REPL_CONNECTED;
3014 return REDIS_OK;
3015}
3016
3017/* =================================== Main! ================================ */
3018
3019static void daemonize(void) {
3020 int fd;
3021 FILE *fp;
3022
3023 if (fork() != 0) exit(0); /* parent exits */
3024 setsid(); /* create a new session */
3025
3026 /* Every output goes to /dev/null. If Redis is daemonized but
3027 * the 'logfile' is set to 'stdout' in the configuration file
3028 * it will not log at all. */
3029 if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
3030 dup2(fd, STDIN_FILENO);
3031 dup2(fd, STDOUT_FILENO);
3032 dup2(fd, STDERR_FILENO);
3033 if (fd > STDERR_FILENO) close(fd);
3034 }
3035 /* Try to write the pid file */
3036 fp = fopen("/var/run/redis.pid","w");
3037 if (fp) {
3038 fprintf(fp,"%d\n",getpid());
3039 fclose(fp);
3040 }
3041}
3042
3043int main(int argc, char **argv) {
3044 initServerConfig();
3045 if (argc == 2) {
3046 ResetServerSaveParams();
3047 loadServerConfig(argv[1]);
3048 } else if (argc > 2) {
3049 fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
3050 exit(1);
3051 }
3052 initServer();
3053 if (server.daemonize) daemonize();
3054 redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);
3055 if (loadDb(server.dbfilename) == REDIS_OK)
3056 redisLog(REDIS_NOTICE,"DB loaded from disk");
3057 if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,
3058 acceptHandler, NULL, NULL) == AE_ERR) oom("creating file event");
3059 redisLog(REDIS_NOTICE,"The server is now ready to accept connections");
3060 aeMain(server.el);
3061 aeDeleteEventLoop(server.el);
3062 return 0;
3063}