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