2010-06-21 18:07:48 -04:00
/*
* Copyright ( c ) 2009 - 2010 , Salvatore Sanfilippo < antirez at gmail dot com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
# include "redis.h"
# ifdef HAVE_BACKTRACE
# include <execinfo.h>
# include <ucontext.h>
# endif /* HAVE_BACKTRACE */
# include <time.h>
# include <signal.h>
# include <sys/wait.h>
# include <errno.h>
# include <assert.h>
# include <ctype.h>
# include <stdarg.h>
# include <arpa/inet.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <sys/time.h>
# include <sys/resource.h>
# include <sys/uio.h>
# include <limits.h>
# include <float.h>
# include <math.h>
# include <pthread.h>
2010-09-16 07:28:58 -04:00
# include <sys/resource.h>
2010-06-21 18:07:48 -04:00
/* Our shared "common" objects */
struct sharedObjectsStruct shared ;
/* Global vars that are actally used as constants. The following double
* values are used for double on - disk serialization , and are initialized
* at runtime to avoid strange compiler optimizations . */
double R_Zero , R_PosInf , R_NegInf , R_Nan ;
/*================================= Globals ================================= */
/* Global vars */
struct redisServer server ; /* server global state */
struct redisCommand * commandTable ;
2011-01-24 04:56:06 -05:00
struct redisCommand redisCommandTable [ ] = {
2011-01-23 16:14:15 -05:00
{ " get " , getCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-03-22 14:35:42 -04:00
{ " set " , setCommand , 3 , REDIS_CMD_DENYOOM , noPreloadGetKeys , 1 , 1 , 1 , 0 , 0 } ,
{ " setnx " , setnxCommand , 3 , REDIS_CMD_DENYOOM , noPreloadGetKeys , 1 , 1 , 1 , 0 , 0 } ,
{ " setex " , setexCommand , 4 , REDIS_CMD_DENYOOM , noPreloadGetKeys , 2 , 2 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " append " , appendCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " strlen " , strlenCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-03-22 14:35:42 -04:00
{ " del " , delCommand , - 2 , 0 , noPreloadGetKeys , 1 , - 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " exists " , existsCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " setbit " , setbitCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " getbit " , getbitCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " setrange " , setrangeCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " getrange " , getrangeCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " substr " , getrangeCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " incr " , incrCommand , 2 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " decr " , decrCommand , 2 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " mget " , mgetCommand , - 2 , 0 , NULL , 1 , - 1 , 1 , 0 , 0 } ,
2011-04-15 10:35:27 -04:00
{ " rpush " , rpushCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lpush " , lpushCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " rpushx " , rpushxCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lpushx " , lpushxCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " linsert " , linsertCommand , 5 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " rpop " , rpopCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lpop " , lpopCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " brpop " , brpopCommand , - 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " brpoplpush " , brpoplpushCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 2 , 1 , 0 , 0 } ,
2011-03-22 14:35:42 -04:00
{ " blpop " , blpopCommand , - 3 , 0 , NULL , 1 , - 2 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " llen " , llenCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lindex " , lindexCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lset " , lsetCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lrange " , lrangeCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " ltrim " , ltrimCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " lrem " , lremCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " rpoplpush " , rpoplpushCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 2 , 1 , 0 , 0 } ,
2011-04-15 12:08:32 -04:00
{ " sadd " , saddCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-04-19 11:37:03 -04:00
{ " srem " , sremCommand , - 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " smove " , smoveCommand , 4 , 0 , NULL , 1 , 2 , 1 , 0 , 0 } ,
{ " sismember " , sismemberCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " scard " , scardCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " spop " , spopCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " srandmember " , srandmemberCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " sinter " , sinterCommand , - 2 , REDIS_CMD_DENYOOM , NULL , 1 , - 1 , 1 , 0 , 0 } ,
{ " sinterstore " , sinterstoreCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 2 , - 1 , 1 , 0 , 0 } ,
{ " sunion " , sunionCommand , - 2 , REDIS_CMD_DENYOOM , NULL , 1 , - 1 , 1 , 0 , 0 } ,
{ " sunionstore " , sunionstoreCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 2 , - 1 , 1 , 0 , 0 } ,
{ " sdiff " , sdiffCommand , - 2 , REDIS_CMD_DENYOOM , NULL , 1 , - 1 , 1 , 0 , 0 } ,
{ " sdiffstore " , sdiffstoreCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 2 , - 1 , 1 , 0 , 0 } ,
{ " smembers " , sinterCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-05-31 11:47:34 -04:00
{ " zadd " , zaddCommand , - 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " zincrby " , zincrbyCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-05-31 14:15:18 -04:00
{ " zrem " , zremCommand , - 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " zremrangebyscore " , zremrangebyscoreCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zremrangebyrank " , zremrangebyrankCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-03-22 14:35:42 -04:00
{ " zunionstore " , zunionstoreCommand , - 4 , REDIS_CMD_DENYOOM , zunionInterGetKeys , 0 , 0 , 0 , 0 , 0 } ,
{ " zinterstore " , zinterstoreCommand , - 4 , REDIS_CMD_DENYOOM , zunionInterGetKeys , 0 , 0 , 0 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " zrange " , zrangeCommand , - 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zrangebyscore " , zrangebyscoreCommand , - 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zrevrangebyscore " , zrevrangebyscoreCommand , - 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zcount " , zcountCommand , 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zrevrange " , zrevrangeCommand , - 4 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zcard " , zcardCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zscore " , zscoreCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zrank " , zrankCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " zrevrank " , zrevrankCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hset " , hsetCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hsetnx " , hsetnxCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hget " , hgetCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hmset " , hmsetCommand , - 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hmget " , hmgetCommand , - 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hincrby " , hincrbyCommand , 4 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-04-19 11:07:55 -04:00
{ " hdel " , hdelCommand , - 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " hlen " , hlenCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hkeys " , hkeysCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hvals " , hvalsCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hgetall " , hgetallCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " hexists " , hexistsCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " incrby " , incrbyCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " decrby " , decrbyCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " getset " , getsetCommand , 3 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " mset " , msetCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 1 , - 1 , 2 , 0 , 0 } ,
{ " msetnx " , msetnxCommand , - 3 , REDIS_CMD_DENYOOM , NULL , 1 , - 1 , 2 , 0 , 0 } ,
{ " randomkey " , randomkeyCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " select " , selectCommand , 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " move " , moveCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-03-22 14:35:42 -04:00
{ " rename " , renameCommand , 3 , 0 , renameGetKeys , 1 , 2 , 1 , 0 , 0 } ,
{ " renamenx " , renamenxCommand , 3 , 0 , renameGetKeys , 1 , 2 , 1 , 0 , 0 } ,
{ " expire " , expireCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " expireat " , expireatCommand , 3 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " keys " , keysCommand , 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " dbsize " , dbsizeCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " auth " , authCommand , 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " ping " , pingCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " echo " , echoCommand , 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " save " , saveCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " bgsave " , bgsaveCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " bgrewriteaof " , bgrewriteaofCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " shutdown " , shutdownCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " lastsave " , lastsaveCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " type " , typeCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " multi " , multiCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
2011-03-23 13:09:17 -04:00
{ " exec " , execCommand , 1 , REDIS_CMD_DENYOOM , NULL , 0 , 0 , 0 , 0 , 0 } ,
2011-01-23 16:14:15 -05:00
{ " discard " , discardCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " sync " , syncCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " flushdb " , flushdbCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " flushall " , flushallCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " sort " , sortCommand , - 2 , REDIS_CMD_DENYOOM , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " info " , infoCommand , - 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " monitor " , monitorCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " ttl " , ttlCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " persist " , persistCommand , 2 , 0 , NULL , 1 , 1 , 1 , 0 , 0 } ,
{ " slaveof " , slaveofCommand , 3 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " debug " , debugCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " config " , configCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " subscribe " , subscribeCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " unsubscribe " , unsubscribeCommand , - 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " psubscribe " , psubscribeCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " punsubscribe " , punsubscribeCommand , - 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " publish " , publishCommand , 3 , REDIS_CMD_FORCE_REPLICATION , NULL , 0 , 0 , 0 , 0 , 0 } ,
2011-03-22 14:35:42 -04:00
{ " watch " , watchCommand , - 2 , 0 , noPreloadGetKeys , 1 , - 1 , 1 , 0 , 0 } ,
2011-03-29 11:51:15 -04:00
{ " unwatch " , unwatchCommand , 1 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " cluster " , clusterCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
2011-04-01 12:59:28 -04:00
{ " restore " , restoreCommand , 4 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " migrate " , migrateCommand , 6 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
2011-04-06 06:19:45 -04:00
{ " dump " , dumpCommand , 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
2011-04-21 09:38:02 -04:00
{ " object " , objectCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 } ,
{ " client " , clientCommand , - 2 , 0 , NULL , 0 , 0 , 0 , 0 , 0 }
2010-06-21 18:07:48 -04:00
} ;
/*============================ Utility functions ============================ */
2011-02-09 04:10:35 -05:00
/* Low level logging. To use only for very big messages, otherwise
* redisLog ( ) is to prefer . */
void redisLogRaw ( int level , const char * msg ) {
2010-12-09 11:10:21 -05:00
const int syslogLevelMap [ ] = { LOG_DEBUG , LOG_INFO , LOG_NOTICE , LOG_WARNING } ;
const char * c = " .-*# " ;
time_t now = time ( NULL ) ;
2010-06-21 18:07:48 -04:00
FILE * fp ;
2010-07-22 17:31:40 -04:00
char buf [ 64 ] ;
2011-04-13 04:58:21 -04:00
int rawmode = ( level & REDIS_LOG_RAW ) ;
2010-07-22 17:31:40 -04:00
2011-04-13 04:58:21 -04:00
level & = 0xff ; /* clear flags */
2010-07-22 17:31:40 -04:00
if ( level < server . verbosity ) return ;
2010-06-21 18:07:48 -04:00
fp = ( server . logfile = = NULL ) ? stdout : fopen ( server . logfile , " a " ) ;
if ( ! fp ) return ;
2011-04-13 04:58:21 -04:00
if ( rawmode ) {
fprintf ( fp , " %s " , msg ) ;
} else {
strftime ( buf , sizeof ( buf ) , " %d %b %H:%M:%S " , localtime ( & now ) ) ;
fprintf ( fp , " [%d] %s %c %s \n " , ( int ) getpid ( ) , buf , c [ level ] , msg ) ;
}
2010-12-09 11:10:21 -05:00
fflush ( fp ) ;
2010-06-21 18:07:48 -04:00
if ( server . logfile ) fclose ( fp ) ;
2010-12-09 11:10:21 -05:00
if ( server . syslog_enabled ) syslog ( syslogLevelMap [ level ] , " %s " , msg ) ;
2010-06-21 18:07:48 -04:00
}
2011-02-09 04:10:35 -05:00
/* Like redisLogRaw() but with printf-alike support. This is the funciton that
* is used across the code . The raw version is only used in order to dump
* the INFO output on crash . */
void redisLog ( int level , const char * fmt , . . . ) {
va_list ap ;
char msg [ REDIS_MAX_LOGMSG_LEN ] ;
2011-04-13 04:58:21 -04:00
if ( ( level & 0xff ) < server . verbosity ) return ;
2011-02-09 04:10:35 -05:00
va_start ( ap , fmt ) ;
vsnprintf ( msg , sizeof ( msg ) , fmt , ap ) ;
va_end ( ap ) ;
redisLogRaw ( level , msg ) ;
}
2010-06-21 18:07:48 -04:00
/* Redis generally does not try to recover from out of memory conditions
* when allocating objects or strings , it is not clear if it will be possible
* to report this condition to the client since the networking layer itself
* is based on heap allocation for send buffers , so we simply abort .
* At least the code will be simpler to read . . . */
void oom ( const char * msg ) {
redisLog ( REDIS_WARNING , " %s: Out of memory \n " , msg ) ;
sleep ( 1 ) ;
abort ( ) ;
}
2011-01-23 05:46:34 -05:00
/* Return the UNIX time in microseconds */
long long ustime ( void ) {
struct timeval tv ;
long long ust ;
gettimeofday ( & tv , NULL ) ;
ust = ( ( long long ) tv . tv_sec ) * 1000000 ;
ust + = tv . tv_usec ;
return ust ;
}
2010-06-21 18:07:48 -04:00
/*====================== Hash table type implementation ==================== */
/* This is an hash table type that uses the SDS dynamic strings libary as
* keys and radis objects as values ( objects can hold SDS strings ,
* lists , sets ) . */
void dictVanillaFree ( void * privdata , void * val )
{
DICT_NOTUSED ( privdata ) ;
zfree ( val ) ;
}
void dictListDestructor ( void * privdata , void * val )
{
DICT_NOTUSED ( privdata ) ;
listRelease ( ( list * ) val ) ;
}
int dictSdsKeyCompare ( void * privdata , const void * key1 ,
const void * key2 )
{
int l1 , l2 ;
DICT_NOTUSED ( privdata ) ;
l1 = sdslen ( ( sds ) key1 ) ;
l2 = sdslen ( ( sds ) key2 ) ;
if ( l1 ! = l2 ) return 0 ;
return memcmp ( key1 , key2 , l1 ) = = 0 ;
}
2010-11-03 06:23:59 -04:00
/* A case insensitive version used for the command lookup table. */
int dictSdsKeyCaseCompare ( void * privdata , const void * key1 ,
const void * key2 )
{
DICT_NOTUSED ( privdata ) ;
return strcasecmp ( key1 , key2 ) = = 0 ;
}
2010-06-21 18:07:48 -04:00
void dictRedisObjectDestructor ( void * privdata , void * val )
{
DICT_NOTUSED ( privdata ) ;
if ( val = = NULL ) return ; /* Values of swapped out keys as set to NULL */
decrRefCount ( val ) ;
}
void dictSdsDestructor ( void * privdata , void * val )
{
DICT_NOTUSED ( privdata ) ;
sdsfree ( val ) ;
}
int dictObjKeyCompare ( void * privdata , const void * key1 ,
const void * key2 )
{
const robj * o1 = key1 , * o2 = key2 ;
return dictSdsKeyCompare ( privdata , o1 - > ptr , o2 - > ptr ) ;
}
unsigned int dictObjHash ( const void * key ) {
const robj * o = key ;
return dictGenHashFunction ( o - > ptr , sdslen ( ( sds ) o - > ptr ) ) ;
}
unsigned int dictSdsHash ( const void * key ) {
return dictGenHashFunction ( ( unsigned char * ) key , sdslen ( ( char * ) key ) ) ;
}
2010-11-03 06:23:59 -04:00
unsigned int dictSdsCaseHash ( const void * key ) {
return dictGenCaseHashFunction ( ( unsigned char * ) key , sdslen ( ( char * ) key ) ) ;
}
2010-06-21 18:07:48 -04:00
int dictEncObjKeyCompare ( void * privdata , const void * key1 ,
const void * key2 )
{
robj * o1 = ( robj * ) key1 , * o2 = ( robj * ) key2 ;
int cmp ;
if ( o1 - > encoding = = REDIS_ENCODING_INT & &
o2 - > encoding = = REDIS_ENCODING_INT )
return o1 - > ptr = = o2 - > ptr ;
o1 = getDecodedObject ( o1 ) ;
o2 = getDecodedObject ( o2 ) ;
cmp = dictSdsKeyCompare ( privdata , o1 - > ptr , o2 - > ptr ) ;
decrRefCount ( o1 ) ;
decrRefCount ( o2 ) ;
return cmp ;
}
unsigned int dictEncObjHash ( const void * key ) {
robj * o = ( robj * ) key ;
if ( o - > encoding = = REDIS_ENCODING_RAW ) {
return dictGenHashFunction ( o - > ptr , sdslen ( ( sds ) o - > ptr ) ) ;
} else {
if ( o - > encoding = = REDIS_ENCODING_INT ) {
char buf [ 32 ] ;
int len ;
len = ll2string ( buf , 32 , ( long ) o - > ptr ) ;
return dictGenHashFunction ( ( unsigned char * ) buf , len ) ;
} else {
unsigned int hash ;
o = getDecodedObject ( o ) ;
hash = dictGenHashFunction ( o - > ptr , sdslen ( ( sds ) o - > ptr ) ) ;
decrRefCount ( o ) ;
return hash ;
}
}
}
2010-12-31 11:32:59 -05:00
/* Sets type and diskstore negative caching hash table */
2010-06-21 18:07:48 -04:00
dictType setDictType = {
dictEncObjHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictEncObjKeyCompare , /* key compare */
dictRedisObjectDestructor , /* key destructor */
NULL /* val destructor */
} ;
/* Sorted sets hash (note: a skiplist is used in addition to the hash table) */
dictType zsetDictType = {
dictEncObjHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictEncObjKeyCompare , /* key compare */
dictRedisObjectDestructor , /* key destructor */
2010-08-03 14:49:53 -04:00
NULL /* val destructor */
2010-06-21 18:07:48 -04:00
} ;
/* Db->dict, keys are sds strings, vals are Redis objects. */
dictType dbDictType = {
dictSdsHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictSdsKeyCompare , /* key compare */
dictSdsDestructor , /* key destructor */
dictRedisObjectDestructor /* val destructor */
} ;
/* Db->expires */
dictType keyptrDictType = {
dictSdsHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictSdsKeyCompare , /* key compare */
NULL , /* key destructor */
NULL /* val destructor */
} ;
2010-11-03 06:23:59 -04:00
/* Command table. sds string -> command struct pointer. */
dictType commandTableDictType = {
dictSdsCaseHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictSdsKeyCaseCompare , /* key compare */
dictSdsDestructor , /* key destructor */
NULL /* val destructor */
} ;
2010-06-21 18:07:48 -04:00
/* Hash type hash table (note that small hashes are represented with zimpaps) */
dictType hashDictType = {
dictEncObjHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictEncObjKeyCompare , /* key compare */
dictRedisObjectDestructor , /* key destructor */
dictRedisObjectDestructor /* val destructor */
} ;
/* Keylist hash table type has unencoded redis objects as keys and
* lists as values . It ' s used for blocking operations ( BLPOP ) and to
* map swapped keys to a list of clients waiting for this keys to be loaded . */
dictType keylistDictType = {
dictObjHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictObjKeyCompare , /* key compare */
dictRedisObjectDestructor , /* key destructor */
dictListDestructor /* val destructor */
} ;
2011-03-29 11:51:15 -04:00
/* Cluster nodes hash table, mapping nodes addresses 1.2.3.4:6379 to
* clusterNode structures . */
dictType clusterNodesDictType = {
dictSdsHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictSdsKeyCompare , /* key compare */
dictSdsDestructor , /* key destructor */
NULL /* val destructor */
} ;
2010-06-21 18:07:48 -04:00
int htNeedsResize ( dict * dict ) {
long long size , used ;
size = dictSlots ( dict ) ;
used = dictSize ( dict ) ;
return ( size & & used & & size > DICT_HT_INITIAL_SIZE & &
( used * 100 / size < REDIS_HT_MINFILL ) ) ;
}
/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
* we resize the hash table to save memory */
void tryResizeHashTables ( void ) {
int j ;
for ( j = 0 ; j < server . dbnum ; j + + ) {
if ( htNeedsResize ( server . db [ j ] . dict ) )
dictResize ( server . db [ j ] . dict ) ;
if ( htNeedsResize ( server . db [ j ] . expires ) )
dictResize ( server . db [ j ] . expires ) ;
}
}
/* Our hash table implementation performs rehashing incrementally while
* we write / read from the hash table . Still if the server is idle , the hash
* table will use two tables for a long time . So we try to use 1 millisecond
* of CPU time at every serverCron ( ) loop in order to rehash some key . */
void incrementallyRehash ( void ) {
int j ;
for ( j = 0 ; j < server . dbnum ; j + + ) {
if ( dictIsRehashing ( server . db [ j ] . dict ) ) {
dictRehashMilliseconds ( server . db [ j ] . dict , 1 ) ;
break ; /* already used our millisecond for this loop... */
}
}
}
/* This function is called once a background process of some kind terminates,
* as we want to avoid resizing the hash tables when there is a child in order
* to play well with copy - on - write ( otherwise when a resize happens lots of
* memory pages are copied ) . The goal of this function is to update the ability
* for dict . c to resize the hash tables accordingly to the fact we have o not
* running childs . */
void updateDictResizePolicy ( void ) {
if ( server . bgsavechildpid = = - 1 & & server . bgrewritechildpid = = - 1 )
dictEnableResize ( ) ;
else
dictDisableResize ( ) ;
}
/* ======================= Cron: called every 100 ms ======================== */
2010-08-02 12:13:39 -04:00
/* Try to expire a few timed out keys. The algorithm used is adaptive and
* will use few CPU cycles if there are few expiring keys , otherwise
* it will get more aggressive to avoid that too much memory is used by
* keys that can be removed from the keyspace . */
void activeExpireCycle ( void ) {
int j ;
for ( j = 0 ; j < server . dbnum ; j + + ) {
int expired ;
redisDb * db = server . db + j ;
/* Continue to expire if at the end of the cycle more than 25%
* of the keys were expired . */
do {
long num = dictSize ( db - > expires ) ;
time_t now = time ( NULL ) ;
expired = 0 ;
if ( num > REDIS_EXPIRELOOKUPS_PER_CRON )
num = REDIS_EXPIRELOOKUPS_PER_CRON ;
while ( num - - ) {
dictEntry * de ;
time_t t ;
if ( ( de = dictGetRandomKey ( db - > expires ) ) = = NULL ) break ;
t = ( time_t ) dictGetEntryVal ( de ) ;
if ( now > t ) {
sds key = dictGetEntryKey ( de ) ;
robj * keyobj = createStringObject ( key , sdslen ( key ) ) ;
propagateExpire ( db , keyobj ) ;
dbDelete ( db , keyobj ) ;
decrRefCount ( keyobj ) ;
expired + + ;
server . stat_expiredkeys + + ;
}
}
} while ( expired > REDIS_EXPIRELOOKUPS_PER_CRON / 4 ) ;
}
}
2010-10-14 15:22:21 -04:00
void updateLRUClock ( void ) {
server . lruclock = ( time ( NULL ) / REDIS_LRU_CLOCK_RESOLUTION ) &
REDIS_LRU_CLOCK_MAX ;
}
2010-08-02 12:13:39 -04:00
2010-06-21 18:07:48 -04:00
int serverCron ( struct aeEventLoop * eventLoop , long long id , void * clientData ) {
2011-01-20 07:18:23 -05:00
int j , loops = server . cronloops ;
2010-06-21 18:07:48 -04:00
REDIS_NOTUSED ( eventLoop ) ;
REDIS_NOTUSED ( id ) ;
REDIS_NOTUSED ( clientData ) ;
/* We take a cached value of the unix time in the global state because
* with virtual memory and aging there is to store the current time
* in objects at every object access , and accuracy is not needed .
* To access a global var is faster than calling time ( NULL ) */
server . unixtime = time ( NULL ) ;
2010-10-14 07:52:58 -04:00
/* We have just 22 bits per object for LRU information.
2010-10-14 15:22:21 -04:00
* So we use an ( eventually wrapping ) LRU clock with 10 seconds resolution .
* 2 ^ 22 bits with 10 seconds resoluton is more or less 1.5 years .
2010-06-21 18:07:48 -04:00
*
2010-10-14 15:22:21 -04:00
* Note that even if this will wrap after 1.5 years it ' s not a problem ,
2010-10-14 07:52:58 -04:00
* everything will still work but just some object will appear younger
2010-10-14 15:22:21 -04:00
* to Redis . But for this to happen a given object should never be touched
* for 1.5 years .
*
* Note that you can change the resolution altering the
* REDIS_LRU_CLOCK_RESOLUTION define .
2010-06-21 18:07:48 -04:00
*/
2010-10-14 15:22:21 -04:00
updateLRUClock ( ) ;
2010-06-21 18:07:48 -04:00
2011-04-21 04:49:52 -04:00
/* Record the max memory used since the server was started. */
if ( zmalloc_used_memory ( ) > server . stat_peak_memory )
server . stat_peak_memory = zmalloc_used_memory ( ) ;
2010-06-21 18:07:48 -04:00
/* We received a SIGTERM, shutting down here in a safe way, as it is
* not ok doing so inside the signal handler . */
if ( server . shutdown_asap ) {
if ( prepareForShutdown ( ) = = REDIS_OK ) exit ( 0 ) ;
redisLog ( REDIS_WARNING , " SIGTERM received but errors trying to shut down the server, check the logs for more information " ) ;
}
/* Show some info about non-empty databases */
for ( j = 0 ; j < server . dbnum ; j + + ) {
long long size , used , vkeys ;
size = dictSlots ( server . db [ j ] . dict ) ;
used = dictSize ( server . db [ j ] . dict ) ;
vkeys = dictSize ( server . db [ j ] . expires ) ;
if ( ! ( loops % 50 ) & & ( used | | vkeys ) ) {
redisLog ( REDIS_VERBOSE , " DB %d: %lld keys (%lld volatile) in %lld slots HT. " , j , used , vkeys , size ) ;
/* dictPrintStats(server.dict); */
}
}
/* We don't want to resize the hash tables while a bacground saving
* is in progress : the saving child is created using fork ( ) that is
* implemented with a copy - on - write semantic in most modern systems , so
* if we resize the HT while there is the saving child at work actually
* a lot of memory movements in the parent will cause a lot of pages
* copied . */
if ( server . bgsavechildpid = = - 1 & & server . bgrewritechildpid = = - 1 ) {
if ( ! ( loops % 10 ) ) tryResizeHashTables ( ) ;
if ( server . activerehashing ) incrementallyRehash ( ) ;
}
/* Show information about connected clients */
if ( ! ( loops % 50 ) ) {
redisLog ( REDIS_VERBOSE , " %d clients connected (%d slaves), %zu bytes in use " ,
listLength ( server . clients ) - listLength ( server . slaves ) ,
listLength ( server . slaves ) ,
2010-11-02 07:09:59 -04:00
zmalloc_used_memory ( ) ) ;
2010-06-21 18:07:48 -04:00
}
/* Close connections of timedout clients */
2010-12-06 08:05:01 -05:00
if ( ( server . maxidletime & & ! ( loops % 100 ) ) | | server . bpop_blocked_clients )
2010-06-21 18:07:48 -04:00
closeTimedoutClients ( ) ;
2011-06-10 06:39:23 -04:00
/* Start a scheduled AOF rewrite if this was requested by the user while
* a BGSAVE was in progress . */
if ( server . bgsavechildpid = = - 1 & & server . bgrewritechildpid = = - 1 & &
server . aofrewrite_scheduled )
{
rewriteAppendOnlyFileBackground ( ) ;
}
2011-01-07 17:41:00 -05:00
/* Check if a background saving or AOF rewrite in progress terminated. */
2010-06-21 18:07:48 -04:00
if ( server . bgsavechildpid ! = - 1 | | server . bgrewritechildpid ! = - 1 ) {
int statloc ;
pid_t pid ;
if ( ( pid = wait3 ( & statloc , WNOHANG , NULL ) ) ! = 0 ) {
2011-01-07 12:15:14 -05:00
int exitcode = WEXITSTATUS ( statloc ) ;
int bysignal = 0 ;
if ( WIFSIGNALED ( statloc ) ) bysignal = WTERMSIG ( statloc ) ;
2010-06-21 18:07:48 -04:00
if ( pid = = server . bgsavechildpid ) {
2011-01-07 12:15:14 -05:00
backgroundSaveDoneHandler ( exitcode , bysignal ) ;
2010-06-21 18:07:48 -04:00
} else {
2011-01-07 12:15:14 -05:00
backgroundRewriteDoneHandler ( exitcode , bysignal ) ;
2010-06-21 18:07:48 -04:00
}
updateDictResizePolicy ( ) ;
}
2011-01-07 17:41:00 -05:00
} else if ( server . bgsavethread ! = ( pthread_t ) - 1 ) {
2011-01-07 12:15:14 -05:00
if ( server . bgsavethread ! = ( pthread_t ) - 1 ) {
int state ;
pthread_mutex_lock ( & server . bgsavethread_mutex ) ;
state = server . bgsavethread_state ;
pthread_mutex_unlock ( & server . bgsavethread_mutex ) ;
2011-01-07 13:31:42 -05:00
if ( state = = REDIS_BGSAVE_THREAD_DONE_OK | |
state = = REDIS_BGSAVE_THREAD_DONE_ERR )
2011-01-07 12:15:14 -05:00
{
backgroundSaveDoneHandler (
2011-01-07 13:31:42 -05:00
( state = = REDIS_BGSAVE_THREAD_DONE_OK ) ? 0 : 1 , 0 ) ;
2011-01-07 12:15:14 -05:00
}
}
2010-12-30 11:53:28 -05:00
} else if ( ! server . ds_enabled ) {
2010-06-21 18:07:48 -04:00
time_t now = time ( NULL ) ;
2011-06-10 06:39:23 -04:00
/* If there is not a background saving/rewrite in progress check if
* we have to save / rewrite now */
2010-06-21 18:07:48 -04:00
for ( j = 0 ; j < server . saveparamslen ; j + + ) {
struct saveparam * sp = server . saveparams + j ;
if ( server . dirty > = sp - > changes & &
now - server . lastsave > sp - > seconds ) {
redisLog ( REDIS_NOTICE , " %d changes in %d seconds. Saving... " ,
sp - > changes , sp - > seconds ) ;
rdbSaveBackground ( server . dbfilename ) ;
break ;
}
}
2011-06-10 06:39:23 -04:00
/* Trigger an AOF rewrite if needed */
if ( server . auto_aofrewrite_perc & &
server . appendonly_current_size > server . auto_aofrewrite_min_size )
{
2011-06-10 08:25:27 -04:00
int base = server . auto_aofrewrite_base_size ?
server . auto_aofrewrite_base_size : 1 ;
int growth = ( server . appendonly_current_size * 100 / base ) ;
2011-06-10 06:39:23 -04:00
if ( growth > = server . auto_aofrewrite_perc ) {
redisLog ( REDIS_NOTICE , " Starting automatic rewriting of AOF on %d growth " , growth ) ;
rewriteAppendOnlyFileBackground ( ) ;
}
}
2010-06-21 18:07:48 -04:00
}
2010-08-02 12:13:39 -04:00
/* Expire a few keys per cycle, only if this is a master.
* On slaves we wait for DEL operations synthesized by the master
* in order to guarantee a strict consistency . */
if ( server . masterhost = = NULL ) activeExpireCycle ( ) ;
2010-06-21 18:07:48 -04:00
2010-12-28 09:20:20 -05:00
/* Remove a few cached objects from memory if we are over the
* configured memory limit */
2010-12-29 16:18:20 -05:00
if ( server . ds_enabled ) cacheCron ( ) ;
2010-06-21 18:07:48 -04:00
2010-11-04 12:29:53 -04:00
/* Replication cron function -- used to reconnect to master and
* to detect transfer failures . */
2010-11-04 12:35:03 -04:00
if ( ! ( loops % 10 ) ) replicationCron ( ) ;
2010-11-04 12:29:53 -04:00
2011-03-29 11:51:15 -04:00
/* Run other sub-systems specific cron jobs */
if ( server . cluster_enabled & & ! ( loops % 10 ) ) clusterCron ( ) ;
2011-01-20 07:18:23 -05:00
server . cronloops + + ;
2010-06-21 18:07:48 -04:00
return 100 ;
}
/* This function gets called every time Redis is entering the
* main loop of the event driven library , that is , before to sleep
* for ready file descriptors . */
void beforeSleep ( struct aeEventLoop * eventLoop ) {
REDIS_NOTUSED ( eventLoop ) ;
2010-12-06 10:39:39 -05:00
listNode * ln ;
redisClient * c ;
2010-06-21 18:07:48 -04:00
2010-12-28 09:20:20 -05:00
/* Awake clients that got all the on disk keys they requested */
if ( server . ds_enabled & & listLength ( server . io_ready_clients ) ) {
2010-06-21 18:07:48 -04:00
listIter li ;
listRewind ( server . io_ready_clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2010-12-06 10:39:39 -05:00
c = ln - > value ;
2010-06-21 18:07:48 -04:00
struct redisCommand * cmd ;
/* Resume the client. */
listDelNode ( server . io_ready_clients , ln ) ;
c - > flags & = ( ~ REDIS_IO_WAIT ) ;
2010-12-28 09:20:20 -05:00
server . cache_blocked_clients - - ;
2010-06-21 18:07:48 -04:00
aeCreateFileEvent ( server . el , c - > fd , AE_READABLE ,
readQueryFromClient , c ) ;
cmd = lookupCommand ( c - > argv [ 0 ] - > ptr ) ;
redisAssert ( cmd ! = NULL ) ;
call ( c , cmd ) ;
resetClient ( c ) ;
/* There may be more data to process in the input buffer. */
if ( c - > querybuf & & sdslen ( c - > querybuf ) > 0 )
processInputBuffer ( c ) ;
}
}
2010-12-06 10:39:39 -05:00
/* Try to process pending commands for clients that were just unblocked. */
while ( listLength ( server . unblocked_clients ) ) {
ln = listFirst ( server . unblocked_clients ) ;
redisAssert ( ln ! = NULL ) ;
c = ln - > value ;
listDelNode ( server . unblocked_clients , ln ) ;
2011-01-17 04:03:21 -05:00
c - > flags & = ~ REDIS_UNBLOCKED ;
2010-12-06 10:39:39 -05:00
/* Process remaining data in the input buffer. */
if ( c - > querybuf & & sdslen ( c - > querybuf ) > 0 )
processInputBuffer ( c ) ;
}
2010-06-21 18:07:48 -04:00
/* Write the AOF buffer on disk */
flushAppendOnlyFile ( ) ;
}
/* =========================== Server initialization ======================== */
void createSharedObjects ( void ) {
int j ;
shared . crlf = createObject ( REDIS_STRING , sdsnew ( " \r \n " ) ) ;
shared . ok = createObject ( REDIS_STRING , sdsnew ( " +OK \r \n " ) ) ;
shared . err = createObject ( REDIS_STRING , sdsnew ( " -ERR \r \n " ) ) ;
shared . emptybulk = createObject ( REDIS_STRING , sdsnew ( " $0 \r \n \r \n " ) ) ;
shared . czero = createObject ( REDIS_STRING , sdsnew ( " :0 \r \n " ) ) ;
shared . cone = createObject ( REDIS_STRING , sdsnew ( " :1 \r \n " ) ) ;
shared . cnegone = createObject ( REDIS_STRING , sdsnew ( " :-1 \r \n " ) ) ;
shared . nullbulk = createObject ( REDIS_STRING , sdsnew ( " $-1 \r \n " ) ) ;
shared . nullmultibulk = createObject ( REDIS_STRING , sdsnew ( " *-1 \r \n " ) ) ;
shared . emptymultibulk = createObject ( REDIS_STRING , sdsnew ( " *0 \r \n " ) ) ;
shared . pong = createObject ( REDIS_STRING , sdsnew ( " +PONG \r \n " ) ) ;
shared . queued = createObject ( REDIS_STRING , sdsnew ( " +QUEUED \r \n " ) ) ;
shared . wrongtypeerr = createObject ( REDIS_STRING , sdsnew (
" -ERR Operation against a key holding the wrong kind of value \r \n " ) ) ;
shared . nokeyerr = createObject ( REDIS_STRING , sdsnew (
" -ERR no such key \r \n " ) ) ;
shared . syntaxerr = createObject ( REDIS_STRING , sdsnew (
" -ERR syntax error \r \n " ) ) ;
shared . sameobjecterr = createObject ( REDIS_STRING , sdsnew (
" -ERR source and destination objects are the same \r \n " ) ) ;
shared . outofrangeerr = createObject ( REDIS_STRING , sdsnew (
" -ERR index out of range \r \n " ) ) ;
2010-11-08 05:52:03 -05:00
shared . loadingerr = createObject ( REDIS_STRING , sdsnew (
" -LOADING Redis is loading the dataset in memory \r \n " ) ) ;
2010-06-21 18:07:48 -04:00
shared . space = createObject ( REDIS_STRING , sdsnew ( " " ) ) ;
shared . colon = createObject ( REDIS_STRING , sdsnew ( " : " ) ) ;
shared . plus = createObject ( REDIS_STRING , sdsnew ( " + " ) ) ;
shared . select0 = createStringObject ( " select 0 \r \n " , 10 ) ;
shared . select1 = createStringObject ( " select 1 \r \n " , 10 ) ;
shared . select2 = createStringObject ( " select 2 \r \n " , 10 ) ;
shared . select3 = createStringObject ( " select 3 \r \n " , 10 ) ;
shared . select4 = createStringObject ( " select 4 \r \n " , 10 ) ;
shared . select5 = createStringObject ( " select 5 \r \n " , 10 ) ;
shared . select6 = createStringObject ( " select 6 \r \n " , 10 ) ;
shared . select7 = createStringObject ( " select 7 \r \n " , 10 ) ;
shared . select8 = createStringObject ( " select 8 \r \n " , 10 ) ;
shared . select9 = createStringObject ( " select 9 \r \n " , 10 ) ;
shared . messagebulk = createStringObject ( " $7 \r \n message \r \n " , 13 ) ;
shared . pmessagebulk = createStringObject ( " $8 \r \n pmessage \r \n " , 14 ) ;
shared . subscribebulk = createStringObject ( " $9 \r \n subscribe \r \n " , 15 ) ;
shared . unsubscribebulk = createStringObject ( " $11 \r \n unsubscribe \r \n " , 18 ) ;
shared . psubscribebulk = createStringObject ( " $10 \r \n psubscribe \r \n " , 17 ) ;
shared . punsubscribebulk = createStringObject ( " $12 \r \n punsubscribe \r \n " , 19 ) ;
shared . mbulk3 = createStringObject ( " *3 \r \n " , 4 ) ;
shared . mbulk4 = createStringObject ( " *4 \r \n " , 4 ) ;
for ( j = 0 ; j < REDIS_SHARED_INTEGERS ; j + + ) {
shared . integers [ j ] = createObject ( REDIS_STRING , ( void * ) ( long ) j ) ;
shared . integers [ j ] - > encoding = REDIS_ENCODING_INT ;
}
}
void initServerConfig ( ) {
server . port = REDIS_SERVERPORT ;
2010-08-03 07:33:12 -04:00
server . bindaddr = NULL ;
2010-10-13 11:17:56 -04:00
server . unixsocket = NULL ;
2010-08-03 07:33:12 -04:00
server . ipfd = - 1 ;
server . sofd = - 1 ;
server . dbnum = REDIS_DEFAULT_DBNUM ;
2010-06-21 18:07:48 -04:00
server . verbosity = REDIS_VERBOSE ;
server . maxidletime = REDIS_MAXIDLETIME ;
server . saveparams = NULL ;
2010-11-08 05:52:03 -05:00
server . loading = 0 ;
2010-06-21 18:07:48 -04:00
server . logfile = NULL ; /* NULL = log on standard output */
2010-12-09 11:10:21 -05:00
server . syslog_enabled = 0 ;
server . syslog_ident = zstrdup ( " redis " ) ;
server . syslog_facility = LOG_LOCAL0 ;
2010-06-21 18:07:48 -04:00
server . daemonize = 0 ;
server . appendonly = 0 ;
server . appendfsync = APPENDFSYNC_EVERYSEC ;
server . no_appendfsync_on_rewrite = 0 ;
2011-06-10 06:39:23 -04:00
server . auto_aofrewrite_perc = REDIS_AUTO_AOFREWRITE_PERC ;
server . auto_aofrewrite_min_size = REDIS_AUTO_AOFREWRITE_MIN_SIZE ;
server . auto_aofrewrite_base_size = 0 ;
server . aofrewrite_scheduled = 0 ;
2010-06-21 18:07:48 -04:00
server . lastfsync = time ( NULL ) ;
server . appendfd = - 1 ;
server . appendseldb = - 1 ; /* Make sure the first time will not match */
server . pidfile = zstrdup ( " /var/run/redis.pid " ) ;
server . dbfilename = zstrdup ( " dump.rdb " ) ;
server . appendfilename = zstrdup ( " appendonly.aof " ) ;
server . requirepass = NULL ;
server . rdbcompression = 1 ;
server . activerehashing = 1 ;
server . maxclients = 0 ;
2010-12-06 08:05:01 -05:00
server . bpop_blocked_clients = 0 ;
2010-06-21 18:07:48 -04:00
server . maxmemory = 0 ;
2010-10-14 15:22:21 -04:00
server . maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU ;
server . maxmemory_samples = 3 ;
2010-12-28 09:20:20 -05:00
server . ds_enabled = 0 ;
2010-12-30 10:41:36 -05:00
server . ds_path = sdsnew ( " /tmp/redis.ds " ) ;
2010-12-28 09:20:20 -05:00
server . cache_max_memory = 64LL * 1024 * 1024 ; /* 64 MB of RAM */
server . cache_blocked_clients = 0 ;
2010-06-21 18:07:48 -04:00
server . hash_max_zipmap_entries = REDIS_HASH_MAX_ZIPMAP_ENTRIES ;
server . hash_max_zipmap_value = REDIS_HASH_MAX_ZIPMAP_VALUE ;
server . list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES ;
server . list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE ;
2010-07-02 13:57:12 -04:00
server . set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES ;
2011-03-09 08:01:57 -05:00
server . zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES ;
server . zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE ;
2010-06-21 18:07:48 -04:00
server . shutdown_asap = 0 ;
2011-01-02 16:27:26 -05:00
server . cache_flush_delay = 0 ;
2011-03-29 11:51:15 -04:00
server . cluster_enabled = 0 ;
2011-03-30 11:41:13 -04:00
server . cluster . configfile = zstrdup ( " nodes.conf " ) ;
2010-06-21 18:07:48 -04:00
2010-10-15 06:22:48 -04:00
updateLRUClock ( ) ;
2010-06-21 18:07:48 -04:00
resetServerSaveParams ( ) ;
appendServerSaveParams ( 60 * 60 , 1 ) ; /* save after 1 hour and 1 change */
appendServerSaveParams ( 300 , 100 ) ; /* save after 5 minutes and 100 changes */
appendServerSaveParams ( 60 , 10000 ) ; /* save after 1 minute and 10000 changes */
/* Replication related */
server . isslave = 0 ;
server . masterauth = NULL ;
server . masterhost = NULL ;
server . masterport = 6379 ;
server . master = NULL ;
server . replstate = REDIS_REPL_NONE ;
2011-05-22 06:57:30 -04:00
server . repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT ;
2010-11-04 14:59:21 -04:00
server . repl_serve_stale_data = 1 ;
2010-06-21 18:07:48 -04:00
/* Double constants initialization */
R_Zero = 0.0 ;
R_PosInf = 1.0 / R_Zero ;
R_NegInf = - 1.0 / R_Zero ;
R_Nan = R_Zero / R_Zero ;
2010-11-03 07:14:36 -04:00
/* Command table -- we intiialize it here as it is part of the
* initial configuration , since command names may be changed via
* redis . conf using the rename - command directive . */
server . commands = dictCreate ( & commandTableDictType , NULL ) ;
populateCommandTable ( ) ;
server . delCommand = lookupCommandByCString ( " del " ) ;
server . multiCommand = lookupCommandByCString ( " multi " ) ;
2010-06-21 18:07:48 -04:00
}
void initServer ( ) {
int j ;
signal ( SIGHUP , SIG_IGN ) ;
signal ( SIGPIPE , SIG_IGN ) ;
2011-03-06 11:49:22 -05:00
setupSignalHandlers ( ) ;
2010-06-21 18:07:48 -04:00
2010-12-09 11:10:21 -05:00
if ( server . syslog_enabled ) {
openlog ( server . syslog_ident , LOG_PID | LOG_NDELAY | LOG_NOWAIT ,
server . syslog_facility ) ;
}
2010-07-22 07:08:02 -04:00
server . mainthread = pthread_self ( ) ;
2010-06-21 18:07:48 -04:00
server . clients = listCreate ( ) ;
server . slaves = listCreate ( ) ;
server . monitors = listCreate ( ) ;
2010-12-06 10:39:39 -05:00
server . unblocked_clients = listCreate ( ) ;
2011-01-01 15:35:56 -05:00
server . cache_io_queue = listCreate ( ) ;
2010-12-29 13:39:42 -05:00
2010-06-21 18:07:48 -04:00
createSharedObjects ( ) ;
server . el = aeCreateEventLoop ( ) ;
server . db = zmalloc ( sizeof ( redisDb ) * server . dbnum ) ;
2011-02-07 06:52:01 -05:00
2011-02-07 07:43:29 -05:00
if ( server . port ! = 0 ) {
2011-02-07 06:52:01 -05:00
server . ipfd = anetTcpServer ( server . neterr , server . port , server . bindaddr ) ;
2011-02-07 07:43:29 -05:00
if ( server . ipfd = = ANET_ERR ) {
redisLog ( REDIS_WARNING , " Opening port: %s " , server . neterr ) ;
exit ( 1 ) ;
}
2010-08-03 07:33:12 -04:00
}
2010-10-13 11:17:56 -04:00
if ( server . unixsocket ! = NULL ) {
unlink ( server . unixsocket ) ; /* don't care if this fails */
server . sofd = anetUnixServer ( server . neterr , server . unixsocket ) ;
2010-08-03 07:33:12 -04:00
if ( server . sofd = = ANET_ERR ) {
redisLog ( REDIS_WARNING , " Opening socket: %s " , server . neterr ) ;
exit ( 1 ) ;
}
2010-08-01 16:55:24 -04:00
}
2010-08-03 07:33:12 -04:00
if ( server . ipfd < 0 & & server . sofd < 0 ) {
redisLog ( REDIS_WARNING , " Configured to not listen anywhere, exiting. " ) ;
2010-06-21 18:07:48 -04:00
exit ( 1 ) ;
}
for ( j = 0 ; j < server . dbnum ; j + + ) {
server . db [ j ] . dict = dictCreate ( & dbDictType , NULL ) ;
server . db [ j ] . expires = dictCreate ( & keyptrDictType , NULL ) ;
server . db [ j ] . blocking_keys = dictCreate ( & keylistDictType , NULL ) ;
server . db [ j ] . watched_keys = dictCreate ( & keylistDictType , NULL ) ;
2010-12-31 11:32:59 -05:00
if ( server . ds_enabled ) {
2010-06-21 18:07:48 -04:00
server . db [ j ] . io_keys = dictCreate ( & keylistDictType , NULL ) ;
2010-12-31 11:32:59 -05:00
server . db [ j ] . io_negcache = dictCreate ( & setDictType , NULL ) ;
2011-01-01 15:35:56 -05:00
server . db [ j ] . io_queued = dictCreate ( & setDictType , NULL ) ;
2010-12-31 11:32:59 -05:00
}
2010-06-21 18:07:48 -04:00
server . db [ j ] . id = j ;
}
server . pubsub_channels = dictCreate ( & keylistDictType , NULL ) ;
server . pubsub_patterns = listCreate ( ) ;
listSetFreeMethod ( server . pubsub_patterns , freePubsubPattern ) ;
listSetMatchMethod ( server . pubsub_patterns , listMatchPubsubPattern ) ;
server . cronloops = 0 ;
server . bgsavechildpid = - 1 ;
server . bgrewritechildpid = - 1 ;
2011-01-07 12:15:14 -05:00
server . bgsavethread_state = REDIS_BGSAVE_THREAD_UNACTIVE ;
server . bgsavethread = ( pthread_t ) - 1 ;
2010-06-21 18:07:48 -04:00
server . bgrewritebuf = sdsempty ( ) ;
server . aofbuf = sdsempty ( ) ;
server . lastsave = time ( NULL ) ;
server . dirty = 0 ;
server . stat_numcommands = 0 ;
server . stat_numconnections = 0 ;
server . stat_expiredkeys = 0 ;
2010-12-19 09:15:08 -05:00
server . stat_evictedkeys = 0 ;
2010-06-21 18:07:48 -04:00
server . stat_starttime = time ( NULL ) ;
2010-10-15 06:19:21 -04:00
server . stat_keyspace_misses = 0 ;
server . stat_keyspace_hits = 0 ;
2011-04-21 04:49:52 -04:00
server . stat_peak_memory = 0 ;
2011-05-29 09:17:29 -04:00
server . stat_fork_time = 0 ;
2010-06-21 18:07:48 -04:00
server . unixtime = time ( NULL ) ;
aeCreateTimeEvent ( server . el , 1 , serverCron , NULL , NULL ) ;
2010-08-03 07:33:12 -04:00
if ( server . ipfd > 0 & & aeCreateFileEvent ( server . el , server . ipfd , AE_READABLE ,
2010-10-13 12:34:24 -04:00
acceptTcpHandler , NULL ) = = AE_ERR ) oom ( " creating file event " ) ;
2010-08-03 07:33:12 -04:00
if ( server . sofd > 0 & & aeCreateFileEvent ( server . el , server . sofd , AE_READABLE ,
2010-10-13 12:34:24 -04:00
acceptUnixHandler , NULL ) = = AE_ERR ) oom ( " creating file event " ) ;
2010-06-21 18:07:48 -04:00
if ( server . appendonly ) {
server . appendfd = open ( server . appendfilename , O_WRONLY | O_APPEND | O_CREAT , 0644 ) ;
if ( server . appendfd = = - 1 ) {
redisLog ( REDIS_WARNING , " Can't open the append-only file: %s " ,
strerror ( errno ) ) ;
exit ( 1 ) ;
}
}
2010-12-28 09:20:20 -05:00
if ( server . ds_enabled ) dsInit ( ) ;
2011-03-29 11:51:15 -04:00
if ( server . cluster_enabled ) clusterInit ( ) ;
2011-02-21 18:03:03 -05:00
srand ( time ( NULL ) ^ getpid ( ) ) ;
2010-06-21 18:07:48 -04:00
}
2010-11-03 06:23:59 -04:00
/* Populates the Redis Command Table starting from the hard coded list
* we have on top of redis . c file . */
void populateCommandTable ( void ) {
int j ;
2011-01-24 04:56:06 -05:00
int numcommands = sizeof ( redisCommandTable ) / sizeof ( struct redisCommand ) ;
2010-11-03 06:23:59 -04:00
for ( j = 0 ; j < numcommands ; j + + ) {
2011-01-24 04:56:06 -05:00
struct redisCommand * c = redisCommandTable + j ;
2010-11-03 06:23:59 -04:00
int retval ;
2010-06-21 18:07:48 -04:00
2010-11-03 06:23:59 -04:00
retval = dictAdd ( server . commands , sdsnew ( c - > name ) , c ) ;
assert ( retval = = DICT_OK ) ;
}
2010-06-21 18:07:48 -04:00
}
2011-01-24 04:56:06 -05:00
void resetCommandTableStats ( void ) {
int numcommands = sizeof ( redisCommandTable ) / sizeof ( struct redisCommand ) ;
int j ;
for ( j = 0 ; j < numcommands ; j + + ) {
struct redisCommand * c = redisCommandTable + j ;
c - > microseconds = 0 ;
c - > calls = 0 ;
}
}
2010-06-21 18:07:48 -04:00
/* ====================== Commands lookup and execution ===================== */
2010-11-03 06:23:59 -04:00
struct redisCommand * lookupCommand ( sds name ) {
return dictFetchValue ( server . commands , name ) ;
}
struct redisCommand * lookupCommandByCString ( char * s ) {
struct redisCommand * cmd ;
sds name = sdsnew ( s ) ;
cmd = dictFetchValue ( server . commands , name ) ;
sdsfree ( name ) ;
return cmd ;
2010-06-21 18:07:48 -04:00
}
/* Call() is the core of Redis execution of a command */
void call ( redisClient * c , struct redisCommand * cmd ) {
2011-01-23 05:46:34 -05:00
long long dirty , start = ustime ( ) ;
2010-06-21 18:07:48 -04:00
dirty = server . dirty ;
cmd - > proc ( c ) ;
dirty = server . dirty - dirty ;
2011-01-23 05:46:34 -05:00
cmd - > microseconds + = ustime ( ) - start ;
2011-01-23 16:14:15 -05:00
cmd - > calls + + ;
2010-06-21 18:07:48 -04:00
if ( server . appendonly & & dirty )
feedAppendOnlyFile ( cmd , c - > db - > id , c - > argv , c - > argc ) ;
if ( ( dirty | | cmd - > flags & REDIS_CMD_FORCE_REPLICATION ) & &
listLength ( server . slaves ) )
replicationFeedSlaves ( server . slaves , c - > db - > id , c - > argv , c - > argc ) ;
if ( listLength ( server . monitors ) )
replicationFeedMonitors ( server . monitors , c - > db - > id , c - > argv , c - > argc ) ;
server . stat_numcommands + + ;
}
/* If this function gets called we already read a whole
* command , argments are in the client argv / argc fields .
* processCommand ( ) execute the command or prepare the
* server for a bulk read from the client .
*
* If 1 is returned the client is still alive and valid and
* and other operations can be performed by the caller . Otherwise
* if 0 is returned the client was destroied ( i . e . after QUIT ) . */
int processCommand ( redisClient * c ) {
struct redisCommand * cmd ;
2010-10-13 05:25:40 -04:00
/* The QUIT command is handled separately. Normal command procs will
* go through checking for replication and QUIT will cause trouble
* when FORCE_REPLICATION is enabled and would be implemented in
* a regular command proc . */
2010-06-21 18:07:48 -04:00
if ( ! strcasecmp ( c - > argv [ 0 ] - > ptr , " quit " ) ) {
2010-10-13 05:25:40 -04:00
addReply ( c , shared . ok ) ;
2010-10-28 10:07:45 -04:00
c - > flags | = REDIS_CLOSE_AFTER_REPLY ;
2010-10-15 09:40:25 -04:00
return REDIS_ERR ;
2010-06-21 18:07:48 -04:00
}
/* Now lookup the command and check ASAP about trivial error conditions
* such wrong arity , bad command name and so forth . */
cmd = lookupCommand ( c - > argv [ 0 ] - > ptr ) ;
if ( ! cmd ) {
2010-09-02 13:52:24 -04:00
addReplyErrorFormat ( c , " unknown command '%s' " ,
( char * ) c - > argv [ 0 ] - > ptr ) ;
2010-10-15 09:40:25 -04:00
return REDIS_OK ;
2010-06-21 18:07:48 -04:00
} else if ( ( cmd - > arity > 0 & & cmd - > arity ! = c - > argc ) | |
( c - > argc < - cmd - > arity ) ) {
2010-09-02 13:52:24 -04:00
addReplyErrorFormat ( c , " wrong number of arguments for '%s' command " ,
cmd - > name ) ;
2010-10-15 09:40:25 -04:00
return REDIS_OK ;
2010-06-21 18:07:48 -04:00
}
/* Check if the user is authenticated */
if ( server . requirepass & & ! c - > authenticated & & cmd - > proc ! = authCommand ) {
2010-09-02 13:52:24 -04:00
addReplyError ( c , " operation not permitted " ) ;
2010-10-15 09:40:25 -04:00
return REDIS_OK ;
2010-06-21 18:07:48 -04:00
}
2011-03-29 11:51:15 -04:00
/* If cluster is enabled, redirect here */
if ( server . cluster_enabled & &
! ( cmd - > getkeys_proc = = NULL & & cmd - > firstkey = = 0 ) ) {
int hashslot ;
if ( server . cluster . state ! = REDIS_CLUSTER_OK ) {
addReplyError ( c , " The cluster is down. Check with CLUSTER INFO for more information " ) ;
return REDIS_OK ;
} else {
2011-05-05 05:13:21 -04:00
int ask ;
clusterNode * n = getNodeByQuery ( c , cmd , c - > argv , c - > argc , & hashslot , & ask ) ;
2011-03-29 11:51:15 -04:00
if ( n = = NULL ) {
2011-05-05 05:13:21 -04:00
addReplyError ( c , " Multi keys request invalid in cluster " ) ;
2011-03-29 11:51:15 -04:00
return REDIS_OK ;
} else if ( n ! = server . cluster . myself ) {
addReplySds ( c , sdscatprintf ( sdsempty ( ) ,
2011-05-05 05:13:21 -04:00
" -%s %d %s:%d \r \n " , ask ? " ASK " : " MOVED " ,
hashslot , n - > ip , n - > port ) ) ;
2011-03-29 11:51:15 -04:00
return REDIS_OK ;
}
}
}
2010-10-11 07:05:09 -04:00
/* Handle the maxmemory directive.
*
* First we try to free some memory if possible ( if there are volatile
* keys in the dataset ) . If there are not the only thing we can do
* is returning an error . */
if ( server . maxmemory ) freeMemoryIfNeeded ( ) ;
2010-06-21 18:07:48 -04:00
if ( server . maxmemory & & ( cmd - > flags & REDIS_CMD_DENYOOM ) & &
2010-11-02 07:09:59 -04:00
zmalloc_used_memory ( ) > server . maxmemory )
2010-06-21 18:07:48 -04:00
{
2010-09-02 13:52:24 -04:00
addReplyError ( c , " command not allowed when used memory > 'maxmemory' " ) ;
2010-10-15 09:40:25 -04:00
return REDIS_OK ;
2010-06-21 18:07:48 -04:00
}
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
if ( ( dictSize ( c - > pubsub_channels ) > 0 | | listLength ( c - > pubsub_patterns ) > 0 )
& &
cmd - > proc ! = subscribeCommand & & cmd - > proc ! = unsubscribeCommand & &
cmd - > proc ! = psubscribeCommand & & cmd - > proc ! = punsubscribeCommand ) {
2010-09-02 13:52:24 -04:00
addReplyError ( c , " only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context " ) ;
2010-10-15 09:40:25 -04:00
return REDIS_OK ;
2010-06-21 18:07:48 -04:00
}
2010-11-04 14:59:21 -04:00
/* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and
* we are a slave with a broken link with master . */
if ( server . masterhost & & server . replstate ! = REDIS_REPL_CONNECTED & &
server . repl_serve_stale_data = = 0 & &
cmd - > proc ! = infoCommand & & cmd - > proc ! = slaveofCommand )
{
addReplyError ( c ,
" link with MASTER is down and slave-serve-stale-data is set to no " ) ;
return REDIS_OK ;
}
2010-11-08 05:52:03 -05:00
/* Loading DB? Return an error if the command is not INFO */
if ( server . loading & & cmd - > proc ! = infoCommand ) {
addReply ( c , shared . loadingerr ) ;
return REDIS_OK ;
}
2010-06-21 18:07:48 -04:00
/* Exec the command */
if ( c - > flags & REDIS_MULTI & &
cmd - > proc ! = execCommand & & cmd - > proc ! = discardCommand & &
cmd - > proc ! = multiCommand & & cmd - > proc ! = watchCommand )
{
queueMultiCommand ( c , cmd ) ;
addReply ( c , shared . queued ) ;
} else {
2010-12-28 09:20:20 -05:00
if ( server . ds_enabled & & blockClientOnSwappedKeys ( c , cmd ) )
return REDIS_ERR ;
2010-06-21 18:07:48 -04:00
call ( c , cmd ) ;
}
2010-10-15 09:40:25 -04:00
return REDIS_OK ;
2010-06-21 18:07:48 -04:00
}
/*================================== Shutdown =============================== */
int prepareForShutdown ( ) {
redisLog ( REDIS_WARNING , " User requested shutdown, saving DB... " ) ;
/* Kill the saving child if there is a background saving in progress.
We want to avoid race conditions , for instance our saving child may
overwrite the synchronous saving did by SHUTDOWN . */
if ( server . bgsavechildpid ! = - 1 ) {
redisLog ( REDIS_WARNING , " There is a live saving child. Killing it! " ) ;
kill ( server . bgsavechildpid , SIGKILL ) ;
rdbRemoveTempFile ( server . bgsavechildpid ) ;
}
2010-12-30 09:55:24 -05:00
if ( server . ds_enabled ) {
/* FIXME: flush all objects on disk */
} else if ( server . appendonly ) {
2010-06-21 18:07:48 -04:00
/* Append only file: fsync() the AOF and exit */
aof_fsync ( server . appendfd ) ;
2010-09-30 14:53:34 -04:00
} else if ( server . saveparamslen > 0 ) {
2010-06-21 18:07:48 -04:00
/* Snapshotting. Perform a SYNC SAVE and exit */
2010-08-24 11:09:25 -04:00
if ( rdbSave ( server . dbfilename ) ! = REDIS_OK ) {
2010-06-21 18:07:48 -04:00
/* Ooops.. error saving! The best we can do is to continue
* operating . Note that if there was a background saving process ,
* in the next cron ( ) Redis will be notified that the background
* saving aborted , handling special stuff like slaves pending for
* synchronization . . . */
redisLog ( REDIS_WARNING , " Error trying to save the DB, can't exit " ) ;
return REDIS_ERR ;
}
2010-09-30 14:53:34 -04:00
} else {
redisLog ( REDIS_WARNING , " Not saving DB. " ) ;
2010-06-21 18:07:48 -04:00
}
2010-08-24 11:09:25 -04:00
if ( server . daemonize ) unlink ( server . pidfile ) ;
2010-06-21 18:07:48 -04:00
redisLog ( REDIS_WARNING , " Server exit now, bye bye... " ) ;
return REDIS_OK ;
}
/*================================== Commands =============================== */
void authCommand ( redisClient * c ) {
if ( ! server . requirepass | | ! strcmp ( c - > argv [ 1 ] - > ptr , server . requirepass ) ) {
c - > authenticated = 1 ;
addReply ( c , shared . ok ) ;
} else {
c - > authenticated = 0 ;
2010-09-02 13:52:24 -04:00
addReplyError ( c , " invalid password " ) ;
2010-06-21 18:07:48 -04:00
}
}
void pingCommand ( redisClient * c ) {
addReply ( c , shared . pong ) ;
}
void echoCommand ( redisClient * c ) {
addReplyBulk ( c , c - > argv [ 1 ] ) ;
}
/* Convert an amount of bytes into a human readable string in the form
* of 100 B , 2 G , 100 M , 4 K , and so forth . */
void bytesToHuman ( char * s , unsigned long long n ) {
double d ;
if ( n < 1024 ) {
/* Bytes */
sprintf ( s , " %lluB " , n ) ;
return ;
} else if ( n < ( 1024 * 1024 ) ) {
d = ( double ) n / ( 1024 ) ;
sprintf ( s , " %.2fK " , d ) ;
} else if ( n < ( 1024LL * 1024 * 1024 ) ) {
d = ( double ) n / ( 1024 * 1024 ) ;
sprintf ( s , " %.2fM " , d ) ;
} else if ( n < ( 1024LL * 1024 * 1024 * 1024 ) ) {
d = ( double ) n / ( 1024LL * 1024 * 1024 ) ;
sprintf ( s , " %.2fG " , d ) ;
}
}
/* Create the string returned by the INFO command. This is decoupled
* by the INFO command itself as we need to report the same information
* on memory corruption problems . */
2011-01-23 10:41:07 -05:00
sds genRedisInfoString ( char * section ) {
sds info = sdsempty ( ) ;
2010-06-21 18:07:48 -04:00
time_t uptime = time ( NULL ) - server . stat_starttime ;
2011-01-23 05:46:34 -05:00
int j , numcommands ;
2010-09-16 07:28:58 -04:00
struct rusage self_ru , c_ru ;
2011-01-14 04:20:02 -05:00
unsigned long lol , bib ;
2011-01-23 10:41:07 -05:00
int allsections = 0 , defsections = 0 ;
int sections = 0 ;
if ( section ) {
allsections = strcasecmp ( section , " all " ) = = 0 ;
2011-01-23 16:14:15 -05:00
defsections = strcasecmp ( section , " default " ) = = 0 ;
2011-01-23 10:41:07 -05:00
}
2010-09-16 07:28:58 -04:00
getrusage ( RUSAGE_SELF , & self_ru ) ;
getrusage ( RUSAGE_CHILDREN , & c_ru ) ;
2011-01-14 04:20:02 -05:00
getClientsMaxBuffers ( & lol , & bib ) ;
2011-01-23 10:41:07 -05:00
/* Server */
if ( allsections | | defsections | | ! strcasecmp ( section , " server " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
2010-06-21 18:07:48 -04:00
info = sdscatprintf ( info ,
2011-01-23 10:41:07 -05:00
" # Server \r \n "
" redis_version:%s \r \n "
" redis_git_sha1:%s \r \n "
" redis_git_dirty:%d \r \n "
" arch_bits:%s \r \n "
" multiplexing_api:%s \r \n "
" process_id:%ld \r \n "
" tcp_port:%d \r \n "
" uptime_in_seconds:%ld \r \n "
" uptime_in_days:%ld \r \n "
" lru_clock:%ld \r \n " ,
REDIS_VERSION ,
redisGitSHA1 ( ) ,
strtol ( redisGitDirty ( ) , NULL , 10 ) > 0 ,
( sizeof ( long ) = = 8 ) ? " 64 " : " 32 " ,
aeGetApiName ( ) ,
( long ) getpid ( ) ,
server . port ,
uptime ,
uptime / ( 3600 * 24 ) ,
( unsigned long ) server . lruclock ) ;
}
/* Clients */
if ( allsections | | defsections | | ! strcasecmp ( section , " clients " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info ,
" # Clients \r \n "
" connected_clients:%d \r \n "
" client_longest_output_list:%lu \r \n "
" client_biggest_input_buf:%lu \r \n "
" blocked_clients:%d \r \n " ,
listLength ( server . clients ) - listLength ( server . slaves ) ,
lol , bib ,
server . bpop_blocked_clients ) ;
}
/* Memory */
if ( allsections | | defsections | | ! strcasecmp ( section , " memory " ) ) {
2011-04-21 04:49:52 -04:00
char hmem [ 64 ] ;
char peak_hmem [ 64 ] ;
bytesToHuman ( hmem , zmalloc_used_memory ( ) ) ;
bytesToHuman ( peak_hmem , server . stat_peak_memory ) ;
2011-01-23 10:41:07 -05:00
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info ,
" # Memory \r \n "
" used_memory:%zu \r \n "
" used_memory_human:%s \r \n "
" used_memory_rss:%zu \r \n "
2011-04-21 04:49:52 -04:00
" used_memory_peak:%zu \r \n "
" used_memory_peak_human:%s \r \n "
2011-01-23 10:41:07 -05:00
" mem_fragmentation_ratio:%.2f \r \n "
2011-04-27 07:35:49 -04:00
" mem_allocator:%s \r \n " ,
2011-01-23 10:41:07 -05:00
zmalloc_used_memory ( ) ,
hmem ,
zmalloc_get_rss ( ) ,
2011-04-21 04:49:52 -04:00
server . stat_peak_memory ,
peak_hmem ,
2011-01-23 10:41:07 -05:00
zmalloc_get_fragmentation_ratio ( ) ,
2011-04-27 07:35:49 -04:00
REDIS_MALLOC
2010-11-04 13:50:23 -04:00
) ;
2011-01-23 16:14:15 -05:00
}
/* Allocation statistics */
if ( allsections | | ! strcasecmp ( section , " allocstats " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscat ( info , " # Allocstats \r \n allocation_stats: " ) ;
2011-01-23 10:41:07 -05:00
for ( j = 0 ; j < = ZMALLOC_MAX_ALLOC_STAT ; j + + ) {
size_t count = zmalloc_allocations_for_size ( j ) ;
if ( count ) {
if ( info [ sdslen ( info ) - 1 ] ! = ' : ' ) info = sdscatlen ( info , " , " , 1 ) ;
info = sdscatprintf ( info , " %s%d=%zu " ,
( j = = ZMALLOC_MAX_ALLOC_STAT ) ? " >= " : " " ,
j , count ) ;
}
2010-11-04 13:50:23 -04:00
}
2011-01-23 16:14:15 -05:00
info = sdscat ( info , " \r \n " ) ;
2010-06-21 18:07:48 -04:00
}
2011-01-23 10:41:07 -05:00
/* Persistence */
if ( allsections | | defsections | | ! strcasecmp ( section , " persistence " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
2010-06-21 18:07:48 -04:00
info = sdscatprintf ( info ,
2011-01-23 10:41:07 -05:00
" # Persistence \r \n "
" loading:%d \r \n "
" aof_enabled:%d \r \n "
" changes_since_last_save:%lld \r \n "
" bgsave_in_progress:%d \r \n "
" last_save_time:%ld \r \n "
" bgrewriteaof_in_progress:%d \r \n " ,
server . loading ,
server . appendonly ,
server . dirty ,
server . bgsavechildpid ! = - 1 | |
server . bgsavethread ! = ( pthread_t ) - 1 ,
server . lastsave ,
server . bgrewritechildpid ! = - 1 ) ;
if ( server . loading ) {
double perc ;
time_t eta , elapsed ;
off_t remaining_bytes = server . loading_total_bytes -
server . loading_loaded_bytes ;
perc = ( ( double ) server . loading_loaded_bytes /
server . loading_total_bytes ) * 100 ;
elapsed = time ( NULL ) - server . loading_start_time ;
if ( elapsed = = 0 ) {
eta = 1 ; /* A fake 1 second figure if we don't have
enough info */
} else {
eta = ( elapsed * remaining_bytes ) / server . loading_loaded_bytes ;
}
info = sdscatprintf ( info ,
" loading_start_time:%ld \r \n "
" loading_total_bytes:%llu \r \n "
" loading_loaded_bytes:%llu \r \n "
" loading_loaded_perc:%.2f \r \n "
" loading_eta_seconds:%ld \r \n "
, ( unsigned long ) server . loading_start_time ,
( unsigned long long ) server . loading_total_bytes ,
( unsigned long long ) server . loading_loaded_bytes ,
perc ,
eta
) ;
}
2010-06-21 18:07:48 -04:00
}
2011-01-23 10:41:07 -05:00
/* Diskstore */
if ( allsections | | defsections | | ! strcasecmp ( section , " diskstore " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info ,
" # Diskstore \r \n "
" ds_enabled:%d \r \n " ,
server . ds_enabled ! = 0 ) ;
if ( server . ds_enabled ) {
lockThreadedIO ( ) ;
info = sdscatprintf ( info ,
" cache_max_memory:%llu \r \n "
" cache_blocked_clients:%lu \r \n "
2011-02-25 04:59:57 -05:00
" cache_io_queue_len:%lu \r \n "
" cache_io_jobs_new:%lu \r \n "
" cache_io_jobs_processing:%lu \r \n "
" cache_io_jobs_processed:%lu \r \n "
" cache_io_ready_clients:%lu \r \n "
2011-01-23 10:41:07 -05:00
, ( unsigned long long ) server . cache_max_memory ,
2011-02-25 04:59:57 -05:00
( unsigned long ) server . cache_blocked_clients ,
( unsigned long ) listLength ( server . cache_io_queue ) ,
( unsigned long ) listLength ( server . io_newjobs ) ,
( unsigned long ) listLength ( server . io_processing ) ,
( unsigned long ) listLength ( server . io_processed ) ,
( unsigned long ) listLength ( server . io_ready_clients )
2011-01-23 10:41:07 -05:00
) ;
unlockThreadedIO ( ) ;
2010-11-08 05:52:03 -05:00
}
2011-01-23 10:41:07 -05:00
}
2010-11-08 05:52:03 -05:00
2011-01-23 10:41:07 -05:00
/* Stats */
if ( allsections | | defsections | | ! strcasecmp ( section , " stats " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
2010-11-08 05:52:03 -05:00
info = sdscatprintf ( info ,
2011-01-23 10:41:07 -05:00
" # Stats \r \n "
" total_connections_received:%lld \r \n "
" total_commands_processed:%lld \r \n "
" expired_keys:%lld \r \n "
" evicted_keys:%lld \r \n "
" keyspace_hits:%lld \r \n "
" keyspace_misses:%lld \r \n "
" pubsub_channels:%ld \r \n "
2011-05-29 09:17:29 -04:00
" pubsub_patterns:%u \r \n "
" latest_fork_usec:%lld \r \n " ,
2011-01-23 10:41:07 -05:00
server . stat_numconnections ,
server . stat_numcommands ,
server . stat_expiredkeys ,
server . stat_evictedkeys ,
server . stat_keyspace_hits ,
server . stat_keyspace_misses ,
dictSize ( server . pubsub_channels ) ,
2011-05-29 09:17:29 -04:00
listLength ( server . pubsub_patterns ) ,
server . stat_fork_time ) ;
2010-11-08 05:52:03 -05:00
}
2011-01-09 09:56:50 -05:00
2011-01-23 10:41:07 -05:00
/* Replication */
if ( allsections | | defsections | | ! strcasecmp ( section , " replication " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info ,
" # Replication \r \n "
" role:%s \r \n " ,
server . masterhost = = NULL ? " master " : " slave " ) ;
if ( server . masterhost ) {
info = sdscatprintf ( info ,
" master_host:%s \r \n "
" master_port:%d \r \n "
" master_link_status:%s \r \n "
" master_last_io_seconds_ago:%d \r \n "
" master_sync_in_progress:%d \r \n "
, server . masterhost ,
server . masterport ,
( server . replstate = = REDIS_REPL_CONNECTED ) ?
" up " : " down " ,
server . master ?
( ( int ) ( time ( NULL ) - server . master - > lastinteraction ) ) : - 1 ,
server . replstate = = REDIS_REPL_TRANSFER
) ;
if ( server . replstate = = REDIS_REPL_TRANSFER ) {
info = sdscatprintf ( info ,
" master_sync_left_bytes:%ld \r \n "
" master_sync_last_io_seconds_ago:%d \r \n "
, ( long ) server . repl_transfer_left ,
( int ) ( time ( NULL ) - server . repl_transfer_lastio )
) ;
}
2011-01-09 09:56:50 -05:00
}
2011-01-23 10:41:07 -05:00
info = sdscatprintf ( info ,
" connected_slaves:%d \r \n " ,
listLength ( server . slaves ) ) ;
2011-01-09 09:56:50 -05:00
}
2011-01-23 16:14:15 -05:00
/* CPU */
if ( allsections | | defsections | | ! strcasecmp ( section , " cpu " ) ) {
2011-01-23 10:41:07 -05:00
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info ,
2011-01-23 16:14:15 -05:00
" # CPU \r \n "
2011-01-23 10:41:07 -05:00
" used_cpu_sys:%.2f \r \n "
" used_cpu_user:%.2f \r \n "
" used_cpu_sys_childrens:%.2f \r \n "
" used_cpu_user_childrens:%.2f \r \n " ,
( float ) self_ru . ru_utime . tv_sec + ( float ) self_ru . ru_utime . tv_usec / 1000000 ,
( float ) self_ru . ru_stime . tv_sec + ( float ) self_ru . ru_stime . tv_usec / 1000000 ,
( float ) c_ru . ru_utime . tv_sec + ( float ) c_ru . ru_utime . tv_usec / 1000000 ,
( float ) c_ru . ru_stime . tv_sec + ( float ) c_ru . ru_stime . tv_usec / 1000000 ) ;
2011-01-23 16:14:15 -05:00
}
2011-01-23 10:41:07 -05:00
2011-01-23 16:14:15 -05:00
/* cmdtime */
if ( allsections | | ! strcasecmp ( section , " commandstats " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info , " # Commandstats \r \n " ) ;
2011-01-24 04:56:06 -05:00
numcommands = sizeof ( redisCommandTable ) / sizeof ( struct redisCommand ) ;
2011-01-23 10:41:07 -05:00
for ( j = 0 ; j < numcommands ; j + + ) {
2011-01-24 04:56:06 -05:00
struct redisCommand * c = redisCommandTable + j ;
2011-01-23 16:14:15 -05:00
2011-01-24 04:56:06 -05:00
if ( ! c - > calls ) continue ;
info = sdscatprintf ( info ,
" cmdstat_%s:calls=%lld,usec=%lld,usec_per_call=%.2f \r \n " ,
c - > name , c - > calls , c - > microseconds ,
( c - > calls = = 0 ) ? 0 : ( ( float ) c - > microseconds / c - > calls ) ) ;
2011-01-23 10:41:07 -05:00
}
2011-01-23 05:46:34 -05:00
}
2011-04-11 10:39:39 -04:00
/* Clusetr */
if ( allsections | | defsections | | ! strcasecmp ( section , " cluster " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info ,
" # Cluster \r \n "
" cluster_enabled:%d \r \n " ,
server . cluster_enabled ) ;
}
2011-01-23 10:41:07 -05:00
/* Key space */
if ( allsections | | defsections | | ! strcasecmp ( section , " keyspace " ) ) {
if ( sections + + ) info = sdscat ( info , " \r \n " ) ;
info = sdscatprintf ( info , " # Keyspace \r \n " ) ;
for ( j = 0 ; j < server . dbnum ; j + + ) {
long long keys , vkeys ;
2010-06-21 18:07:48 -04:00
2011-01-23 10:41:07 -05:00
keys = dictSize ( server . db [ j ] . dict ) ;
vkeys = dictSize ( server . db [ j ] . expires ) ;
if ( keys | | vkeys ) {
info = sdscatprintf ( info , " db%d:keys=%lld,expires=%lld \r \n " ,
j , keys , vkeys ) ;
}
2010-06-21 18:07:48 -04:00
}
}
return info ;
}
void infoCommand ( redisClient * c ) {
2011-01-23 10:41:07 -05:00
char * section = c - > argc = = 2 ? c - > argv [ 1 ] - > ptr : " default " ;
if ( c - > argc > 2 ) {
addReply ( c , shared . syntaxerr ) ;
return ;
}
sds info = genRedisInfoString ( section ) ;
2010-06-21 18:07:48 -04:00
addReplySds ( c , sdscatprintf ( sdsempty ( ) , " $%lu \r \n " ,
( unsigned long ) sdslen ( info ) ) ) ;
addReplySds ( c , info ) ;
addReply ( c , shared . crlf ) ;
}
void monitorCommand ( redisClient * c ) {
/* ignore MONITOR if aleady slave or in monitor mode */
if ( c - > flags & REDIS_SLAVE ) return ;
c - > flags | = ( REDIS_SLAVE | REDIS_MONITOR ) ;
c - > slaveseldb = 0 ;
listAddNodeTail ( server . monitors , c ) ;
addReply ( c , shared . ok ) ;
}
/* ============================ Maxmemory directive ======================== */
/* This function gets called when 'maxmemory' is set on the config file to limit
* the max memory used by the server , and we are out of memory .
* This function will try to , in order :
*
* - Free objects from the free list
* - Try to remove keys with an EXPIRE set
*
* It is not possible to free enough memory to reach used - memory < maxmemory
* the server will start refusing commands that will enlarge even more the
* memory usage .
*/
void freeMemoryIfNeeded ( void ) {
2010-10-14 15:22:21 -04:00
/* Remove keys accordingly to the active policy as long as we are
* over the memory limit . */
2010-11-08 10:12:16 -05:00
if ( server . maxmemory_policy = = REDIS_MAXMEMORY_NO_EVICTION ) return ;
2010-11-02 07:09:59 -04:00
while ( server . maxmemory & & zmalloc_used_memory ( ) > server . maxmemory ) {
2010-06-21 18:07:48 -04:00
int j , k , freed = 0 ;
2010-10-14 15:22:21 -04:00
for ( j = 0 ; j < server . dbnum ; j + + ) {
2010-11-02 06:15:09 -04:00
long bestval = 0 ; /* just to prevent warning */
2010-10-14 15:22:21 -04:00
sds bestkey = NULL ;
struct dictEntry * de ;
redisDb * db = server . db + j ;
dict * dict ;
if ( server . maxmemory_policy = = REDIS_MAXMEMORY_ALLKEYS_LRU | |
server . maxmemory_policy = = REDIS_MAXMEMORY_ALLKEYS_RANDOM )
{
dict = server . db [ j ] . dict ;
} else {
dict = server . db [ j ] . expires ;
}
if ( dictSize ( dict ) = = 0 ) continue ;
/* volatile-random and allkeys-random policy */
if ( server . maxmemory_policy = = REDIS_MAXMEMORY_ALLKEYS_RANDOM | |
server . maxmemory_policy = = REDIS_MAXMEMORY_VOLATILE_RANDOM )
{
de = dictGetRandomKey ( dict ) ;
bestkey = dictGetEntryKey ( de ) ;
}
/* volatile-lru and allkeys-lru policy */
else if ( server . maxmemory_policy = = REDIS_MAXMEMORY_ALLKEYS_LRU | |
server . maxmemory_policy = = REDIS_MAXMEMORY_VOLATILE_LRU )
{
for ( k = 0 ; k < server . maxmemory_samples ; k + + ) {
sds thiskey ;
long thisval ;
robj * o ;
de = dictGetRandomKey ( dict ) ;
thiskey = dictGetEntryKey ( de ) ;
2010-11-11 07:19:17 -05:00
/* When policy is volatile-lru we need an additonal lookup
* to locate the real key , as dict is set to db - > expires . */
if ( server . maxmemory_policy = = REDIS_MAXMEMORY_VOLATILE_LRU )
de = dictFind ( db - > dict , thiskey ) ;
2010-10-14 15:22:21 -04:00
o = dictGetEntryVal ( de ) ;
thisval = estimateObjectIdleTime ( o ) ;
/* Higher idle time is better candidate for deletion */
if ( bestkey = = NULL | | thisval > bestval ) {
bestkey = thiskey ;
bestval = thisval ;
}
}
}
/* volatile-ttl */
else if ( server . maxmemory_policy = = REDIS_MAXMEMORY_VOLATILE_TTL ) {
for ( k = 0 ; k < server . maxmemory_samples ; k + + ) {
sds thiskey ;
long thisval ;
de = dictGetRandomKey ( dict ) ;
thiskey = dictGetEntryKey ( de ) ;
thisval = ( long ) dictGetEntryVal ( de ) ;
/* Expire sooner (minor expire unix timestamp) is better
* candidate for deletion */
if ( bestkey = = NULL | | thisval < bestval ) {
bestkey = thiskey ;
bestval = thisval ;
}
}
}
/* Finally remove the selected key. */
if ( bestkey ) {
robj * keyobj = createStringObject ( bestkey , sdslen ( bestkey ) ) ;
2011-01-27 10:52:37 -05:00
propagateExpire ( db , keyobj ) ;
2010-10-14 15:22:21 -04:00
dbDelete ( db , keyobj ) ;
2010-12-19 09:15:08 -05:00
server . stat_evictedkeys + + ;
2010-10-14 15:22:21 -04:00
decrRefCount ( keyobj ) ;
freed + + ;
}
}
if ( ! freed ) return ; /* nothing to free... */
}
2010-06-21 18:07:48 -04:00
}
/* =================================== Main! ================================ */
# ifdef __linux__
int linuxOvercommitMemoryValue ( void ) {
FILE * fp = fopen ( " /proc/sys/vm/overcommit_memory " , " r " ) ;
char buf [ 64 ] ;
if ( ! fp ) return - 1 ;
if ( fgets ( buf , 64 , fp ) = = NULL ) {
fclose ( fp ) ;
return - 1 ;
}
fclose ( fp ) ;
return atoi ( buf ) ;
}
void linuxOvercommitMemoryWarning ( void ) {
if ( linuxOvercommitMemoryValue ( ) = = 0 ) {
redisLog ( REDIS_WARNING , " WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. " ) ;
}
}
# endif /* __linux__ */
2010-08-24 11:09:25 -04:00
void createPidFile ( void ) {
/* Try to write the pid file in a best-effort way. */
FILE * fp = fopen ( server . pidfile , " w " ) ;
if ( fp ) {
2010-12-23 06:25:56 -05:00
fprintf ( fp , " %d \n " , ( int ) getpid ( ) ) ;
2010-08-24 11:09:25 -04:00
fclose ( fp ) ;
}
}
2010-06-21 18:07:48 -04:00
void daemonize ( void ) {
int fd ;
if ( fork ( ) ! = 0 ) exit ( 0 ) ; /* parent exits */
setsid ( ) ; /* create a new session */
/* Every output goes to /dev/null. If Redis is daemonized but
* the ' logfile ' is set to ' stdout ' in the configuration file
* it will not log at all . */
if ( ( fd = open ( " /dev/null " , O_RDWR , 0 ) ) ! = - 1 ) {
dup2 ( fd , STDIN_FILENO ) ;
dup2 ( fd , STDOUT_FILENO ) ;
dup2 ( fd , STDERR_FILENO ) ;
if ( fd > STDERR_FILENO ) close ( fd ) ;
}
}
void version ( ) {
printf ( " Redis server version %s (%s:%d) \n " , REDIS_VERSION ,
redisGitSHA1 ( ) , atoi ( redisGitDirty ( ) ) > 0 ) ;
exit ( 0 ) ;
}
void usage ( ) {
fprintf ( stderr , " Usage: ./redis-server [/path/to/redis.conf] \n " ) ;
fprintf ( stderr , " ./redis-server - (read config from stdin) \n " ) ;
exit ( 1 ) ;
}
2011-04-13 04:58:21 -04:00
void redisAsciiArt ( void ) {
# include "asciilogo.h"
char * buf = zmalloc ( 1024 * 16 ) ;
snprintf ( buf , 1024 * 16 , ascii_logo ,
REDIS_VERSION ,
redisGitSHA1 ( ) ,
strtol ( redisGitDirty ( ) , NULL , 10 ) > 0 ,
( sizeof ( long ) = = 8 ) ? " 64 " : " 32 " ,
server . cluster_enabled ? " cluster " : " stand alone " ,
server . port ,
( long ) getpid ( )
) ;
redisLogRaw ( REDIS_NOTICE | REDIS_LOG_RAW , buf ) ;
zfree ( buf ) ;
}
2010-06-21 18:07:48 -04:00
int main ( int argc , char * * argv ) {
2011-02-28 09:31:54 -05:00
long long start ;
2010-06-21 18:07:48 -04:00
initServerConfig ( ) ;
if ( argc = = 2 ) {
if ( strcmp ( argv [ 1 ] , " -v " ) = = 0 | |
strcmp ( argv [ 1 ] , " --version " ) = = 0 ) version ( ) ;
if ( strcmp ( argv [ 1 ] , " --help " ) = = 0 ) usage ( ) ;
resetServerSaveParams ( ) ;
loadServerConfig ( argv [ 1 ] ) ;
} else if ( ( argc > 2 ) ) {
usage ( ) ;
} else {
redisLog ( REDIS_WARNING , " Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf' " ) ;
}
if ( server . daemonize ) daemonize ( ) ;
initServer ( ) ;
2010-08-24 11:09:25 -04:00
if ( server . daemonize ) createPidFile ( ) ;
2011-04-13 04:58:21 -04:00
redisAsciiArt ( ) ;
2010-06-21 18:07:48 -04:00
redisLog ( REDIS_NOTICE , " Server started, Redis version " REDIS_VERSION ) ;
# ifdef __linux__
linuxOvercommitMemoryWarning ( ) ;
# endif
2011-02-28 09:31:54 -05:00
start = ustime ( ) ;
2010-12-29 17:00:00 -05:00
if ( server . ds_enabled ) {
2010-12-29 17:08:18 -05:00
redisLog ( REDIS_NOTICE , " DB not loaded (running with disk back end) " ) ;
2010-12-29 17:00:00 -05:00
} else if ( server . appendonly ) {
2010-06-21 18:07:48 -04:00
if ( loadAppendOnlyFile ( server . appendfilename ) = = REDIS_OK )
2011-02-28 09:31:54 -05:00
redisLog ( REDIS_NOTICE , " DB loaded from append only file: %.3f seconds " , ( float ) ( ustime ( ) - start ) / 1000000 ) ;
2010-06-21 18:07:48 -04:00
} else {
if ( rdbLoad ( server . dbfilename ) = = REDIS_OK )
2011-02-28 09:31:54 -05:00
redisLog ( REDIS_NOTICE , " DB loaded from disk: %.3f seconds " , ( float ) ( ustime ( ) - start ) / 1000000 ) ;
2010-06-21 18:07:48 -04:00
}
2010-08-03 07:33:12 -04:00
if ( server . ipfd > 0 )
redisLog ( REDIS_NOTICE , " The server is now ready to accept connections on port %d " , server . port ) ;
if ( server . sofd > 0 )
2010-10-13 11:17:56 -04:00
redisLog ( REDIS_NOTICE , " The server is now ready to accept connections at %s " , server . unixsocket ) ;
2010-06-21 18:07:48 -04:00
aeSetBeforeSleepProc ( server . el , beforeSleep ) ;
aeMain ( server . el ) ;
aeDeleteEventLoop ( server . el ) ;
return 0 ;
}
# ifdef HAVE_BACKTRACE
2011-03-06 11:49:22 -05:00
static void * getMcontextEip ( ucontext_t * uc ) {
2010-06-21 18:07:48 -04:00
# if defined(__FreeBSD__)
return ( void * ) uc - > uc_mcontext . mc_eip ;
# elif defined(__dietlibc__)
return ( void * ) uc - > uc_mcontext . eip ;
# elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
# if __x86_64__
return ( void * ) uc - > uc_mcontext - > __ss . __rip ;
# else
return ( void * ) uc - > uc_mcontext - > __ss . __eip ;
# endif
# elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
# if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
return ( void * ) uc - > uc_mcontext - > __ss . __rip ;
# else
return ( void * ) uc - > uc_mcontext - > __ss . __eip ;
# endif
2010-07-01 15:13:38 -04:00
# elif defined(__i386__)
return ( void * ) uc - > uc_mcontext . gregs [ 14 ] ; /* Linux 32 */
# elif defined(__X86_64__) || defined(__x86_64__)
return ( void * ) uc - > uc_mcontext . gregs [ 16 ] ; /* Linux 64 */
2010-06-21 18:07:48 -04:00
# elif defined(__ia64__) /* Linux IA64 */
return ( void * ) uc - > uc_mcontext . sc_ip ;
# else
return NULL ;
# endif
}
2011-03-06 11:49:22 -05:00
static void sigsegvHandler ( int sig , siginfo_t * info , void * secret ) {
2010-06-21 18:07:48 -04:00
void * trace [ 100 ] ;
char * * messages = NULL ;
int i , trace_size = 0 ;
ucontext_t * uc = ( ucontext_t * ) secret ;
sds infostring ;
2010-10-22 17:30:48 -04:00
struct sigaction act ;
2010-06-21 18:07:48 -04:00
REDIS_NOTUSED ( info ) ;
redisLog ( REDIS_WARNING ,
" ======= Ooops! Redis %s got signal: -%d- ======= " , REDIS_VERSION , sig ) ;
2011-01-23 10:41:07 -05:00
infostring = genRedisInfoString ( " all " ) ;
2011-02-09 04:10:35 -05:00
redisLogRaw ( REDIS_WARNING , infostring ) ;
2010-06-21 18:07:48 -04:00
/* It's not safe to sdsfree() the returned string under memory
* corruption conditions . Let it leak as we are going to abort */
trace_size = backtrace ( trace , 100 ) ;
/* overwrite sigaction with caller's address */
if ( getMcontextEip ( uc ) ! = NULL ) {
trace [ 1 ] = getMcontextEip ( uc ) ;
}
messages = backtrace_symbols ( trace , trace_size ) ;
for ( i = 1 ; i < trace_size ; + + i )
redisLog ( REDIS_WARNING , " %s " , messages [ i ] ) ;
/* free(messages); Don't call free() with possibly corrupted memory. */
2010-08-24 11:09:25 -04:00
if ( server . daemonize ) unlink ( server . pidfile ) ;
2010-10-22 17:30:48 -04:00
/* Make sure we exit with the right signal at the end. So for instance
* the core will be dumped if enabled . */
sigemptyset ( & act . sa_mask ) ;
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction
* is used . Otherwise , sa_handler is used */
act . sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND ;
act . sa_handler = SIG_DFL ;
sigaction ( sig , & act , NULL ) ;
kill ( getpid ( ) , sig ) ;
2010-06-21 18:07:48 -04:00
}
2011-03-06 11:49:22 -05:00
# endif /* HAVE_BACKTRACE */
2010-06-21 18:07:48 -04:00
2011-03-06 11:49:22 -05:00
static void sigtermHandler ( int sig ) {
2010-06-21 18:07:48 -04:00
REDIS_NOTUSED ( sig ) ;
2011-03-06 11:49:22 -05:00
redisLog ( REDIS_WARNING , " Received SIGTERM, scheduling shutdown... " ) ;
2010-06-21 18:07:48 -04:00
server . shutdown_asap = 1 ;
}
2011-03-06 11:49:22 -05:00
void setupSignalHandlers ( void ) {
2010-06-21 18:07:48 -04:00
struct sigaction act ;
2011-03-06 11:49:22 -05:00
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
* Otherwise , sa_handler is used . */
sigemptyset ( & act . sa_mask ) ;
2010-06-21 18:07:48 -04:00
act . sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND ;
act . sa_handler = sigtermHandler ;
2011-03-06 11:49:22 -05:00
sigaction ( SIGTERM , & act , NULL ) ;
2010-06-21 18:07:48 -04:00
2011-03-06 11:49:22 -05:00
# ifdef HAVE_BACKTRACE
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO ;
act . sa_sigaction = sigsegvHandler ;
sigaction ( SIGSEGV , & act , NULL ) ;
sigaction ( SIGBUS , & act , NULL ) ;
sigaction ( SIGFPE , & act , NULL ) ;
sigaction ( SIGILL , & act , NULL ) ;
# endif
return ;
2010-06-21 18:07:48 -04:00
}
/* The End */