2012-11-08 12:25:23 -05:00
/*
* Copyright ( c ) 2009 - 2012 , 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 .
*/
2015-07-26 09:14:57 -04:00
# include "server.h"
2010-06-21 18:07:48 -04:00
# include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
2012-11-29 08:20:08 -05:00
# include "crc64.h"
2010-06-21 18:07:48 -04:00
2010-07-01 15:13:38 -04:00
# include <arpa/inet.h>
2012-01-20 06:20:45 -05:00
# include <signal.h>
# ifdef HAVE_BACKTRACE
# include <execinfo.h>
# include <ucontext.h>
2012-04-26 10:04:53 -04:00
# include <fcntl.h>
2012-11-22 16:52:39 -05:00
# include "bio.h"
2016-07-04 16:28:32 -04:00
# include <unistd.h>
# define _GNU_SOURCE
# include <dlfcn.h>
2012-01-20 06:20:45 -05:00
# endif /* HAVE_BACKTRACE */
2010-07-01 15:13:38 -04:00
2014-05-12 10:56:43 -04:00
# ifdef __CYGWIN__
# ifndef SA_ONSTACK
# define SA_ONSTACK 0x08000000
# endif
# endif
2010-06-21 18:07:48 -04:00
/* ================================= Debugging ============================== */
/* Compute the sha1 of string at 's' with 'len' bytes long.
2013-01-16 12:00:20 -05:00
* The SHA1 is then xored against the string pointed by digest .
2010-06-21 18:07:48 -04:00
* Since xor is commutative , this operation is used in order to
* " add " digests relative to unordered elements .
*
* So digest ( a , b , c , d ) will be the same of digest ( b , a , c , d ) */
void xorDigest ( unsigned char * digest , void * ptr , size_t len ) {
SHA1_CTX ctx ;
unsigned char hash [ 20 ] , * s = ptr ;
int j ;
SHA1Init ( & ctx ) ;
SHA1Update ( & ctx , s , len ) ;
SHA1Final ( hash , & ctx ) ;
for ( j = 0 ; j < 20 ; j + + )
digest [ j ] ^ = hash [ j ] ;
}
void xorObjectDigest ( unsigned char * digest , robj * o ) {
o = getDecodedObject ( o ) ;
xorDigest ( digest , o - > ptr , sdslen ( o - > ptr ) ) ;
decrRefCount ( o ) ;
}
/* This function instead of just computing the SHA1 and xoring it
2013-01-16 12:00:20 -05:00
* against digest , also perform the digest of " digest " itself and
2010-06-21 18:07:48 -04:00
* replace the old value with the new one .
*
* So the final digest will be :
*
* digest = SHA1 ( digest xor SHA1 ( data ) )
*
* This function is used every time we want to preserve the order so
* that digest ( a , b , c , d ) will be different than digest ( b , c , d , a )
*
* Also note that mixdigest ( " foo " ) followed by mixdigest ( " bar " )
* will lead to a different digest compared to " fo " , " obar " .
*/
void mixDigest ( unsigned char * digest , void * ptr , size_t len ) {
SHA1_CTX ctx ;
char * s = ptr ;
xorDigest ( digest , s , len ) ;
SHA1Init ( & ctx ) ;
SHA1Update ( & ctx , digest , 20 ) ;
SHA1Final ( digest , & ctx ) ;
}
void mixObjectDigest ( unsigned char * digest , robj * o ) {
o = getDecodedObject ( o ) ;
mixDigest ( digest , o - > ptr , sdslen ( o - > ptr ) ) ;
decrRefCount ( o ) ;
}
/* Compute the dataset digest. Since keys, sets elements, hashes elements
* are not ordered , we use a trick : every aggregate digest is the xor
* of the digests of their elements . This way the order will not change
* the result . For list instead we use a feedback entering the output digest
* as input in order to ensure that a different ordered list will result in
* a different digest . */
void computeDatasetDigest ( unsigned char * final ) {
unsigned char digest [ 20 ] ;
char buf [ 128 ] ;
dictIterator * di = NULL ;
dictEntry * de ;
int j ;
uint32_t aux ;
memset ( final , 0 , 20 ) ; /* Start with a clean result */
for ( j = 0 ; j < server . dbnum ; j + + ) {
redisDb * db = server . db + j ;
if ( dictSize ( db - > dict ) = = 0 ) continue ;
di = dictGetIterator ( db - > dict ) ;
/* hash the DB id, so the same dataset moved in a different
* DB will lead to a different digest */
aux = htonl ( j ) ;
mixDigest ( final , & aux , sizeof ( aux ) ) ;
/* Iterate this DB writing every entry */
while ( ( de = dictNext ( di ) ) ! = NULL ) {
sds key ;
robj * keyobj , * o ;
2011-11-11 19:04:27 -05:00
long long expiretime ;
2010-06-21 18:07:48 -04:00
memset ( digest , 0 , 20 ) ; /* This key-val digest */
2011-11-08 11:07:55 -05:00
key = dictGetKey ( de ) ;
2010-06-21 18:07:48 -04:00
keyobj = createStringObject ( key , sdslen ( key ) ) ;
mixDigest ( digest , key , sdslen ( key ) ) ;
2011-11-08 11:07:55 -05:00
o = dictGetVal ( de ) ;
2010-06-21 18:07:48 -04:00
aux = htonl ( o - > type ) ;
mixDigest ( digest , & aux , sizeof ( aux ) ) ;
expiretime = getExpire ( db , keyobj ) ;
/* Save the key and associated value */
2015-07-26 09:28:00 -04:00
if ( o - > type = = OBJ_STRING ) {
2010-06-21 18:07:48 -04:00
mixObjectDigest ( digest , o ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_LIST ) {
2015-07-27 03:41:48 -04:00
listTypeIterator * li = listTypeInitIterator ( o , 0 , LIST_TAIL ) ;
2010-06-21 18:07:48 -04:00
listTypeEntry entry ;
while ( listTypeNext ( li , & entry ) ) {
robj * eleobj = listTypeGet ( & entry ) ;
mixObjectDigest ( digest , eleobj ) ;
decrRefCount ( eleobj ) ;
}
listTypeReleaseIterator ( li ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_SET ) {
2010-08-21 05:25:13 -04:00
setTypeIterator * si = setTypeInitIterator ( o ) ;
2015-07-31 12:01:23 -04:00
sds sdsele ;
while ( ( sdsele = setTypeNextObject ( si ) ) ! = NULL ) {
2015-08-04 03:20:55 -04:00
xorDigest ( digest , sdsele , sdslen ( sdsele ) ) ;
2015-09-04 04:22:24 -04:00
sdsfree ( sdsele ) ;
2010-06-21 18:07:48 -04:00
}
2010-07-02 14:42:20 -04:00
setTypeReleaseIterator ( si ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_ZSET ) {
2011-03-14 08:30:06 -04:00
unsigned char eledigest [ 20 ] ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_ZIPLIST ) {
2011-03-14 08:30:06 -04:00
unsigned char * zl = o - > ptr ;
unsigned char * eptr , * sptr ;
unsigned char * vstr ;
unsigned int vlen ;
long long vll ;
double score ;
eptr = ziplistIndex ( zl , 0 ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( eptr ! = NULL ) ;
2011-03-14 08:30:06 -04:00
sptr = ziplistNext ( zl , eptr ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( sptr ! = NULL ) ;
2011-03-14 08:30:06 -04:00
while ( eptr ! = NULL ) {
2015-07-26 09:29:53 -04:00
serverAssert ( ziplistGet ( eptr , & vstr , & vlen , & vll ) ) ;
2011-03-14 08:30:06 -04:00
score = zzlGetScore ( sptr ) ;
memset ( eledigest , 0 , 20 ) ;
if ( vstr ! = NULL ) {
mixDigest ( eledigest , vstr , vlen ) ;
} else {
ll2string ( buf , sizeof ( buf ) , vll ) ;
mixDigest ( eledigest , buf , strlen ( buf ) ) ;
}
snprintf ( buf , sizeof ( buf ) , " %.17g " , score ) ;
mixDigest ( eledigest , buf , strlen ( buf ) ) ;
xorDigest ( digest , eledigest , 20 ) ;
zzlNext ( zl , & eptr , & sptr ) ;
}
2015-07-26 09:28:00 -04:00
} else if ( o - > encoding = = OBJ_ENCODING_SKIPLIST ) {
2011-03-14 08:30:06 -04:00
zset * zs = o - > ptr ;
dictIterator * di = dictGetIterator ( zs - > dict ) ;
dictEntry * de ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
2015-08-04 03:20:55 -04:00
sds sdsele = dictGetKey ( de ) ;
2011-11-08 11:07:55 -05:00
double * score = dictGetVal ( de ) ;
2011-03-14 08:30:06 -04:00
snprintf ( buf , sizeof ( buf ) , " %.17g " , * score ) ;
memset ( eledigest , 0 , 20 ) ;
2015-08-04 03:20:55 -04:00
mixDigest ( eledigest , sdsele , sdslen ( sdsele ) ) ;
2011-03-14 08:30:06 -04:00
mixDigest ( eledigest , buf , strlen ( buf ) ) ;
xorDigest ( digest , eledigest , 20 ) ;
}
dictReleaseIterator ( di ) ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown sorted set encoding " ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_HASH ) {
2015-09-23 03:33:23 -04:00
hashTypeIterator * hi = hashTypeInitIterator ( o ) ;
2015-07-26 17:17:55 -04:00
while ( hashTypeNext ( hi ) ! = C_ERR ) {
2010-06-21 18:07:48 -04:00
unsigned char eledigest [ 20 ] ;
2015-09-23 03:33:23 -04:00
sds sdsele ;
2010-06-21 18:07:48 -04:00
memset ( eledigest , 0 , 20 ) ;
2015-09-23 03:33:23 -04:00
sdsele = hashTypeCurrentObjectNewSds ( hi , OBJ_HASH_KEY ) ;
mixDigest ( eledigest , sdsele , sdslen ( sdsele ) ) ;
sdsfree ( sdsele ) ;
sdsele = hashTypeCurrentObjectNewSds ( hi , OBJ_HASH_VALUE ) ;
mixDigest ( eledigest , sdsele , sdslen ( sdsele ) ) ;
sdsfree ( sdsele ) ;
2010-06-21 18:07:48 -04:00
xorDigest ( digest , eledigest , 20 ) ;
}
hashTypeReleaseIterator ( hi ) ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown object type " ) ;
2010-06-21 18:07:48 -04:00
}
/* If the key has an expire, add it to the mix */
if ( expiretime ! = - 1 ) xorDigest ( digest , " !!expire!! " , 10 ) ;
/* We can finally xor the key-val digest to the final digest */
xorDigest ( final , digest , 20 ) ;
decrRefCount ( keyobj ) ;
}
dictReleaseIterator ( di ) ;
}
}
2016-04-25 09:47:42 -04:00
# if defined(USE_JEMALLOC)
2014-11-16 13:05:48 -05:00
void inputCatSds ( void * result , const char * str ) {
/* result is actually a (sds *), so re-cast it here */
sds * info = ( sds * ) result ;
* info = sdscat ( * info , str ) ;
}
2016-04-25 09:47:42 -04:00
# endif
2014-11-16 13:05:48 -05:00
2015-07-26 09:20:46 -04:00
void debugCommand ( client * c ) {
2016-05-04 06:45:55 -04:00
if ( c - > argc = = 1 ) {
addReplyError ( c , " You must specify a subcommand for DEBUG. Try DEBUG HELP for info. " ) ;
return ;
}
if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " help " ) ) {
void * blenp = addDeferredMultiBulkLength ( c ) ;
int blen = 0 ;
blen + + ; addReplyStatus ( c ,
" DEBUG <subcommand> arg arg ... arg. Subcommands: " ) ;
blen + + ; addReplyStatus ( c ,
" segfault -- Crash the server with sigsegv. " ) ;
blen + + ; addReplyStatus ( c ,
" restart -- Graceful restart: save config, db, restart. " ) ;
blen + + ; addReplyStatus ( c ,
" crash-and-recovery <milliseconds> -- Hard crash and restart after <milliseconds> delay. " ) ;
blen + + ; addReplyStatus ( c ,
" assert -- Crash by assertion failed. " ) ;
blen + + ; addReplyStatus ( c ,
" reload -- Save the RDB on disk and reload it back in memory. " ) ;
blen + + ; addReplyStatus ( c ,
" loadaof -- Flush the AOF buffers on disk and reload the AOF in memory. " ) ;
blen + + ; addReplyStatus ( c ,
" object <key> -- Show low level info about key and associated value. " ) ;
blen + + ; addReplyStatus ( c ,
" sdslen <key> -- Show low level SDS string info representing key and value. " ) ;
blen + + ; addReplyStatus ( c ,
" populate <count> [prefix] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix. " ) ;
blen + + ; addReplyStatus ( c ,
" digest -- Outputs an hex signature representing the current DB content. " ) ;
blen + + ; addReplyStatus ( c ,
" sleep <seconds> -- Stop the server for <seconds>. Decimals allowed. " ) ;
blen + + ; addReplyStatus ( c ,
" set-active-expire (0|1) -- Setting it to 0 disables expiring keys in background when they are not accessed (otherwise the Redis behavior). Setting it to 1 reenables back the default. " ) ;
blen + + ; addReplyStatus ( c ,
" lua-always-replicate-commands (0|1) -- Setting it to 1 makes Lua replication defaulting to replicating single commands, without the script having to enable effects replication. " ) ;
blen + + ; addReplyStatus ( c ,
" error <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors. " ) ;
blen + + ; addReplyStatus ( c ,
" structsize -- Return the size of different Redis core C structures. " ) ;
blen + + ; addReplyStatus ( c ,
" htstats <dbid> -- Return hash table statistics of the specified Redis database. " ) ;
blen + + ; addReplyStatus ( c ,
" jemalloc info -- Show internal jemalloc statistics. " ) ;
blen + + ; addReplyStatus ( c ,
" jemalloc purge -- Force jemalloc to release unused memory. " ) ;
setDeferredMultiBulkLength ( c , blenp , blen ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " segfault " ) ) {
2010-06-21 18:07:48 -04:00
* ( ( char * ) - 1 ) = ' x ' ;
2015-10-13 05:08:24 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " restart " ) | |
! strcasecmp ( c - > argv [ 1 ] - > ptr , " crash-and-recover " ) )
{
long long delay = 0 ;
if ( c - > argc > = 3 ) {
if ( getLongLongFromObjectOrReply ( c , c - > argv [ 2 ] , & delay , NULL )
! = C_OK ) return ;
if ( delay < 0 ) delay = 0 ;
}
int flags = ! strcasecmp ( c - > argv [ 1 ] - > ptr , " restart " ) ?
( RESTART_SERVER_GRACEFULLY | RESTART_SERVER_CONFIG_REWRITE ) :
RESTART_SERVER_NONE ;
restartServer ( flags , delay ) ;
addReplyError ( c , " failed to restart the server. Check server logs. " ) ;
2012-08-24 06:55:37 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " oom " ) ) {
void * ptr = zmalloc ( ULONG_MAX ) ; /* Should trigger an out of memory. */
zfree ( ptr ) ;
addReply ( c , shared . ok ) ;
2011-10-04 11:22:29 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " assert " ) ) {
if ( c - > argc > = 3 ) c - > argv [ 2 ] = tryObjectEncoding ( c - > argv [ 2 ] ) ;
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , c - > argv [ 0 ] , 1 = = 2 ) ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " reload " ) ) {
2015-07-26 17:17:55 -04:00
if ( rdbSave ( server . rdb_filename ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
addReply ( c , shared . err ) ;
return ;
}
2015-09-28 04:47:45 -04:00
emptyDb ( - 1 , EMPTYDB_NO_FLAGS , NULL ) ;
2015-07-26 17:17:55 -04:00
if ( rdbLoad ( server . rdb_filename ) ! = C_OK ) {
2011-10-14 08:30:41 -04:00
addReplyError ( c , " Error trying to load the RDB dump " ) ;
2010-06-21 18:07:48 -04:00
return ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " DB reloaded by DEBUG RELOAD " ) ;
2010-06-21 18:07:48 -04:00
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " loadaof " ) ) {
2015-10-30 06:57:36 -04:00
if ( server . aof_state = = AOF_ON ) flushAppendOnlyFile ( 1 ) ;
2015-09-28 04:47:45 -04:00
emptyDb ( - 1 , EMPTYDB_NO_FLAGS , NULL ) ;
2015-07-26 17:17:55 -04:00
if ( loadAppendOnlyFile ( server . aof_filename ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
addReply ( c , shared . err ) ;
return ;
}
2011-12-20 11:52:57 -05:00
server . dirty = 0 ; /* Prevent AOF / replication */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Append Only File loaded by DEBUG LOADAOF " ) ;
2010-06-21 18:07:48 -04:00
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " object " ) & & c - > argc = = 3 ) {
2011-01-07 20:06:01 -05:00
dictEntry * de ;
2010-06-21 18:07:48 -04:00
robj * val ;
2010-12-29 10:58:57 -05:00
char * strenc ;
2010-06-21 18:07:48 -04:00
2011-01-07 20:06:01 -05:00
if ( ( de = dictFind ( c - > db - > dict , c - > argv [ 2 ] - > ptr ) ) = = NULL ) {
2010-06-21 18:07:48 -04:00
addReply ( c , shared . nokeyerr ) ;
return ;
}
2011-11-08 11:07:55 -05:00
val = dictGetVal ( de ) ;
2010-12-29 10:58:57 -05:00
strenc = strEncoding ( val - > encoding ) ;
2011-01-01 15:35:56 -05:00
2014-12-10 22:54:19 -05:00
char extra [ 128 ] = { 0 } ;
2015-07-26 09:28:00 -04:00
if ( val - > encoding = = OBJ_ENCODING_QUICKLIST ) {
2014-12-10 22:54:19 -05:00
char * nextra = extra ;
int remaining = sizeof ( extra ) ;
quicklist * ql = val - > ptr ;
2014-12-19 10:41:52 -05:00
/* Add number of quicklist nodes */
2014-12-10 21:26:31 -05:00
int used = snprintf ( nextra , remaining , " ql_nodes:%u " , ql - > len ) ;
2014-12-10 22:54:19 -05:00
nextra + = used ;
remaining - = used ;
2014-12-19 10:41:52 -05:00
/* Add average quicklist fill factor */
double avg = ( double ) ql - > count / ql - > len ;
used = snprintf ( nextra , remaining , " ql_avg_node:%.2f " , avg ) ;
nextra + = used ;
remaining - = used ;
/* Add quicklist fill level / max ziplist size */
used = snprintf ( nextra , remaining , " ql_ziplist_max:%d " , ql - > fill ) ;
nextra + = used ;
remaining - = used ;
/* Add isCompressed? */
int compressed = ql - > compress ! = 0 ;
used = snprintf ( nextra , remaining , " ql_compressed:%d " , compressed ) ;
nextra + = used ;
remaining - = used ;
/* Add total uncompressed size */
unsigned long sz = 0 ;
for ( quicklistNode * node = ql - > head ; node ; node = node - > next ) {
sz + = node - > sz ;
}
used = snprintf ( nextra , remaining , " ql_uncompressed_size:%lu " , sz ) ;
nextra + = used ;
remaining - = used ;
2014-12-10 22:54:19 -05:00
}
2010-12-29 10:58:57 -05:00
addReplyStatusFormat ( c ,
" Value at:%p refcount:%d "
2015-01-18 15:54:30 -05:00
" encoding:%s serializedlength:%zu "
2014-12-10 22:54:19 -05:00
" lru:%d lru_seconds_idle:%llu%s " ,
2010-12-29 10:58:57 -05:00
( void * ) val , val - > refcount ,
2015-01-18 15:54:30 -05:00
strenc , rdbSavedObjectLen ( val ) ,
2014-12-10 22:54:19 -05:00
val - > lru , estimateObjectIdleTime ( val ) / 1000 , extra ) ;
2013-08-27 05:52:12 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " sdslen " ) & & c - > argc = = 3 ) {
dictEntry * de ;
robj * val ;
sds key ;
if ( ( de = dictFind ( c - > db - > dict , c - > argv [ 2 ] - > ptr ) ) = = NULL ) {
addReply ( c , shared . nokeyerr ) ;
return ;
}
val = dictGetVal ( de ) ;
key = dictGetKey ( de ) ;
2015-07-26 09:28:00 -04:00
if ( val - > type ! = OBJ_STRING | | ! sdsEncodedObject ( val ) ) {
2013-08-27 05:52:12 -04:00
addReplyError ( c , " Not an sds encoded string. " ) ;
} else {
addReplyStatusFormat ( c ,
" key_sds_len:%lld, key_sds_avail:%lld, "
" val_sds_len:%lld, val_sds_avail:%lld " ,
( long long ) sdslen ( key ) ,
( long long ) sdsavail ( key ) ,
( long long ) sdslen ( val - > ptr ) ,
( long long ) sdsavail ( val - > ptr ) ) ;
}
2014-09-25 11:01:56 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " populate " ) & &
( c - > argc = = 3 | | c - > argc = = 4 ) ) {
2010-06-21 18:07:48 -04:00
long keys , j ;
robj * key , * val ;
char buf [ 128 ] ;
2015-07-26 17:17:55 -04:00
if ( getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & keys , NULL ) ! = C_OK )
2010-06-21 18:07:48 -04:00
return ;
2014-05-09 09:02:29 -04:00
dictExpand ( c - > db - > dict , keys ) ;
2010-06-21 18:07:48 -04:00
for ( j = 0 ; j < keys ; j + + ) {
2014-09-25 11:01:56 -04:00
snprintf ( buf , sizeof ( buf ) , " %s:%lu " ,
2014-10-09 05:17:27 -04:00
( c - > argc = = 3 ) ? " key " : ( char * ) c - > argv [ 3 ] - > ptr , j ) ;
2010-06-21 18:07:48 -04:00
key = createStringObject ( buf , strlen ( buf ) ) ;
Better read-only behavior for expired keys in slaves.
Slaves key expire is orchestrated by the master. Sometimes the master
will send the synthesized DEL to expire keys on the slave with a non
trivial delay (when the key is not accessed, only the incremental expiry
algorithm will expire it in background).
During that time, a key is logically expired, but slaves still return
the key if you GET (or whatever) it. This is a bad behavior.
However we can't simply trust the slave view of the key, since we need
the master to be able to send write commands to update the slave data
set, and DELs should only happen when the key is expired in the master
in order to ensure consistency.
However 99.99% of the issues with this behavior is when a client which
is not a master sends a read only command. In this case we are safe and
can consider the key as non existing.
This commit does a few changes in order to make this sane:
1. lookupKeyRead() is modified in order to return NULL if the above
conditions are met.
2. Calls to lookupKeyRead() in commands actually writing to the data set
are repliaced with calls to lookupKeyWrite().
There are redundand checks, so for example, if in "2" something was
overlooked, we should be still safe, since anyway, when the master
writes the behavior is to don't care about what expireIfneeded()
returns.
This commit is related to #1768, #1770, #2131.
2014-12-10 10:10:21 -05:00
if ( lookupKeyWrite ( c - > db , key ) ! = NULL ) {
2010-06-21 18:07:48 -04:00
decrRefCount ( key ) ;
continue ;
}
snprintf ( buf , sizeof ( buf ) , " value:%lu " , j ) ;
val = createStringObject ( buf , strlen ( buf ) ) ;
dbAdd ( c - > db , key , val ) ;
2016-04-25 09:47:42 -04:00
signalModifiedKey ( c - > db , key ) ;
2010-06-21 18:07:48 -04:00
decrRefCount ( key ) ;
}
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " digest " ) & & c - > argc = = 2 ) {
unsigned char digest [ 20 ] ;
2010-09-02 13:52:24 -04:00
sds d = sdsempty ( ) ;
2010-06-21 18:07:48 -04:00
int j ;
computeDatasetDigest ( digest ) ;
for ( j = 0 ; j < 20 ; j + + )
d = sdscatprintf ( d , " %02x " , digest [ j ] ) ;
2010-09-02 13:52:24 -04:00
addReplyStatus ( c , d ) ;
sdsfree ( d ) ;
2011-06-30 07:31:44 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " sleep " ) & & c - > argc = = 3 ) {
double dtime = strtod ( c - > argv [ 2 ] - > ptr , NULL ) ;
long long utime = dtime * 1000000 ;
2012-07-19 08:37:34 -04:00
struct timespec tv ;
2011-06-30 07:31:44 -04:00
2012-07-19 08:37:34 -04:00
tv . tv_sec = utime / 1000000 ;
tv . tv_nsec = ( utime % 1000000 ) * 1000 ;
nanosleep ( & tv , NULL ) ;
2011-06-30 07:31:44 -04:00
addReply ( c , shared . ok ) ;
2013-03-27 12:55:02 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " set-active-expire " ) & &
c - > argc = = 3 )
{
server . active_expire_enabled = atoi ( c - > argv [ 2 ] - > ptr ) ;
addReply ( c , shared . ok ) ;
2015-10-30 05:13:04 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " lua-always-replicate-commands " ) & &
c - > argc = = 3 )
{
server . lua_always_replicate_commands = atoi ( c - > argv [ 2 ] - > ptr ) ;
addReply ( c , shared . ok ) ;
2014-03-10 18:01:55 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " error " ) & & c - > argc = = 3 ) {
sds errstr = sdsnewlen ( " - " , 1 ) ;
errstr = sdscatsds ( errstr , c - > argv [ 2 ] - > ptr ) ;
errstr = sdsmapchars ( errstr , " \n \r " , " " , 2 ) ; /* no newlines in errors. */
errstr = sdscatlen ( errstr , " \r \n " , 2 ) ;
addReplySds ( c , errstr ) ;
2015-01-23 12:10:14 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " structsize " ) & & c - > argc = = 2 ) {
sds sizes = sdsempty ( ) ;
2015-07-14 10:04:00 -04:00
sizes = sdscatprintf ( sizes , " bits:%d " , ( sizeof ( void * ) = = 8 ) ? 64 : 32 ) ;
sizes = sdscatprintf ( sizes , " robj:%d " , ( int ) sizeof ( robj ) ) ;
sizes = sdscatprintf ( sizes , " dictentry:%d " , ( int ) sizeof ( dictEntry ) ) ;
2015-07-16 03:14:39 -04:00
sizes = sdscatprintf ( sizes , " sdshdr5:%d " , ( int ) sizeof ( struct sdshdr5 ) ) ;
2015-07-14 10:04:00 -04:00
sizes = sdscatprintf ( sizes , " sdshdr8:%d " , ( int ) sizeof ( struct sdshdr8 ) ) ;
sizes = sdscatprintf ( sizes , " sdshdr16:%d " , ( int ) sizeof ( struct sdshdr16 ) ) ;
sizes = sdscatprintf ( sizes , " sdshdr32:%d " , ( int ) sizeof ( struct sdshdr32 ) ) ;
sizes = sdscatprintf ( sizes , " sdshdr64:%d " , ( int ) sizeof ( struct sdshdr64 ) ) ;
2015-01-23 12:10:14 -05:00
addReplyBulkSds ( c , sizes ) ;
2015-07-14 11:15:37 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " htstats " ) & & c - > argc = = 3 ) {
long dbid ;
sds stats = sdsempty ( ) ;
char buf [ 4096 ] ;
2015-07-26 17:17:55 -04:00
if ( getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & dbid , NULL ) ! = C_OK )
2015-07-14 11:15:37 -04:00
return ;
if ( dbid < 0 | | dbid > = server . dbnum ) {
addReplyError ( c , " Out of range database " ) ;
return ;
}
stats = sdscatprintf ( stats , " [Dictionary HT] \n " ) ;
dictGetStats ( buf , sizeof ( buf ) , server . db [ dbid ] . dict ) ;
stats = sdscat ( stats , buf ) ;
stats = sdscatprintf ( stats , " [Expires HT] \n " ) ;
dictGetStats ( buf , sizeof ( buf ) , server . db [ dbid ] . expires ) ;
stats = sdscat ( stats , buf ) ;
addReplyBulkSds ( c , stats ) ;
2014-11-16 13:05:48 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " jemalloc " ) & & c - > argc = = 3 ) {
# if defined(USE_JEMALLOC)
if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " info " ) ) {
sds info = sdsempty ( ) ;
je_malloc_stats_print ( inputCatSds , & info , NULL ) ;
addReplyBulkSds ( c , info ) ;
2016-04-25 09:47:42 -04:00
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " purge " ) ) {
char tmp [ 32 ] ;
unsigned narenas = 0 ;
size_t sz = sizeof ( unsigned ) ;
if ( ! je_mallctl ( " arenas.narenas " , & narenas , & sz , NULL , 0 ) ) {
sprintf ( tmp , " arena.%d.purge " , narenas ) ;
if ( ! je_mallctl ( tmp , NULL , 0 , NULL , 0 ) ) {
addReply ( c , shared . ok ) ;
return ;
}
}
addReplyError ( c , " Error purging dirty pages " ) ;
2014-11-16 13:05:48 -05:00
} else {
2016-04-25 09:47:42 -04:00
addReplyErrorFormat ( c , " Valid jemalloc debug fields: info, purge " ) ;
2014-11-16 13:05:48 -05:00
}
# else
addReplyErrorFormat ( c , " jemalloc support not available " ) ;
# endif
2010-06-21 18:07:48 -04:00
} else {
2013-03-28 06:39:02 -04:00
addReplyErrorFormat ( c , " Unknown DEBUG subcommand or wrong number of arguments for '%s' " ,
2013-03-27 12:55:02 -04:00
( char * ) c - > argv [ 1 ] - > ptr ) ;
2010-06-21 18:07:48 -04:00
}
}
2012-01-20 06:20:45 -05:00
/* =========================== Crash handling ============================== */
2016-06-20 16:08:06 -04:00
void _serverAssert ( const char * estr , const char * file , int line ) {
2011-11-24 09:47:26 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " === ASSERTION FAILED === " ) ;
serverLog ( LL_WARNING , " ==> %s:%d '%s' is not true " , file , line , estr ) ;
2010-06-21 18:07:48 -04:00
# ifdef HAVE_BACKTRACE
2011-11-24 09:47:26 -05:00
server . assert_failed = estr ;
server . assert_file = file ;
server . assert_line = line ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (forcing SIGSEGV to print the bug report.) " ) ;
2010-06-21 18:07:48 -04:00
# endif
2012-02-21 13:05:46 -05:00
* ( ( char * ) - 1 ) = ' x ' ;
2010-06-21 18:07:48 -04:00
}
2016-06-20 16:08:06 -04:00
void _serverAssertPrintClientInfo ( const client * c ) {
2011-10-04 12:05:26 -04:00
int j ;
2011-10-04 11:22:29 -04:00
2011-11-24 09:47:26 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " === ASSERTION FAILED CLIENT CONTEXT === " ) ;
serverLog ( LL_WARNING , " client->flags = %d " , c - > flags ) ;
serverLog ( LL_WARNING , " client->fd = %d " , c - > fd ) ;
serverLog ( LL_WARNING , " client->argc = %d " , c - > argc ) ;
2011-10-04 12:05:26 -04:00
for ( j = 0 ; j < c - > argc ; j + + ) {
char buf [ 128 ] ;
char * arg ;
2015-07-26 09:28:00 -04:00
if ( c - > argv [ j ] - > type = = OBJ_STRING & & sdsEncodedObject ( c - > argv [ j ] ) ) {
2011-10-04 12:05:26 -04:00
arg = ( char * ) c - > argv [ j ] - > ptr ;
} else {
2015-11-28 03:05:41 -05:00
snprintf ( buf , sizeof ( buf ) , " Object type: %u, encoding: %u " ,
2011-10-04 12:05:26 -04:00
c - > argv [ j ] - > type , c - > argv [ j ] - > encoding ) ;
arg = buf ;
2011-10-04 11:22:29 -04:00
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " client->argv[%d] = \" %s \" (refcount: %d) " ,
2011-10-04 12:05:26 -04:00
j , arg , c - > argv [ j ] - > refcount ) ;
}
}
2016-06-20 16:08:06 -04:00
void serverLogObjectDebugInfo ( const robj * o ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Object type: %d " , o - > type ) ;
serverLog ( LL_WARNING , " Object encoding: %d " , o - > encoding ) ;
serverLog ( LL_WARNING , " Object refcount: %d " , o - > refcount ) ;
2015-07-26 09:28:00 -04:00
if ( o - > type = = OBJ_STRING & & sdsEncodedObject ( o ) ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Object raw string len: %zu " , sdslen ( o - > ptr ) ) ;
2013-06-04 09:53:53 -04:00
if ( sdslen ( o - > ptr ) < 4096 ) {
sds repr = sdscatrepr ( sdsempty ( ) , o - > ptr , sdslen ( o - > ptr ) ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Object raw string content: %s " , repr ) ;
2013-06-04 09:53:53 -04:00
sdsfree ( repr ) ;
}
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_LIST ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " List length: %d " , ( int ) listTypeLength ( o ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_SET ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Set size: %d " , ( int ) setTypeSize ( o ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_HASH ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Hash size: %d " , ( int ) hashTypeLength ( o ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_ZSET ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Sorted set size: %d " , ( int ) zsetLength ( o ) ) ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_SKIPLIST )
2016-06-20 16:08:06 -04:00
serverLog ( LL_WARNING , " Skiplist level: %d " , ( int ) ( ( const zset * ) o - > ptr ) - > zsl - > level ) ;
2011-10-04 11:22:29 -04:00
}
}
2016-06-20 16:08:06 -04:00
void _serverAssertPrintObject ( const robj * o ) {
2012-01-12 10:02:57 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " === ASSERTION FAILED OBJECT CONTEXT === " ) ;
2015-07-26 09:17:43 -04:00
serverLogObjectDebugInfo ( o ) ;
2012-01-12 10:02:57 -05:00
}
2016-06-20 16:08:06 -04:00
void _serverAssertWithInfo ( const client * c , const robj * o , const char * estr , const char * file , int line ) {
2015-07-26 09:29:53 -04:00
if ( c ) _serverAssertPrintClientInfo ( c ) ;
if ( o ) _serverAssertPrintObject ( o ) ;
_serverAssert ( estr , file , line ) ;
2011-10-04 11:22:29 -04:00
}
2016-06-20 16:08:06 -04:00
void _serverPanic ( const char * msg , const char * file , int line ) {
2011-11-24 09:47:26 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " ------------------------------------------------ " ) ;
serverLog ( LL_WARNING , " !!! Software Failure. Press left mouse button to continue " ) ;
serverLog ( LL_WARNING , " Guru Meditation: %s #%s:%d " , msg , file , line ) ;
2010-06-21 18:07:48 -04:00
# ifdef HAVE_BACKTRACE
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (forcing SIGSEGV in order to print the stack trace) " ) ;
2012-02-21 13:05:46 -05:00
# endif
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " ------------------------------------------------ " ) ;
2010-06-21 18:07:48 -04:00
* ( ( char * ) - 1 ) = ' x ' ;
}
2012-01-20 06:20:45 -05:00
2012-02-08 16:24:59 -05:00
void bugReportStart ( void ) {
if ( server . bug_report_start = = 0 ) {
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
" \n \n === REDIS BUG REPORT START: Cut & paste starting from here === \n " ) ;
2012-02-08 16:24:59 -05:00
server . bug_report_start = 1 ;
}
}
2012-01-20 06:20:45 -05:00
# ifdef HAVE_BACKTRACE
static void * getMcontextEip ( ucontext_t * uc ) {
2012-04-24 05:07:15 -04:00
# if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
/* OSX < 10.6 */
# if defined(__x86_64__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __rip ;
2012-04-24 05:07:15 -04:00
# elif defined(__i386__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __eip ;
2012-04-24 05:07:15 -04:00
# else
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __srr0 ;
2012-04-24 05:07:15 -04:00
# endif
2012-01-20 06:20:45 -05:00
# elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
2012-04-24 05:07:15 -04:00
/* OSX >= 10.6 */
# if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __rip ;
2012-04-24 05:07:15 -04:00
# else
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __eip ;
2012-04-24 05:07:15 -04:00
# endif
# elif defined(__linux__)
/* Linux */
# if defined(__i386__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext . gregs [ 14 ] ; /* Linux 32 */
2012-04-24 05:07:15 -04:00
# elif defined(__X86_64__) || defined(__x86_64__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext . gregs [ 16 ] ; /* Linux 64 */
2012-04-24 05:07:15 -04:00
# elif defined(__ia64__) /* Linux IA64 */
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext . sc_ip ;
2016-07-04 16:28:32 -04:00
# elif defined(__arm__) /* Linux ARM */
return ( void * ) uc - > uc_mcontext . arm_pc ;
2012-04-24 05:07:15 -04:00
# endif
2012-01-20 06:20:45 -05:00
# else
return NULL ;
# endif
}
void logStackContent ( void * * sp ) {
int i ;
for ( i = 15 ; i > = 0 ; i - - ) {
2013-02-27 06:27:15 -05:00
unsigned long addr = ( unsigned long ) sp + i ;
unsigned long val = ( unsigned long ) sp [ i ] ;
2012-01-20 10:40:43 -05:00
if ( sizeof ( long ) = = 4 )
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (%08lx) -> %08lx " , addr , val ) ;
2012-01-20 10:40:43 -05:00
else
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (%016lx) -> %016lx " , addr , val ) ;
2012-01-20 06:20:45 -05:00
}
}
void logRegisters ( ucontext_t * uc ) {
2015-12-16 03:13:41 -05:00
serverLog ( LL_WARNING | LL_RAW , " \n ------ REGISTERS ------ \n " ) ;
2012-04-24 05:07:15 -04:00
/* OSX */
2012-01-20 06:20:45 -05:00
# if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
2012-04-24 05:07:15 -04:00
/* OSX AMD64 */
# if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:20:45 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" RAX:%016lx RBX:%016lx \n RCX:%016lx RDX:%016lx \n "
" RDI:%016lx RSI:%016lx \n RBP:%016lx RSP:%016lx \n "
" R8 :%016lx R9 :%016lx \n R10:%016lx R11:%016lx \n "
" R12:%016lx R13:%016lx \n R14:%016lx R15:%016lx \n "
" RIP:%016lx EFL:%016lx \n CS :%016lx FS:%016lx GS:%016lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext - > __ss . __rax ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rbx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rcx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rdx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rdi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rsi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rbp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rsp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r8 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r9 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r10 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r11 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r12 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r13 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r14 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r15 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rip ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rflags ,
( unsigned long ) uc - > uc_mcontext - > __ss . __cs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __fs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __gs
2012-01-20 06:20:45 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext - > __ss . __rsp ) ;
2012-04-24 05:07:15 -04:00
# else
/* OSX x86 */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:20:45 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx \n "
" EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx \n "
" SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx \n "
" DS:%08lx ES:%08lx FS :%08lx GS :%08lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext - > __ss . __eax ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ebx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ecx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __edx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __edi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __esi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ebp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __esp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ss ,
( unsigned long ) uc - > uc_mcontext - > __ss . __eflags ,
( unsigned long ) uc - > uc_mcontext - > __ss . __eip ,
( unsigned long ) uc - > uc_mcontext - > __ss . __cs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ds ,
( unsigned long ) uc - > uc_mcontext - > __ss . __es ,
( unsigned long ) uc - > uc_mcontext - > __ss . __fs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __gs
2012-01-20 06:20:45 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext - > __ss . __esp ) ;
2012-04-24 05:07:15 -04:00
# endif
/* Linux */
# elif defined(__linux__)
/* Linux x86 */
# if defined(__i386__)
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 08:37:50 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx \n "
" EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx \n "
" SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx \n "
" DS :%08lx ES :%08lx FS :%08lx GS:%08lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext . gregs [ 11 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 8 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 10 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 9 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 4 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 5 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 6 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 7 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 18 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 17 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 14 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 15 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 3 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 2 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 1 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 0 ]
2012-01-20 08:37:50 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext . gregs [ 7 ] ) ;
2012-04-24 05:07:15 -04:00
# elif defined(__X86_64__) || defined(__x86_64__)
/* Linux AMD64 */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:54:15 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" RAX:%016lx RBX:%016lx \n RCX:%016lx RDX:%016lx \n "
" RDI:%016lx RSI:%016lx \n RBP:%016lx RSP:%016lx \n "
" R8 :%016lx R9 :%016lx \n R10:%016lx R11:%016lx \n "
" R12:%016lx R13:%016lx \n R14:%016lx R15:%016lx \n "
" RIP:%016lx EFL:%016lx \n CSGSFS:%016lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext . gregs [ 13 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 11 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 14 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 12 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 8 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 9 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 10 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 15 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 0 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 1 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 2 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 3 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 4 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 5 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 6 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 7 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 16 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 17 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 18 ]
2012-01-20 06:54:15 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext . gregs [ 15 ] ) ;
2012-04-24 05:07:15 -04:00
# endif
2012-01-20 06:20:45 -05:00
# else
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:20:45 -05:00
" Dumping of registers not supported for this OS/arch " ) ;
# endif
}
2015-12-16 11:41:20 -05:00
/* Return a file descriptor to write directly to the Redis log with the
* write ( 2 ) syscall , that can be used in critical sections of the code
* where the rest of Redis can ' t be trusted ( for example during the memory
* test ) or when an API call requires a raw fd .
*
* Close it with closeDirectLogFiledes ( ) . */
int openDirectLogFiledes ( void ) {
int log_to_stdout = server . logfile [ 0 ] = = ' \0 ' ;
int fd = log_to_stdout ?
STDOUT_FILENO :
open ( server . logfile , O_APPEND | O_CREAT | O_WRONLY , 0644 ) ;
return fd ;
}
/* Used to close what closeDirectLogFiledes() returns. */
void closeDirectLogFiledes ( int fd ) {
int log_to_stdout = server . logfile [ 0 ] = = ' \0 ' ;
if ( ! log_to_stdout ) close ( fd ) ;
}
2012-04-26 10:04:53 -04:00
/* Logs the stack trace using the backtrace() call. This function is designed
* to be called from signal handlers safely . */
void logStackTrace ( ucontext_t * uc ) {
2015-12-16 03:13:41 -05:00
void * trace [ 101 ] ;
2015-12-16 11:41:20 -05:00
int trace_size = 0 , fd = openDirectLogFiledes ( ) ;
2012-04-26 10:04:53 -04:00
2015-12-16 11:41:20 -05:00
if ( fd = = - 1 ) return ; /* If we can't log there is anything to do. */
2012-01-20 06:20:45 -05:00
/* Generate the stack trace */
2015-12-16 03:13:41 -05:00
trace_size = backtrace ( trace + 1 , 100 ) ;
if ( getMcontextEip ( uc ) ! = NULL ) {
char * msg1 = " EIP: \n " ;
char * msg2 = " \n Backtrace: \n " ;
2015-12-16 06:36:29 -05:00
if ( write ( fd , msg1 , strlen ( msg1 ) ) = = - 1 ) { /* Avoid warning. */ } ;
2015-12-16 03:13:41 -05:00
trace [ 0 ] = getMcontextEip ( uc ) ;
backtrace_symbols_fd ( trace , 1 , fd ) ;
2015-12-16 06:36:29 -05:00
if ( write ( fd , msg2 , strlen ( msg2 ) ) = = - 1 ) { /* Avoid warning. */ } ;
2015-12-16 03:13:41 -05:00
}
2012-04-26 10:04:53 -04:00
/* Write symbols to log file */
2015-12-16 03:13:41 -05:00
backtrace_symbols_fd ( trace + 1 , trace_size , fd ) ;
2012-04-26 10:04:53 -04:00
/* Cleanup */
2015-12-16 11:41:20 -05:00
closeDirectLogFiledes ( fd ) ;
2012-03-27 04:33:45 -04:00
}
/* Log information about the "current" client, that is, the client that is
* currently being served by Redis . May be NULL if Redis is not serving a
* client right now . */
void logCurrentClient ( void ) {
if ( server . current_client = = NULL ) return ;
2015-07-26 09:20:46 -04:00
client * cc = server . current_client ;
2012-03-27 04:33:45 -04:00
sds client ;
int j ;
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ CURRENT CLIENT INFO ------ \n " ) ;
2014-04-28 11:36:57 -04:00
client = catClientInfoString ( sdsempty ( ) , cc ) ;
2015-12-16 03:13:41 -05:00
serverLog ( LL_WARNING | LL_RAW , " %s \n " , client ) ;
2012-03-27 04:33:45 -04:00
sdsfree ( client ) ;
for ( j = 0 ; j < cc - > argc ; j + + ) {
robj * decoded ;
decoded = getDecodedObject ( cc - > argv [ j ] ) ;
2015-12-16 03:13:41 -05:00
serverLog ( LL_WARNING | LL_RAW , " argv[%d]: '%s' \n " , j ,
( char * ) decoded - > ptr ) ;
2012-03-27 04:33:45 -04:00
decrRefCount ( decoded ) ;
}
/* Check if the first argument, usually a key, is found inside the
* selected DB , and if so print info about the associated object . */
if ( cc - > argc > = 1 ) {
robj * val , * key ;
dictEntry * de ;
key = getDecodedObject ( cc - > argv [ 1 ] ) ;
de = dictFind ( cc - > db - > dict , key - > ptr ) ;
if ( de ) {
val = dictGetVal ( de ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " key '%s' found in DB containing the following object: " , ( char * ) key - > ptr ) ;
2015-07-26 09:17:43 -04:00
serverLogObjectDebugInfo ( val ) ;
2012-03-27 04:33:45 -04:00
}
decrRefCount ( key ) ;
}
}
2012-11-21 07:19:38 -05:00
# if defined(HAVE_PROC_MAPS)
2015-12-16 11:41:20 -05:00
2012-11-25 10:21:21 -05:00
# define MEMTEST_MAX_REGIONS 128
2012-11-21 07:19:38 -05:00
2015-12-16 11:41:20 -05:00
/* A non destructive memory test executed during segfauls. */
2012-11-21 07:19:38 -05:00
int memtest_test_linux_anonymous_maps ( void ) {
2015-12-16 11:41:20 -05:00
FILE * fp ;
2012-11-21 07:19:38 -05:00
char line [ 1024 ] ;
2015-12-16 11:41:20 -05:00
char logbuf [ 1024 ] ;
2012-11-21 07:19:38 -05:00
size_t start_addr , end_addr , size ;
2012-11-25 10:21:21 -05:00
size_t start_vect [ MEMTEST_MAX_REGIONS ] ;
size_t size_vect [ MEMTEST_MAX_REGIONS ] ;
int regions = 0 , j ;
2012-11-21 07:19:38 -05:00
2015-12-16 11:41:20 -05:00
int fd = openDirectLogFiledes ( ) ;
if ( ! fd ) return 0 ;
fp = fopen ( " /proc/self/maps " , " r " ) ;
if ( ! fp ) return 0 ;
2012-11-21 07:19:38 -05:00
while ( fgets ( line , sizeof ( line ) , fp ) ! = NULL ) {
char * start , * end , * p = line ;
start = p ;
p = strchr ( p , ' - ' ) ;
if ( ! p ) continue ;
* p + + = ' \0 ' ;
end = p ;
p = strchr ( p , ' ' ) ;
if ( ! p ) continue ;
* p + + = ' \0 ' ;
if ( strstr ( p , " stack " ) | |
strstr ( p , " vdso " ) | |
strstr ( p , " vsyscall " ) ) continue ;
if ( ! strstr ( p , " 00:00 " ) ) continue ;
if ( ! strstr ( p , " rw " ) ) continue ;
start_addr = strtoul ( start , NULL , 16 ) ;
end_addr = strtoul ( end , NULL , 16 ) ;
size = end_addr - start_addr ;
2012-11-25 10:21:21 -05:00
start_vect [ regions ] = start_addr ;
size_vect [ regions ] = size ;
2015-12-16 11:41:20 -05:00
snprintf ( logbuf , sizeof ( logbuf ) ,
" *** Preparing to test memory region %lx (%lu bytes) \n " ,
( unsigned long ) start_vect [ regions ] ,
( unsigned long ) size_vect [ regions ] ) ;
if ( write ( fd , logbuf , strlen ( logbuf ) ) = = - 1 ) { /* Nothing to do. */ }
2012-11-25 10:21:21 -05:00
regions + + ;
2012-11-21 07:19:38 -05:00
}
2012-11-25 10:21:21 -05:00
2015-12-16 11:41:20 -05:00
int errors = 0 ;
2012-11-25 10:21:21 -05:00
for ( j = 0 ; j < regions ; j + + ) {
2015-12-16 11:41:20 -05:00
if ( write ( fd , " . " , 1 ) = = - 1 ) { /* Nothing to do. */ }
errors + = memtest_preserving_test ( ( void * ) start_vect [ j ] , size_vect [ j ] , 1 ) ;
if ( write ( fd , errors ? " E " : " O " , 1 ) = = - 1 ) { /* Nothing to do. */ }
2012-11-25 10:21:21 -05:00
}
2015-12-16 11:41:20 -05:00
if ( write ( fd , " \n " , 1 ) = = - 1 ) { /* Nothing to do. */ }
2012-11-25 10:21:21 -05:00
/* NOTE: It is very important to close the file descriptor only now
* because closing it before may result into unmapping of some memory
* region that we are testing . */
2012-11-21 07:19:38 -05:00
fclose ( fp ) ;
2015-12-16 11:41:20 -05:00
closeDirectLogFiledes ( fd ) ;
return errors ;
2012-11-21 07:19:38 -05:00
}
# endif
2012-03-27 04:33:45 -04:00
void sigsegvHandler ( int sig , siginfo_t * info , void * secret ) {
ucontext_t * uc = ( ucontext_t * ) secret ;
2015-12-16 03:13:41 -05:00
void * eip = getMcontextEip ( uc ) ;
2012-04-26 10:04:53 -04:00
sds infostring , clients ;
2012-03-27 04:33:45 -04:00
struct sigaction act ;
2015-07-27 03:41:48 -04:00
UNUSED ( info ) ;
2012-03-27 04:33:45 -04:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2015-12-16 03:13:41 -05:00
" Redis %s crashed by signal: %d " , REDIS_VERSION , sig ) ;
if ( eip ! = NULL ) {
2015-12-15 12:00:29 -05:00
serverLog ( LL_WARNING ,
2015-12-16 03:13:41 -05:00
" Crashed running the instuction at: %p " , eip ) ;
}
if ( sig = = SIGSEGV | | sig = = SIGBUS ) {
serverLog ( LL_WARNING ,
" Accessing address: %p " , ( void * ) info - > si_addr ) ;
2015-12-15 12:00:29 -05:00
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2015-12-16 03:13:41 -05:00
" Failed assertion: %s (%s:%d) " , server . assert_failed ,
2012-03-27 04:33:45 -04:00
server . assert_file , server . assert_line ) ;
/* Log the stack trace */
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ STACK TRACE ------ \n " ) ;
2012-04-26 10:04:53 -04:00
logStackTrace ( uc ) ;
2012-01-20 06:20:45 -05:00
/* Log INFO and CLIENT LIST */
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ INFO OUTPUT ------ \n " ) ;
2012-01-20 06:20:45 -05:00
infostring = genRedisInfoString ( " all " ) ;
2012-01-21 17:05:32 -05:00
infostring = sdscatprintf ( infostring , " hash_init_value: %u \n " ,
dictGetHashFunctionSeed ( ) ) ;
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , infostring ) ;
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ CLIENT LIST OUTPUT ------ \n " ) ;
2012-01-20 06:20:45 -05:00
clients = getAllClientsInfoString ( ) ;
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , clients ) ;
2012-03-27 04:33:45 -04:00
sdsfree ( infostring ) ;
sdsfree ( clients ) ;
2012-01-20 06:20:45 -05:00
2012-03-27 04:33:45 -04:00
/* Log the current client */
logCurrentClient ( ) ;
2012-01-20 06:20:45 -05:00
/* Log dump of processor registers */
logRegisters ( uc ) ;
2012-11-21 07:19:38 -05:00
# if defined(HAVE_PROC_MAPS)
/* Test memory */
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ FAST MEMORY TEST ------ \n " ) ;
2012-11-22 04:08:44 -05:00
bioKillThreads ( ) ;
2012-11-21 07:19:38 -05:00
if ( memtest_test_linux_anonymous_maps ( ) ) {
2015-12-16 11:41:20 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
2012-11-21 07:19:38 -05:00
" !!! MEMORY ERROR DETECTED! Check your memory ASAP !!! " ) ;
} else {
2015-12-16 11:41:20 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
2012-11-21 07:19:38 -05:00
" Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible. " ) ;
}
# endif
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
2012-01-20 06:20:45 -05:00
" \n === REDIS BUG REPORT END. Make sure to include from START to END. === \n \n "
2014-08-28 10:36:32 -04:00
" Please report the crash by opening an issue on github: \n \n "
2012-03-18 06:35:35 -04:00
" http://github.com/antirez/redis/issues \n \n "
2014-05-28 03:46:01 -04:00
" Suspect RAM error? Use redis-server --test-memory to verify it. \n \n "
2012-01-20 06:20:45 -05:00
) ;
/* free(messages); Don't call free() with possibly corrupted memory. */
2015-01-12 09:58:23 -05:00
if ( server . daemonize & & server . supervised = = 0 ) unlink ( server . pidfile ) ;
2012-01-20 06:20:45 -05:00
2016-07-04 16:28:32 -04:00
if ( eip ! = NULL ) {
Dl_info info ;
if ( dladdr ( eip , & info ) ! = 0 )
{
serverLog ( LL_WARNING ,
" symbol: %s (base %p), in module: %s (base: %p) " ,
info . dli_sname , info . dli_saddr , info . dli_fname , info . dli_fbase ) ;
size_t len = ( long ) eip - ( long ) info . dli_saddr ;
long sz = sysconf ( _SC_PAGESIZE ) ;
if ( len < 1 < < 13 ) { /* we don't have functions over 8k (verified) */
long end = ( ( long ) eip + sz ) & ~ ( sz - 1 ) ; /* round up to page boundary */
len = end - ( long ) info . dli_saddr ;
serverLogHexDump ( LL_WARNING , " dump of function " , info . dli_saddr , len ) ;
}
}
}
2012-01-20 06:20:45 -05: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 ) ;
act . sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND ;
act . sa_handler = SIG_DFL ;
sigaction ( sig , & act , NULL ) ;
kill ( getpid ( ) , sig ) ;
}
# endif /* HAVE_BACKTRACE */
2012-03-27 05:47:51 -04:00
2012-06-11 17:44:34 -04:00
/* ==================== Logging functions for debugging ===================== */
2015-07-26 09:17:43 -04:00
void serverLogHexDump ( int level , char * descr , void * value , size_t len ) {
2012-06-11 17:44:34 -04:00
char buf [ 65 ] , * b ;
unsigned char * v = value ;
char charset [ ] = " 0123456789abcdef " ;
2015-07-26 09:17:43 -04:00
serverLog ( level , " %s (hexdump): " , descr ) ;
2012-06-11 17:44:34 -04:00
b = buf ;
while ( len ) {
b [ 0 ] = charset [ ( * v ) > > 4 ] ;
b [ 1 ] = charset [ ( * v ) & 0xf ] ;
b [ 2 ] = ' \0 ' ;
b + = 2 ;
len - - ;
v + + ;
if ( b - buf = = 64 | | len = = 0 ) {
2015-07-27 03:41:48 -04:00
serverLogRaw ( level | LL_RAW , buf ) ;
2012-06-11 17:44:34 -04:00
b = buf ;
}
}
2015-07-27 03:41:48 -04:00
serverLogRaw ( level | LL_RAW , " \n " ) ;
2012-06-11 17:44:34 -04:00
}
2012-03-27 05:47:51 -04:00
/* =========================== Software Watchdog ============================ */
# include <sys/time.h>
void watchdogSignalHandler ( int sig , siginfo_t * info , void * secret ) {
2012-04-24 05:07:15 -04:00
# ifdef HAVE_BACKTRACE
2012-03-27 05:47:51 -04:00
ucontext_t * uc = ( ucontext_t * ) secret ;
2012-04-24 05:07:15 -04:00
# endif
2015-07-27 03:41:48 -04:00
UNUSED ( info ) ;
UNUSED ( sig ) ;
2012-03-27 05:47:51 -04:00
2015-07-27 03:41:48 -04:00
serverLogFromHandler ( LL_WARNING , " \n --- WATCHDOG TIMER EXPIRED --- " ) ;
2012-03-27 05:47:51 -04:00
# ifdef HAVE_BACKTRACE
2012-04-26 10:04:53 -04:00
logStackTrace ( uc ) ;
2012-03-27 09:24:33 -04:00
# else
2015-07-27 03:41:48 -04:00
serverLogFromHandler ( LL_WARNING , " Sorry: no support for backtrace(). " ) ;
2012-03-27 05:47:51 -04:00
# endif
2015-07-27 03:41:48 -04:00
serverLogFromHandler ( LL_WARNING , " -------- \n " ) ;
2012-03-27 05:47:51 -04:00
}
/* Schedule a SIGALRM delivery after the specified period in milliseconds.
* If a timer is already scheduled , this function will re - schedule it to the
* specified time . If period is 0 the current timer is disabled . */
void watchdogScheduleSignal ( int period ) {
struct itimerval it ;
/* Will stop the timer if period is 0. */
it . it_value . tv_sec = period / 1000 ;
2012-03-27 06:11:37 -04:00
it . it_value . tv_usec = ( period % 1000 ) * 1000 ;
2012-03-27 05:47:51 -04:00
/* Don't automatically restart. */
it . it_interval . tv_sec = 0 ;
it . it_interval . tv_usec = 0 ;
setitimer ( ITIMER_REAL , & it , NULL ) ;
}
2012-11-27 14:41:33 -05:00
/* Enable the software watchdog with the specified period in milliseconds. */
2012-03-27 05:47:51 -04:00
void enableWatchdog ( int period ) {
2012-05-13 15:52:35 -04:00
int min_period ;
2012-03-27 05:47:51 -04:00
if ( server . watchdog_period = = 0 ) {
struct sigaction act ;
/* Watchdog was actually disabled, so we have to setup the signal
* handler . */
sigemptyset ( & act . sa_mask ) ;
2014-05-22 15:34:02 -04:00
act . sa_flags = SA_ONSTACK | SA_SIGINFO ;
2012-03-27 05:47:51 -04:00
act . sa_sigaction = watchdogSignalHandler ;
sigaction ( SIGALRM , & act , NULL ) ;
}
2012-05-13 15:52:35 -04:00
/* If the configured period is smaller than twice the timer period, it is
* too short for the software watchdog to work reliably . Fix it now
* if needed . */
2012-12-14 11:10:40 -05:00
min_period = ( 1000 / server . hz ) * 2 ;
2012-05-13 15:52:35 -04:00
if ( period < min_period ) period = min_period ;
2012-03-27 05:47:51 -04:00
watchdogScheduleSignal ( period ) ; /* Adjust the current timer. */
server . watchdog_period = period ;
}
/* Disable the software watchdog. */
void disableWatchdog ( void ) {
struct sigaction act ;
if ( server . watchdog_period = = 0 ) return ; /* Already disabled. */
watchdogScheduleSignal ( 0 ) ; /* Stop the current timer. */
/* Set the signal handler to SIG_IGN, this will also remove pending
* signals from the queue . */
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_IGN ;
sigaction ( SIGALRM , & act , NULL ) ;
server . watchdog_period = 0 ;
}