2012-11-08 12:25:23 -05:00
/* Configuration file parsing and CONFIG GET/SET commands implementation.
*
* 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 .
*/
2010-06-21 18:07:48 -04:00
# include "redis.h"
2013-10-09 09:37:20 -04:00
# include "cluster.h"
2010-06-21 18:07:48 -04:00
2013-05-14 06:32:25 -04:00
# include <fcntl.h>
# include <sys/stat.h>
2015-03-11 11:59:56 -04:00
/*-----------------------------------------------------------------------------
* Config file name - value maps .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
typedef struct configEnum {
const char * name ;
const int val ;
} configEnum ;
configEnum maxmemory_policy_enum [ ] = {
{ " volatile-lru " , REDIS_MAXMEMORY_VOLATILE_LRU } ,
{ " volatile-random " , REDIS_MAXMEMORY_VOLATILE_RANDOM } ,
{ " volatile-ttl " , REDIS_MAXMEMORY_VOLATILE_TTL } ,
{ " allkeys-lru " , REDIS_MAXMEMORY_ALLKEYS_LRU } ,
{ " allkeys-random " , REDIS_MAXMEMORY_ALLKEYS_RANDOM } ,
{ " noeviction " , REDIS_MAXMEMORY_NO_EVICTION } ,
{ NULL , 0 }
} ;
configEnum syslog_facility_enum [ ] = {
2013-05-13 05:11:35 -04:00
{ " user " , LOG_USER } ,
{ " local0 " , LOG_LOCAL0 } ,
{ " local1 " , LOG_LOCAL1 } ,
{ " local2 " , LOG_LOCAL2 } ,
{ " local3 " , LOG_LOCAL3 } ,
{ " local4 " , LOG_LOCAL4 } ,
{ " local5 " , LOG_LOCAL5 } ,
{ " local6 " , LOG_LOCAL6 } ,
{ " local7 " , LOG_LOCAL7 } ,
{ NULL , 0 }
} ;
2015-03-11 18:20:57 -04:00
configEnum loglevel_enum [ ] = {
{ " debug " , REDIS_DEBUG } ,
{ " verbose " , REDIS_VERBOSE } ,
{ " notice " , REDIS_NOTICE } ,
{ " warning " , REDIS_WARNING } ,
{ NULL , 0 }
} ;
configEnum supervised_mode_enum [ ] = {
{ " upstart " , REDIS_SUPERVISED_UPSTART } ,
{ " systemd " , REDIS_SUPERVISED_SYSTEMD } ,
{ " auto " , REDIS_SUPERVISED_AUTODETECT } ,
{ " no " , REDIS_SUPERVISED_NONE } ,
{ NULL , 0 }
} ;
configEnum aof_fsync_enum [ ] = {
{ " everysec " , AOF_FSYNC_EVERYSEC } ,
{ " always " , AOF_FSYNC_ALWAYS } ,
{ " no " , AOF_FSYNC_NO } ,
{ NULL , 0 }
} ;
2015-03-11 11:59:56 -04:00
/* Output buffer limits presets. */
2014-06-16 04:43:05 -04:00
clientBufferLimitsConfig clientBufferLimitsDefaults [ REDIS_CLIENT_TYPE_COUNT ] = {
2013-05-13 12:34:18 -04:00
{ 0 , 0 , 0 } , /* normal */
{ 1024 * 1024 * 256 , 1024 * 1024 * 64 , 60 } , /* slave */
{ 1024 * 1024 * 32 , 1024 * 1024 * 8 , 60 } /* pubsub */
} ;
2015-03-11 11:59:56 -04:00
/*-----------------------------------------------------------------------------
* Enum access functions
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Get enum value from name. If there is no match INT_MIN is returned. */
int configEnumGetValue ( configEnum * ce , char * name ) {
while ( ce - > name ! = NULL ) {
if ( ! strcasecmp ( ce - > name , name ) ) return ce - > val ;
ce + + ;
}
return INT_MIN ;
}
/* Get enum name from value. If no match is found NULL is returned. */
const char * configEnumGetName ( configEnum * ce , int val ) {
while ( ce - > name ! = NULL ) {
if ( ce - > val = = val ) return ce - > name ;
ce + + ;
}
return NULL ;
}
/* Wrapper for configEnumGetName() returning "unknown" insetad of NULL if
* there is no match . */
const char * configEnumGetNameOrUnknown ( configEnum * ce , int val ) {
const char * name = configEnumGetName ( ce , val ) ;
return name ? name : " unknown " ;
}
/* Used for INFO generation. */
const char * maxmemoryToString ( void ) {
return configEnumGetNameOrUnknown ( maxmemory_policy_enum , server . maxmemory ) ;
}
2010-06-21 18:07:48 -04:00
/*-----------------------------------------------------------------------------
* Config file parsing
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int yesnotoi ( char * s ) {
if ( ! strcasecmp ( s , " yes " ) ) return 1 ;
else if ( ! strcasecmp ( s , " no " ) ) return 0 ;
else return - 1 ;
}
void appendServerSaveParams ( time_t seconds , int changes ) {
server . saveparams = zrealloc ( server . saveparams , sizeof ( struct saveparam ) * ( server . saveparamslen + 1 ) ) ;
server . saveparams [ server . saveparamslen ] . seconds = seconds ;
server . saveparams [ server . saveparamslen ] . changes = changes ;
server . saveparamslen + + ;
}
2014-03-15 19:51:54 -04:00
void resetServerSaveParams ( void ) {
2010-06-21 18:07:48 -04:00
zfree ( server . saveparams ) ;
server . saveparams = NULL ;
server . saveparamslen = 0 ;
}
2011-12-01 07:44:53 -05:00
void loadServerConfigFromString ( char * config ) {
char * err = NULL ;
int linenum = 0 , totlines , i ;
2014-01-20 05:08:14 -05:00
int slaveof_linenum = 0 ;
2011-12-01 07:44:53 -05:00
sds * lines ;
2010-06-21 18:07:48 -04:00
2011-12-01 07:44:53 -05:00
lines = sdssplitlen ( config , strlen ( config ) , " \n " , 1 , & totlines ) ;
2010-06-21 18:07:48 -04:00
2011-12-01 07:44:53 -05:00
for ( i = 0 ; i < totlines ; i + + ) {
2010-06-21 18:07:48 -04:00
sds * argv ;
2011-12-01 07:44:53 -05:00
int argc ;
2010-06-21 18:07:48 -04:00
2011-12-01 07:44:53 -05:00
linenum = i + 1 ;
lines [ i ] = sdstrim ( lines [ i ] , " \t \r \n " ) ;
2010-06-21 18:07:48 -04:00
2013-03-06 06:40:48 -05:00
/* Skip comments and blank lines */
2011-12-01 07:44:53 -05:00
if ( lines [ i ] [ 0 ] = = ' # ' | | lines [ i ] [ 0 ] = = ' \0 ' ) continue ;
2010-06-21 18:07:48 -04:00
/* Split into arguments */
2011-12-01 07:44:53 -05:00
argv = sdssplitargs ( lines [ i ] , & argc ) ;
2013-02-06 00:47:14 -05:00
if ( argv = = NULL ) {
2013-03-06 06:24:12 -05:00
err = " Unbalanced quotes in configuration line " ;
2013-02-06 00:47:14 -05:00
goto loaderr ;
}
2013-03-06 06:40:48 -05:00
/* Skip this line if the resulting command vector is empty. */
if ( argc = = 0 ) {
sdsfreesplitres ( argv , argc ) ;
2013-05-18 10:21:52 -04:00
continue ;
2013-03-06 06:40:48 -05:00
}
2010-06-21 18:07:48 -04:00
sdstolower ( argv [ 0 ] ) ;
/* Execute config directives */
if ( ! strcasecmp ( argv [ 0 ] , " timeout " ) & & argc = = 2 ) {
server . maxidletime = atoi ( argv [ 1 ] ) ;
if ( server . maxidletime < 0 ) {
err = " Invalid timeout value " ; goto loaderr ;
}
2013-02-08 10:40:59 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " tcp-keepalive " ) & & argc = = 2 ) {
server . tcpkeepalive = atoi ( argv [ 1 ] ) ;
if ( server . tcpkeepalive < 0 ) {
err = " Invalid tcp-keepalive value " ; goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " port " ) & & argc = = 2 ) {
server . port = atoi ( argv [ 1 ] ) ;
2011-02-22 05:49:17 -05:00
if ( server . port < 0 | | server . port > 65535 ) {
2010-06-21 18:07:48 -04:00
err = " Invalid port " ; goto loaderr ;
}
2014-01-31 08:55:43 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " tcp-backlog " ) & & argc = = 2 ) {
server . tcp_backlog = atoi ( argv [ 1 ] ) ;
if ( server . tcp_backlog < 0 ) {
2013-11-08 14:55:48 -05:00
err = " Invalid backlog value " ; goto loaderr ;
}
2013-07-04 12:50:15 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " bind " ) & & argc > = 2 ) {
int j , addresses = argc - 1 ;
if ( addresses > REDIS_BINDADDR_MAX ) {
err = " Too many bind addresses specified " ; goto loaderr ;
}
for ( j = 0 ; j < addresses ; j + + )
server . bindaddr [ j ] = zstrdup ( argv [ j + 1 ] ) ;
server . bindaddr_count = addresses ;
2010-10-13 11:17:56 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " unixsocket " ) & & argc = = 2 ) {
server . unixsocket = zstrdup ( argv [ 1 ] ) ;
2011-10-10 14:21:15 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " unixsocketperm " ) & & argc = = 2 ) {
2011-10-20 21:20:58 -04:00
errno = 0 ;
2011-10-10 14:21:15 -04:00
server . unixsocketperm = ( mode_t ) strtol ( argv [ 1 ] , NULL , 8 ) ;
if ( errno | | server . unixsocketperm > 0777 ) {
err = " Invalid socket file permissions " ; goto loaderr ;
}
2012-01-16 10:50:24 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " save " ) ) {
if ( argc = = 3 ) {
int seconds = atoi ( argv [ 1 ] ) ;
int changes = atoi ( argv [ 2 ] ) ;
if ( seconds < 1 | | changes < 0 ) {
err = " Invalid save parameters " ; goto loaderr ;
}
appendServerSaveParams ( seconds , changes ) ;
} else if ( argc = = 2 & & ! strcasecmp ( argv [ 1 ] , " " ) ) {
resetServerSaveParams ( ) ;
2010-06-21 18:07:48 -04:00
}
} else if ( ! strcasecmp ( argv [ 0 ] , " dir " ) & & argc = = 2 ) {
if ( chdir ( argv [ 1 ] ) = = - 1 ) {
redisLog ( REDIS_WARNING , " Can't chdir to '%s': %s " ,
argv [ 1 ] , strerror ( errno ) ) ;
exit ( 1 ) ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " loglevel " ) & & argc = = 2 ) {
2015-03-11 18:20:57 -04:00
server . verbosity = configEnumGetValue ( loglevel_enum , argv [ 1 ] ) ;
if ( server . verbosity = = INT_MIN ) {
err = " Invalid log level. "
2015-03-12 09:43:07 -04:00
" Must be one of debug, verbose, notice, warning " ;
2010-06-21 18:07:48 -04:00
goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " logfile " ) & & argc = = 2 ) {
FILE * logfp ;
2013-05-15 04:12:29 -04:00
zfree ( server . logfile ) ;
2010-06-21 18:07:48 -04:00
server . logfile = zstrdup ( argv [ 1 ] ) ;
2013-05-15 04:12:29 -04:00
if ( server . logfile [ 0 ] ! = ' \0 ' ) {
2010-06-21 18:07:48 -04:00
/* Test if we are able to open the file. The server will not
* be able to abort just for this problem later . . . */
logfp = fopen ( server . logfile , " a " ) ;
if ( logfp = = NULL ) {
err = sdscatprintf ( sdsempty ( ) ,
" Can't open the log file: %s " , strerror ( errno ) ) ;
goto loaderr ;
}
fclose ( logfp ) ;
}
2010-12-09 11:10:21 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " syslog-enabled " ) & & argc = = 2 ) {
if ( ( server . syslog_enabled = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " syslog-ident " ) & & argc = = 2 ) {
if ( server . syslog_ident ) zfree ( server . syslog_ident ) ;
server . syslog_ident = zstrdup ( argv [ 1 ] ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " syslog-facility " ) & & argc = = 2 ) {
2015-03-11 11:59:56 -04:00
server . syslog_facility =
configEnumGetValue ( syslog_facility_enum , argv [ 1 ] ) ;
if ( server . syslog_facility = = INT_MIN ) {
2010-12-09 11:10:21 -05:00
err = " Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7 " ;
goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " databases " ) & & argc = = 2 ) {
server . dbnum = atoi ( argv [ 1 ] ) ;
if ( server . dbnum < 1 ) {
err = " Invalid number of databases " ; goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " include " ) & & argc = = 2 ) {
2011-12-01 07:44:53 -05:00
loadServerConfig ( argv [ 1 ] , NULL ) ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " maxclients " ) & & argc = = 2 ) {
server . maxclients = atoi ( argv [ 1 ] ) ;
2012-04-18 05:31:24 -04:00
if ( server . maxclients < 1 ) {
err = " Invalid max clients limit " ; goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " maxmemory " ) & & argc = = 2 ) {
server . maxmemory = memtoll ( argv [ 1 ] , NULL ) ;
2010-10-14 15:22:21 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " maxmemory-policy " ) & & argc = = 2 ) {
2015-03-11 11:59:56 -04:00
server . maxmemory_policy =
configEnumGetValue ( maxmemory_policy_enum , argv [ 1 ] ) ;
if ( server . maxmemory_policy = = INT_MIN ) {
2010-10-14 15:22:21 -04:00
err = " Invalid maxmemory policy " ;
goto loaderr ;
}
2010-10-15 05:57:38 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " maxmemory-samples " ) & & argc = = 2 ) {
server . maxmemory_samples = atoi ( argv [ 1 ] ) ;
if ( server . maxmemory_samples < = 0 ) {
err = " maxmemory-samples must be 1 or greater " ;
goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " slaveof " ) & & argc = = 3 ) {
2014-01-20 05:08:14 -05:00
slaveof_linenum = linenum ;
2010-06-21 18:07:48 -04:00
server . masterhost = sdsnew ( argv [ 1 ] ) ;
server . masterport = atoi ( argv [ 2 ] ) ;
2011-12-21 06:23:18 -05:00
server . repl_state = REDIS_REPL_CONNECT ;
2011-10-31 06:13:28 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-ping-slave-period " ) & & argc = = 2 ) {
server . repl_ping_slave_period = atoi ( argv [ 1 ] ) ;
if ( server . repl_ping_slave_period < = 0 ) {
err = " repl-ping-slave-period must be 1 or greater " ;
goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-timeout " ) & & argc = = 2 ) {
server . repl_timeout = atoi ( argv [ 1 ] ) ;
if ( server . repl_timeout < = 0 ) {
err = " repl-timeout must be 1 or greater " ;
goto loaderr ;
}
2013-01-31 05:14:15 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-disable-tcp-nodelay " ) & & argc = = 2 ) {
if ( ( server . repl_disable_tcp_nodelay = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
2013-02-12 06:56:32 -05:00
}
2014-10-16 04:22:02 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-diskless-sync " ) & & argc = = 2 ) {
if ( ( server . repl_diskless_sync = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2014-10-27 05:36:30 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-diskless-sync-delay " ) & & argc = = 2 ) {
server . repl_diskless_sync_delay = atoi ( argv [ 1 ] ) ;
if ( server . repl_diskless_sync_delay < 0 ) {
err = " repl-diskless-sync-delay can't be negative " ;
goto loaderr ;
}
2013-01-30 12:33:16 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-backlog-size " ) & & argc = = 2 ) {
2013-05-15 05:55:14 -04:00
long long size = memtoll ( argv [ 1 ] , NULL ) ;
2013-01-30 12:33:16 -05:00
if ( size < = 0 ) {
err = " repl-backlog-size must be 1 or greater. " ;
goto loaderr ;
}
resizeReplicationBacklog ( size ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " repl-backlog-ttl " ) & & argc = = 2 ) {
server . repl_backlog_time_limit = atoi ( argv [ 1 ] ) ;
if ( server . repl_backlog_time_limit < 0 ) {
err = " repl-backlog-ttl can't be negative " ;
goto loaderr ;
2013-01-31 05:14:15 -05:00
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " masterauth " ) & & argc = = 2 ) {
2014-10-16 04:22:02 -04:00
server . masterauth = zstrdup ( argv [ 1 ] ) ;
2010-11-04 14:59:21 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " slave-serve-stale-data " ) & & argc = = 2 ) {
if ( ( server . repl_serve_stale_data = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2012-03-20 12:32:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " slave-read-only " ) & & argc = = 2 ) {
if ( ( server . repl_slave_ro = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " rdbcompression " ) & & argc = = 2 ) {
2011-12-21 06:22:13 -05:00
if ( ( server . rdb_compression = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
2010-06-21 18:07:48 -04:00
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2012-04-10 09:47:10 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " rdbchecksum " ) & & argc = = 2 ) {
if ( ( server . rdb_checksum = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " activerehashing " ) & & argc = = 2 ) {
if ( ( server . activerehashing = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " daemonize " ) & & argc = = 2 ) {
if ( ( server . daemonize = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2012-12-14 11:10:40 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " hz " ) & & argc = = 2 ) {
server . hz = atoi ( argv [ 1 ] ) ;
if ( server . hz < REDIS_MIN_HZ ) server . hz = REDIS_MIN_HZ ;
if ( server . hz > REDIS_MAX_HZ ) server . hz = REDIS_MAX_HZ ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " appendonly " ) & & argc = = 2 ) {
2011-12-21 04:31:34 -05:00
int yes ;
if ( ( yes = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
2010-06-21 18:07:48 -04:00
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2011-12-21 04:31:34 -05:00
server . aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " appendfilename " ) & & argc = = 2 ) {
2013-07-02 06:14:28 -04:00
if ( ! pathIsBaseName ( argv [ 1 ] ) ) {
err = " appendfilename can't be a path, just a filename " ;
goto loaderr ;
}
2011-12-21 05:58:42 -05:00
zfree ( server . aof_filename ) ;
server . aof_filename = zstrdup ( argv [ 1 ] ) ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " no-appendfsync-on-rewrite " )
& & argc = = 2 ) {
2011-12-21 05:58:42 -05:00
if ( ( server . aof_no_fsync_on_rewrite = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
2010-06-21 18:07:48 -04:00
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " appendfsync " ) & & argc = = 2 ) {
2015-03-11 18:20:57 -04:00
server . aof_fsync = configEnumGetValue ( aof_fsync_enum , argv [ 1 ] ) ;
if ( server . aof_fsync = = INT_MIN ) {
2010-06-21 18:07:48 -04:00
err = " argument must be 'no', 'always' or 'everysec' " ;
goto loaderr ;
}
2011-06-10 06:39:23 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " auto-aof-rewrite-percentage " ) & &
argc = = 2 )
{
2011-12-21 05:58:42 -05:00
server . aof_rewrite_perc = atoi ( argv [ 1 ] ) ;
if ( server . aof_rewrite_perc < 0 ) {
2011-06-10 06:39:23 -04:00
err = " Invalid negative percentage for AOF auto rewrite " ;
goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " auto-aof-rewrite-min-size " ) & &
argc = = 2 )
{
2011-12-21 05:58:42 -05:00
server . aof_rewrite_min_size = memtoll ( argv [ 1 ] , NULL ) ;
2013-04-24 04:57:07 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " aof-rewrite-incremental-fsync " ) & &
argc = = 2 )
{
2014-09-08 03:23:55 -04:00
if ( ( server . aof_rewrite_incremental_fsync =
yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " aof-load-truncated " ) & & argc = = 2 ) {
if ( ( server . aof_load_truncated = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
2013-04-24 04:57:07 -04:00
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " requirepass " ) & & argc = = 2 ) {
2012-06-21 05:50:01 -04:00
if ( strlen ( argv [ 1 ] ) > REDIS_AUTHPASS_MAX_LEN ) {
err = " Password is longer than REDIS_AUTHPASS_MAX_LEN " ;
goto loaderr ;
}
2010-06-21 18:07:48 -04:00
server . requirepass = zstrdup ( argv [ 1 ] ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " pidfile " ) & & argc = = 2 ) {
zfree ( server . pidfile ) ;
server . pidfile = zstrdup ( argv [ 1 ] ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " dbfilename " ) & & argc = = 2 ) {
2013-07-02 06:14:28 -04:00
if ( ! pathIsBaseName ( argv [ 1 ] ) ) {
err = " dbfilename can't be a path, just a filename " ;
goto loaderr ;
}
2011-12-21 06:22:13 -05:00
zfree ( server . rdb_filename ) ;
server . rdb_filename = zstrdup ( argv [ 1 ] ) ;
2012-01-03 01:14:10 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " hash-max-ziplist-entries " ) & & argc = = 2 ) {
server . hash_max_ziplist_entries = memtoll ( argv [ 1 ] , NULL ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " hash-max-ziplist-value " ) & & argc = = 2 ) {
server . hash_max_ziplist_value = memtoll ( argv [ 1 ] , NULL ) ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " list-max-ziplist-entries " ) & & argc = = 2 ) {
2014-12-16 00:49:14 -05:00
/* DEAD OPTION */
2010-11-03 07:14:36 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " list-max-ziplist-value " ) & & argc = = 2 ) {
2014-12-16 00:49:14 -05:00
/* DEAD OPTION */
} else if ( ! strcasecmp ( argv [ 0 ] , " list-max-ziplist-size " ) & & argc = = 2 ) {
server . list_max_ziplist_size = atoi ( argv [ 1 ] ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " list-compress-depth " ) & & argc = = 2 ) {
server . list_compress_depth = atoi ( argv [ 1 ] ) ;
2010-11-03 07:14:36 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " set-max-intset-entries " ) & & argc = = 2 ) {
2010-07-02 13:57:12 -04:00
server . set_max_intset_entries = memtoll ( argv [ 1 ] , NULL ) ;
2011-03-09 08:01:57 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " zset-max-ziplist-entries " ) & & argc = = 2 ) {
server . zset_max_ziplist_entries = memtoll ( argv [ 1 ] , NULL ) ;
} else if ( ! strcasecmp ( argv [ 0 ] , " zset-max-ziplist-value " ) & & argc = = 2 ) {
server . zset_max_ziplist_value = memtoll ( argv [ 1 ] , NULL ) ;
2014-04-15 11:46:51 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " hll-sparse-max-bytes " ) & & argc = = 2 ) {
server . hll_sparse_max_bytes = memtoll ( argv [ 1 ] , NULL ) ;
2010-11-03 07:14:36 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " rename-command " ) & & argc = = 3 ) {
struct redisCommand * cmd = lookupCommand ( argv [ 1 ] ) ;
int retval ;
if ( ! cmd ) {
err = " No such command in rename-command " ;
goto loaderr ;
}
2013-01-16 12:00:20 -05:00
/* If the target command name is the empty string we just
2010-11-03 07:14:36 -04:00
* remove it from the command table . */
retval = dictDelete ( server . commands , argv [ 1 ] ) ;
redisAssert ( retval = = DICT_OK ) ;
/* Otherwise we re-add the command under a different name. */
if ( sdslen ( argv [ 2 ] ) ! = 0 ) {
sds copy = sdsdup ( argv [ 2 ] ) ;
retval = dictAdd ( server . commands , copy , cmd ) ;
if ( retval ! = DICT_OK ) {
sdsfree ( copy ) ;
err = " Target command name already exists " ; goto loaderr ;
}
}
2011-03-29 11:51:15 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " cluster-enabled " ) & & argc = = 2 ) {
if ( ( server . cluster_enabled = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2011-04-07 15:34:41 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " cluster-config-file " ) & & argc = = 2 ) {
2013-02-14 09:20:02 -05:00
zfree ( server . cluster_configfile ) ;
server . cluster_configfile = zstrdup ( argv [ 1 ] ) ;
2014-09-17 05:10:09 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " cluster-require-full-coverage " ) & &
argc = = 2 )
{
if ( ( server . cluster_require_full_coverage = yesnotoi ( argv [ 1 ] ) ) = = - 1 )
{
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2013-04-04 06:29:10 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " cluster-node-timeout " ) & & argc = = 2 ) {
2013-10-09 10:18:33 -04:00
server . cluster_node_timeout = strtoll ( argv [ 1 ] , NULL , 10 ) ;
2013-04-04 06:29:10 -04:00
if ( server . cluster_node_timeout < = 0 ) {
err = " cluster node timeout must be 1 or greater " ; goto loaderr ;
}
2014-01-31 05:12:34 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " cluster-migration-barrier " )
& & argc = = 2 )
{
server . cluster_migration_barrier = atoi ( argv [ 1 ] ) ;
if ( server . cluster_migration_barrier < 0 ) {
2014-05-22 10:57:47 -04:00
err = " cluster migration barrier must zero or positive " ;
goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " cluster-slave-validity-factor " )
& & argc = = 2 )
{
server . cluster_slave_validity_factor = atoi ( argv [ 1 ] ) ;
if ( server . cluster_slave_validity_factor < 0 ) {
err = " cluster slave validity factor must be zero or positive " ;
2014-01-31 05:12:34 -05:00
goto loaderr ;
}
2011-05-06 11:21:27 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " lua-time-limit " ) & & argc = = 2 ) {
server . lua_time_limit = strtoll ( argv [ 1 ] , NULL , 10 ) ;
2011-06-30 09:47:15 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " slowlog-log-slower-than " ) & &
argc = = 2 )
{
server . slowlog_log_slower_than = strtoll ( argv [ 1 ] , NULL , 10 ) ;
2014-07-02 06:27:24 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " latency-monitor-threshold " ) & &
argc = = 2 )
{
server . latency_monitor_threshold = strtoll ( argv [ 1 ] , NULL , 10 ) ;
if ( server . latency_monitor_threshold < 0 ) {
err = " The latency threshold can't be negative " ;
goto loaderr ;
}
2011-06-30 09:47:15 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " slowlog-max-len " ) & & argc = = 2 ) {
server . slowlog_max_len = strtoll ( argv [ 1 ] , NULL , 10 ) ;
2012-01-24 04:43:30 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " client-output-buffer-limit " ) & &
argc = = 5 )
{
2014-06-16 04:43:05 -04:00
int class = getClientTypeByName ( argv [ 1 ] ) ;
2012-01-24 04:43:30 -05:00
unsigned long long hard , soft ;
int soft_seconds ;
if ( class = = - 1 ) {
err = " Unrecognized client limit class " ;
goto loaderr ;
}
hard = memtoll ( argv [ 2 ] , NULL ) ;
soft = memtoll ( argv [ 3 ] , NULL ) ;
soft_seconds = atoi ( argv [ 4 ] ) ;
if ( soft_seconds < 0 ) {
2013-01-16 12:00:20 -05:00
err = " Negative number of seconds in soft limit is invalid " ;
2012-01-24 04:43:30 -05:00
goto loaderr ;
}
server . client_obuf_limits [ class ] . hard_limit_bytes = hard ;
server . client_obuf_limits [ class ] . soft_limit_bytes = soft ;
server . client_obuf_limits [ class ] . soft_limit_seconds = soft_seconds ;
2012-03-07 12:02:26 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " stop-writes-on-bgsave-error " ) & &
argc = = 2 ) {
if ( ( server . stop_writes_on_bgsave_err = yesnotoi ( argv [ 1 ] ) ) = = - 1 ) {
err = " argument must be 'yes' or 'no' " ; goto loaderr ;
}
2012-08-28 11:20:26 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " slave-priority " ) & & argc = = 2 ) {
server . slave_priority = atoi ( argv [ 1 ] ) ;
2013-05-29 05:36:44 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " min-slaves-to-write " ) & & argc = = 2 ) {
server . repl_min_slaves_to_write = atoi ( argv [ 1 ] ) ;
if ( server . repl_min_slaves_to_write < 0 ) {
err = " Invalid value for min-slaves-to-write. " ; goto loaderr ;
}
} else if ( ! strcasecmp ( argv [ 0 ] , " min-slaves-max-lag " ) & & argc = = 2 ) {
server . repl_min_slaves_max_lag = atoi ( argv [ 1 ] ) ;
if ( server . repl_min_slaves_max_lag < 0 ) {
err = " Invalid value for min-slaves-max-lag. " ; goto loaderr ;
}
2013-01-23 10:23:33 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " notify-keyspace-events " ) & & argc = = 2 ) {
2013-01-25 07:19:08 -05:00
int flags = keyspaceEventsStringToFlags ( argv [ 1 ] ) ;
if ( flags = = - 1 ) {
err = " Invalid event class character. Use 'g$lshzxeA'. " ;
goto loaderr ;
2013-01-23 10:23:33 -05:00
}
2013-01-25 07:19:08 -05:00
server . notify_keyspace_events = flags ;
2015-01-08 15:22:33 -05:00
} else if ( ! strcasecmp ( argv [ 0 ] , " supervised " ) & & argc = = 2 ) {
2015-03-11 18:20:57 -04:00
server . supervised_mode =
configEnumGetValue ( supervised_mode_enum , argv [ 1 ] ) ;
2015-01-08 15:22:33 -05:00
2015-03-11 18:20:57 -04:00
if ( server . supervised_mode = = INT_MIN ) {
2015-01-08 15:22:33 -05:00
err = " Invalid option for 'supervised'. "
" Allowed values: 'upstart', 'systemd', 'auto', or 'no' " ;
goto loaderr ;
}
2012-07-23 06:54:52 -04:00
} else if ( ! strcasecmp ( argv [ 0 ] , " sentinel " ) ) {
/* argc == 1 is handled by main() as we need to enter the sentinel
* mode ASAP . */
if ( argc ! = 1 ) {
if ( ! server . sentinel_mode ) {
err = " sentinel directive while not in sentinel mode " ;
goto loaderr ;
}
err = sentinelHandleConfiguration ( argv + 1 , argc - 1 ) ;
if ( err ) goto loaderr ;
}
2010-06-21 18:07:48 -04:00
} else {
err = " Bad directive or wrong number of arguments " ; goto loaderr ;
}
2011-12-01 07:44:53 -05:00
sdsfreesplitres ( argv , argc ) ;
2010-06-21 18:07:48 -04:00
}
2013-03-05 06:58:22 -05:00
/* Sanity checks. */
if ( server . cluster_enabled & & server . masterhost ) {
2014-01-20 05:08:14 -05:00
linenum = slaveof_linenum ;
i = linenum - 1 ;
2013-03-05 06:58:22 -05:00
err = " slaveof directive not allowed in cluster mode " ;
goto loaderr ;
}
2014-01-20 05:08:14 -05:00
sdsfreesplitres ( lines , totlines ) ;
2010-06-21 18:07:48 -04:00
return ;
loaderr :
fprintf ( stderr , " \n *** FATAL CONFIG FILE ERROR *** \n " ) ;
fprintf ( stderr , " Reading the configuration file, at line %d \n " , linenum ) ;
2011-12-01 07:44:53 -05:00
fprintf ( stderr , " >>> '%s' \n " , lines [ i ] ) ;
2010-06-21 18:07:48 -04:00
fprintf ( stderr , " %s \n " , err ) ;
exit ( 1 ) ;
}
2011-12-01 07:44:53 -05:00
/* Load the server configuration from the specified filename.
* The function appends the additional configuration directives stored
* in the ' options ' string to the config file before loading .
*
* Both filename and options can be NULL , in such a case are considered
2013-01-16 12:00:20 -05:00
* empty . This way loadServerConfig can be used to just load a file or
2011-12-01 07:44:53 -05:00
* just load a string . */
void loadServerConfig ( char * filename , char * options ) {
sds config = sdsempty ( ) ;
char buf [ REDIS_CONFIGLINE_MAX + 1 ] ;
/* Load the file content */
if ( filename ) {
FILE * fp ;
if ( filename [ 0 ] = = ' - ' & & filename [ 1 ] = = ' \0 ' ) {
fp = stdin ;
} else {
if ( ( fp = fopen ( filename , " r " ) ) = = NULL ) {
redisLog ( REDIS_WARNING ,
" Fatal error, can't open config file '%s' " , filename ) ;
exit ( 1 ) ;
}
}
while ( fgets ( buf , REDIS_CONFIGLINE_MAX + 1 , fp ) ! = NULL )
config = sdscat ( config , buf ) ;
if ( fp ! = stdin ) fclose ( fp ) ;
}
/* Append the additional options */
if ( options ) {
config = sdscat ( config , " \n " ) ;
config = sdscat ( config , options ) ;
}
loadServerConfigFromString ( config ) ;
sdsfree ( config ) ;
}
2010-06-21 18:07:48 -04:00
/*-----------------------------------------------------------------------------
2013-05-09 18:15:18 -04:00
* CONFIG SET implementation
2010-06-21 18:07:48 -04:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-03-10 07:37:39 -04:00
# define config_set_bool_field(_name,_var) \
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , _name ) ) { \
int yn = yesnotoi ( o - > ptr ) ; \
if ( yn = = - 1 ) goto badfmt ; \
2015-03-11 04:02:04 -04:00
_var = yn ;
2015-03-10 07:37:39 -04:00
# define config_set_numerical_field(_name,_var,min,max) \
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , _name ) ) { \
if ( getLongLongFromObject ( o , & ll ) = = REDIS_ERR | | ll < 0 ) goto badfmt ; \
if ( min ! = LLONG_MIN & & ll < min ) goto badfmt ; \
if ( max ! = LLONG_MAX & & ll > max ) goto badfmt ; \
2015-03-11 04:02:04 -04:00
_var = ll ;
# define config_set_memory_field(_name,_var) \
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , _name ) ) { \
ll = memtoll ( o - > ptr , & err ) ; \
if ( err | | ll < 0 ) goto badfmt ; \
_var = ll ;
2015-03-11 11:59:56 -04:00
# define config_set_enum_field(_name,_var,_enumvar) \
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , _name ) ) { \
int enumval = configEnumGetValue ( _enumvar , o - > ptr ) ; \
if ( enumval = = INT_MIN ) goto badfmt ; \
_var = enumval ;
2015-03-11 04:02:04 -04:00
# define config_set_special_field(_name) \
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , _name ) ) {
2015-03-10 07:37:39 -04:00
2015-03-11 18:20:57 -04:00
# define config_set_else } else
2010-06-21 18:07:48 -04:00
void configSetCommand ( redisClient * c ) {
2010-10-17 12:09:23 -04:00
robj * o ;
2010-06-21 18:07:48 -04:00
long long ll ;
2015-02-12 10:51:20 -05:00
int err ;
2012-06-05 15:50:10 -04:00
redisAssertWithInfo ( c , c - > argv [ 2 ] , sdsEncodedObject ( c - > argv [ 2 ] ) ) ;
redisAssertWithInfo ( c , c - > argv [ 3 ] , sdsEncodedObject ( c - > argv [ 3 ] ) ) ;
2010-10-17 12:09:23 -04:00
o = c - > argv [ 3 ] ;
2010-06-21 18:07:48 -04:00
2015-03-11 04:02:04 -04:00
if ( 0 ) { /* this starts the config_set macros else-if chain. */
/* Special fields that can't be handled with general macros. */
config_set_special_field ( " dbfilename " ) {
2013-07-02 06:14:28 -04:00
if ( ! pathIsBaseName ( o - > ptr ) ) {
addReplyError ( c , " dbfilename can't be a path, just a filename " ) ;
return ;
}
2011-12-21 06:22:13 -05:00
zfree ( server . rdb_filename ) ;
server . rdb_filename = zstrdup ( o - > ptr ) ;
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " requirepass " ) {
2012-06-21 05:50:01 -04:00
if ( sdslen ( o - > ptr ) > REDIS_AUTHPASS_MAX_LEN ) goto badfmt ;
2010-06-21 18:07:48 -04:00
zfree ( server . requirepass ) ;
2011-10-31 04:57:06 -04:00
server . requirepass = ( ( char * ) o - > ptr ) [ 0 ] ? zstrdup ( o - > ptr ) : NULL ;
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " masterauth " ) {
2010-06-21 18:07:48 -04:00
zfree ( server . masterauth ) ;
2013-05-02 11:20:49 -04:00
server . masterauth = ( ( char * ) o - > ptr ) [ 0 ] ? zstrdup ( o - > ptr ) : NULL ;
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " maxclients " ) {
2013-06-28 11:08:03 -04:00
int orig_value = server . maxclients ;
Fix maxclients error handling
Everywhere in the Redis code base, maxclients is treated
as an int with (int)maxclients or `maxclients = atoi(source)`,
so let's make maxclients an int.
This fixes a bug where someone could specify a negative maxclients
on startup and it would work (as well as set maxclients very high)
because:
unsigned int maxclients;
char *update = "-300";
maxclients = atoi(update);
if (maxclients < 1) goto fail;
But, (maxclients < 1) can only catch the case when maxclients
is exactly 0. maxclients happily sets itself to -300, which isn't
-300, but rather 4294966996, which isn't < 1, so... everything
"worked."
maxclients config parsing checks for the case of < 1, but maxclients
CONFIG SET parsing was checking for case of < 0 (allowing
maxclients to be set to 0). CONFIG SET parsing is now updated to
match config parsing of < 1.
It's tempting to add a MINIMUM_CLIENTS define, but... I didn't.
These changes were inspired by antirez#356, but this doesn't
fix that issue.
2014-03-22 18:56:28 -04:00
if ( getLongLongFromObject ( o , & ll ) = = REDIS_ERR | | ll < 1 ) goto badfmt ;
2013-06-28 11:08:03 -04:00
/* Try to check if the OS is capable of supporting so many FDs. */
server . maxclients = ll ;
if ( ll > orig_value ) {
adjustOpenFilesLimit ( ) ;
if ( server . maxclients ! = ll ) {
addReplyErrorFormat ( c , " The operating system is not able to handle the specified number of clients, try with %d " , server . maxclients ) ;
server . maxclients = orig_value ;
return ;
}
2014-08-13 05:44:38 -04:00
if ( ( unsigned int ) aeGetSetSize ( server . el ) <
2013-06-28 11:08:03 -04:00
server . maxclients + REDIS_EVENTLOOP_FDSET_INCR )
{
if ( aeResizeSetSize ( server . el ,
server . maxclients + REDIS_EVENTLOOP_FDSET_INCR ) = = AE_ERR )
{
addReplyError ( c , " The event loop API used by Redis is not able to handle the specified number of clients " ) ;
server . maxclients = orig_value ;
return ;
}
}
}
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " appendonly " ) {
2011-12-21 04:31:34 -05:00
int enable = yesnotoi ( o - > ptr ) ;
if ( enable = = - 1 ) goto badfmt ;
if ( enable = = 0 & & server . aof_state ! = REDIS_AOF_OFF ) {
stopAppendOnly ( ) ;
} else if ( enable & & server . aof_state = = REDIS_AOF_OFF ) {
if ( startAppendOnly ( ) = = REDIS_ERR ) {
addReplyError ( c ,
" Unable to turn on AOF. Check server logs. " ) ;
return ;
2010-06-21 18:07:48 -04:00
}
}
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " save " ) {
2010-06-21 18:07:48 -04:00
int vlen , j ;
sds * v = sdssplitlen ( o - > ptr , sdslen ( o - > ptr ) , " " , 1 , & vlen ) ;
/* Perform sanity check before setting the new config:
* - Even number of args
* - Seconds > = 1 , changes > = 0 */
if ( vlen & 1 ) {
sdsfreesplitres ( v , vlen ) ;
goto badfmt ;
}
for ( j = 0 ; j < vlen ; j + + ) {
char * eptr ;
long val ;
val = strtoll ( v [ j ] , & eptr , 10 ) ;
if ( eptr [ 0 ] ! = ' \0 ' | |
( ( j & 1 ) = = 0 & & val < 1 ) | |
( ( j & 1 ) = = 1 & & val < 0 ) ) {
sdsfreesplitres ( v , vlen ) ;
goto badfmt ;
}
}
/* Finally set the new config */
resetServerSaveParams ( ) ;
for ( j = 0 ; j < vlen ; j + = 2 ) {
time_t seconds ;
int changes ;
seconds = strtoll ( v [ j ] , NULL , 10 ) ;
changes = strtoll ( v [ j + 1 ] , NULL , 10 ) ;
appendServerSaveParams ( seconds , changes ) ;
}
sdsfreesplitres ( v , vlen ) ;
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " dir " ) {
2011-02-13 20:51:27 -05:00
if ( chdir ( ( char * ) o - > ptr ) = = - 1 ) {
addReplyErrorFormat ( c , " Changing directory: %s " , strerror ( errno ) ) ;
return ;
}
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " client-output-buffer-limit " ) {
2012-01-24 04:43:30 -05:00
int vlen , j ;
sds * v = sdssplitlen ( o - > ptr , sdslen ( o - > ptr ) , " " , 1 , & vlen ) ;
/* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
if ( vlen % 4 ) {
sdsfreesplitres ( v , vlen ) ;
goto badfmt ;
}
/* Sanity check of single arguments, so that we either refuse the
* whole configuration string or accept it all , even if a single
* error in a single client class is present . */
for ( j = 0 ; j < vlen ; j + + ) {
long val ;
if ( ( j % 4 ) = = 0 ) {
2014-06-16 04:43:05 -04:00
if ( getClientTypeByName ( v [ j ] ) = = - 1 ) {
2012-01-24 04:43:30 -05:00
sdsfreesplitres ( v , vlen ) ;
goto badfmt ;
}
} else {
2015-02-12 10:51:20 -05:00
val = memtoll ( v [ j ] , & err ) ;
if ( err | | val < 0 ) {
2012-01-24 04:43:30 -05:00
sdsfreesplitres ( v , vlen ) ;
goto badfmt ;
}
}
}
/* Finally set the new config */
for ( j = 0 ; j < vlen ; j + = 4 ) {
int class ;
unsigned long long hard , soft ;
int soft_seconds ;
2014-06-16 04:43:05 -04:00
class = getClientTypeByName ( v [ j ] ) ;
2012-01-24 04:43:30 -05:00
hard = strtoll ( v [ j + 1 ] , NULL , 10 ) ;
soft = strtoll ( v [ j + 2 ] , NULL , 10 ) ;
soft_seconds = strtoll ( v [ j + 3 ] , NULL , 10 ) ;
server . client_obuf_limits [ class ] . hard_limit_bytes = hard ;
server . client_obuf_limits [ class ] . soft_limit_bytes = soft ;
server . client_obuf_limits [ class ] . soft_limit_seconds = soft_seconds ;
}
sdsfreesplitres ( v , vlen ) ;
2015-03-11 04:02:04 -04:00
} config_set_special_field ( " notify-keyspace-events " ) {
2013-01-25 07:19:08 -05:00
int flags = keyspaceEventsStringToFlags ( o - > ptr ) ;
2012-04-10 09:47:10 -04:00
2013-01-25 07:19:08 -05:00
if ( flags = = - 1 ) goto badfmt ;
server . notify_keyspace_events = flags ;
2015-03-10 07:37:39 -04:00
/* Boolean fields.
* config_set_bool_field ( name , var ) . */
2015-03-11 04:02:04 -04:00
} config_set_bool_field (
2015-03-10 07:37:39 -04:00
" rdbcompression " , server . rdb_compression ) {
} config_set_bool_field (
" repl-disable-tcp-nodelay " , server . repl_disable_tcp_nodelay ) {
} config_set_bool_field (
" repl-diskless-sync " , server . repl_diskless_sync ) {
} config_set_bool_field (
" cluster-require-full-coverage " , server . cluster_require_full_coverage ) {
} config_set_bool_field (
" aof-rewrite-incremental-fsync " , server . aof_rewrite_incremental_fsync ) {
} config_set_bool_field (
" aof-load-truncated " , server . aof_load_truncated ) {
} config_set_bool_field (
" slave-serve-stale-data " , server . repl_serve_stale_data ) {
} config_set_bool_field (
" slave-read-only " , server . repl_slave_ro ) {
} config_set_bool_field (
" activerehashing " , server . activerehashing ) {
} config_set_bool_field (
" stop-writes-on-bgsave-error " , server . stop_writes_on_bgsave_err ) {
} config_set_bool_field (
" tcp-keepalive " , server . tcpkeepalive ) {
/* Numerical fields.
* config_set_numerical_field ( name , var , min , max ) */
} config_set_numerical_field (
" maxmemory-samples " , server . maxmemory_samples , 1 , LLONG_MAX ) {
} config_set_numerical_field (
" timeout " , server . maxidletime , 0 , LONG_MAX ) {
} config_set_numerical_field (
" auto-aof-rewrite-percentage " , server . aof_rewrite_perc , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" auto-aof-rewrite-min-size " , server . aof_rewrite_min_size , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" hash-max-ziplist-entries " , server . hash_max_ziplist_entries , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" hash-max-ziplist-value " , server . hash_max_ziplist_value , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" list-max-ziplist-size " , server . list_max_ziplist_size , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" list-compress-depth " , server . list_compress_depth , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" set-max-intset-entries " , server . set_max_intset_entries , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" zset-max-ziplist-entries " , server . zset_max_ziplist_entries , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" zset-max-ziplist-value " , server . zset_max_ziplist_value , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" hll-sparse-max-bytes " , server . hll_sparse_max_bytes , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" lua-time-limit " , server . lua_time_limit , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" slowlog-log-slower-than " , server . slowlog_log_slower_than , 0 , LLONG_MAX ) {
2015-03-10 08:00:33 -04:00
} config_set_numerical_field (
" slowlog-max-len " , ll , 0 , LLONG_MAX ) {
/* Cast to unsigned. */
server . slowlog_max_len = ( unsigned ) ll ;
2015-03-10 07:37:39 -04:00
} config_set_numerical_field (
" latency-monitor-threshold " , server . latency_monitor_threshold , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" repl-ping-slave-period " , server . repl_ping_slave_period , 1 , LLONG_MAX ) {
} config_set_numerical_field (
" repl-timeout " , server . repl_timeout , 1 , LLONG_MAX ) {
} config_set_numerical_field (
" repl-backlog-ttl " , server . repl_backlog_time_limit , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" repl-diskless-sync-delay " , server . repl_diskless_sync_delay , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" slave-priority " , server . slave_priority , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" min-slaves-to-write " , server . repl_min_slaves_to_write , 0 , LLONG_MAX ) {
2013-05-30 06:23:28 -04:00
refreshGoodSlavesCount ( ) ;
2015-03-10 07:37:39 -04:00
} config_set_numerical_field (
" min-slaves-max-lag " , server . repl_min_slaves_max_lag , 0 , LLONG_MAX ) {
2013-05-30 06:23:28 -04:00
refreshGoodSlavesCount ( ) ;
2015-03-10 07:37:39 -04:00
} config_set_numerical_field (
" cluster-node-timeout " , server . cluster_node_timeout , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" cluster-migration-barrier " , server . cluster_migration_barrier , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" cluster-slave-validity-factor " , server . cluster_slave_validity_factor , 0 , LLONG_MAX ) {
} config_set_numerical_field (
" hz " , server . hz , 0 , LLONG_MAX ) {
/* Hz is more an hint from the user, so we accept values out of range
* but cap them to reasonable values . */
if ( server . hz < REDIS_MIN_HZ ) server . hz = REDIS_MIN_HZ ;
if ( server . hz > REDIS_MAX_HZ ) server . hz = REDIS_MAX_HZ ;
2015-03-10 08:00:33 -04:00
} config_set_numerical_field (
" watchdog-period " , ll , 0 , LLONG_MAX ) {
if ( ll )
enableWatchdog ( ll ) ;
else
disableWatchdog ( ) ;
2015-03-11 04:02:04 -04:00
/* Memory fields.
* config_set_memory_field ( name , var ) */
} config_set_memory_field ( " maxmemory " , server . maxmemory ) {
if ( server . maxmemory ) {
if ( server . maxmemory < zmalloc_used_memory ( ) ) {
redisLog ( REDIS_WARNING , " WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in keys eviction and/or inability to accept new write commands depending on the maxmemory-policy. " ) ;
}
freeMemoryIfNeeded ( ) ;
}
} config_set_memory_field ( " repl-backlog-size " , ll ) {
resizeReplicationBacklog ( ll ) ;
2015-03-11 18:20:57 -04:00
/* Enumeration fields.
* config_set_enum_field ( name , var , enum_var ) */
} config_set_enum_field (
" loglevel " , server . verbosity , loglevel_enum ) {
} config_set_enum_field (
" maxmemory-policy " , server . maxmemory_policy , maxmemory_policy_enum ) {
} config_set_enum_field (
" appendfsync " , server . aof_fsync , aof_fsync_enum ) {
2015-03-10 07:37:39 -04:00
/* Everyhing else is an error... */
2015-03-11 18:20:57 -04:00
} config_set_else {
2010-09-02 13:52:24 -04:00
addReplyErrorFormat ( c , " Unsupported CONFIG parameter: %s " ,
( char * ) c - > argv [ 2 ] - > ptr ) ;
2010-06-21 18:07:48 -04:00
return ;
}
2015-03-11 18:20:57 -04:00
/* On success we just return a generic OK for all the options. */
2010-06-21 18:07:48 -04:00
addReply ( c , shared . ok ) ;
return ;
badfmt : /* Bad format errors */
2010-09-02 13:52:24 -04:00
addReplyErrorFormat ( c , " Invalid argument '%s' for CONFIG SET '%s' " ,
2010-06-21 18:07:48 -04:00
( char * ) o - > ptr ,
2010-09-02 13:52:24 -04:00
( char * ) c - > argv [ 2 ] - > ptr ) ;
2010-06-21 18:07:48 -04:00
}
2013-05-09 18:15:18 -04:00
/*-----------------------------------------------------------------------------
* CONFIG GET implementation
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2012-03-08 06:14:23 -05:00
# define config_get_string_field(_name,_var) do { \
if ( stringmatch ( pattern , _name , 0 ) ) { \
addReplyBulkCString ( c , _name ) ; \
addReplyBulkCString ( c , _var ? _var : " " ) ; \
matches + + ; \
} \
} while ( 0 ) ;
# define config_get_bool_field(_name,_var) do { \
if ( stringmatch ( pattern , _name , 0 ) ) { \
addReplyBulkCString ( c , _name ) ; \
addReplyBulkCString ( c , _var ? " yes " : " no " ) ; \
matches + + ; \
} \
} while ( 0 ) ;
# define config_get_numerical_field(_name,_var) do { \
if ( stringmatch ( pattern , _name , 0 ) ) { \
ll2string ( buf , sizeof ( buf ) , _var ) ; \
addReplyBulkCString ( c , _name ) ; \
addReplyBulkCString ( c , buf ) ; \
matches + + ; \
} \
} while ( 0 ) ;
2015-03-11 11:59:56 -04:00
# define config_get_enum_field(_name,_var,_enumvar) do { \
if ( stringmatch ( pattern , _name , 0 ) ) { \
addReplyBulkCString ( c , _name ) ; \
addReplyBulkCString ( c , configEnumGetNameOrUnknown ( _enumvar , _var ) ) ; \
matches + + ; \
} \
} while ( 0 ) ;
2014-06-17 10:42:19 -04:00
2010-06-21 18:07:48 -04:00
void configGetCommand ( redisClient * c ) {
2010-10-17 12:09:23 -04:00
robj * o = c - > argv [ 2 ] ;
2010-08-30 10:02:06 -04:00
void * replylen = addDeferredMultiBulkLength ( c ) ;
2010-06-21 18:07:48 -04:00
char * pattern = o - > ptr ;
2010-10-15 05:57:38 -04:00
char buf [ 128 ] ;
2010-06-21 18:07:48 -04:00
int matches = 0 ;
2012-06-05 15:50:10 -04:00
redisAssertWithInfo ( c , o , sdsEncodedObject ( o ) ) ;
2010-06-21 18:07:48 -04:00
2012-03-08 06:14:23 -05:00
/* String values */
config_get_string_field ( " dbfilename " , server . rdb_filename ) ;
config_get_string_field ( " requirepass " , server . requirepass ) ;
2013-03-14 01:52:43 -04:00
config_get_string_field ( " masterauth " , server . masterauth ) ;
2012-03-08 06:14:23 -05:00
config_get_string_field ( " unixsocket " , server . unixsocket ) ;
config_get_string_field ( " logfile " , server . logfile ) ;
config_get_string_field ( " pidfile " , server . pidfile ) ;
/* Numerical values */
config_get_numerical_field ( " maxmemory " , server . maxmemory ) ;
config_get_numerical_field ( " maxmemory-samples " , server . maxmemory_samples ) ;
config_get_numerical_field ( " timeout " , server . maxidletime ) ;
config_get_numerical_field ( " auto-aof-rewrite-percentage " ,
server . aof_rewrite_perc ) ;
config_get_numerical_field ( " auto-aof-rewrite-min-size " ,
server . aof_rewrite_min_size ) ;
2012-03-09 16:07:45 -05:00
config_get_numerical_field ( " hash-max-ziplist-entries " ,
server . hash_max_ziplist_entries ) ;
config_get_numerical_field ( " hash-max-ziplist-value " ,
server . hash_max_ziplist_value ) ;
2014-12-16 00:49:14 -05:00
config_get_numerical_field ( " list-max-ziplist-size " ,
server . list_max_ziplist_size ) ;
config_get_numerical_field ( " list-compress-depth " ,
server . list_compress_depth ) ;
2012-03-08 06:14:23 -05:00
config_get_numerical_field ( " set-max-intset-entries " ,
server . set_max_intset_entries ) ;
config_get_numerical_field ( " zset-max-ziplist-entries " ,
server . zset_max_ziplist_entries ) ;
config_get_numerical_field ( " zset-max-ziplist-value " ,
server . zset_max_ziplist_value ) ;
2014-04-15 11:46:51 -04:00
config_get_numerical_field ( " hll-sparse-max-bytes " ,
server . hll_sparse_max_bytes ) ;
2012-03-08 06:14:23 -05:00
config_get_numerical_field ( " lua-time-limit " , server . lua_time_limit ) ;
config_get_numerical_field ( " slowlog-log-slower-than " ,
server . slowlog_log_slower_than ) ;
2014-07-02 06:27:24 -04:00
config_get_numerical_field ( " latency-monitor-threshold " ,
server . latency_monitor_threshold ) ;
2012-03-08 06:14:23 -05:00
config_get_numerical_field ( " slowlog-max-len " ,
server . slowlog_max_len ) ;
config_get_numerical_field ( " port " , server . port ) ;
2014-01-31 08:55:43 -05:00
config_get_numerical_field ( " tcp-backlog " , server . tcp_backlog ) ;
2012-03-08 06:14:23 -05:00
config_get_numerical_field ( " databases " , server . dbnum ) ;
config_get_numerical_field ( " repl-ping-slave-period " , server . repl_ping_slave_period ) ;
config_get_numerical_field ( " repl-timeout " , server . repl_timeout ) ;
2013-01-30 12:33:16 -05:00
config_get_numerical_field ( " repl-backlog-size " , server . repl_backlog_size ) ;
config_get_numerical_field ( " repl-backlog-ttl " , server . repl_backlog_time_limit ) ;
2012-03-08 06:14:23 -05:00
config_get_numerical_field ( " maxclients " , server . maxclients ) ;
2012-03-27 05:47:51 -04:00
config_get_numerical_field ( " watchdog-period " , server . watchdog_period ) ;
2012-08-28 11:20:26 -04:00
config_get_numerical_field ( " slave-priority " , server . slave_priority ) ;
2013-05-29 05:36:44 -04:00
config_get_numerical_field ( " min-slaves-to-write " , server . repl_min_slaves_to_write ) ;
config_get_numerical_field ( " min-slaves-max-lag " , server . repl_min_slaves_max_lag ) ;
2012-12-14 11:10:40 -05:00
config_get_numerical_field ( " hz " , server . hz ) ;
2013-04-04 08:21:01 -04:00
config_get_numerical_field ( " cluster-node-timeout " , server . cluster_node_timeout ) ;
2014-01-31 05:12:34 -05:00
config_get_numerical_field ( " cluster-migration-barrier " , server . cluster_migration_barrier ) ;
2014-05-22 10:57:47 -04:00
config_get_numerical_field ( " cluster-slave-validity-factor " , server . cluster_slave_validity_factor ) ;
2014-10-27 05:36:30 -04:00
config_get_numerical_field ( " repl-diskless-sync-delay " , server . repl_diskless_sync_delay ) ;
2012-03-08 06:14:23 -05:00
/* Bool (yes/no) values */
2015-03-10 07:37:39 -04:00
config_get_bool_field ( " tcp-keepalive " , server . tcpkeepalive ) ;
2014-09-17 05:10:09 -04:00
config_get_bool_field ( " cluster-require-full-coverage " ,
server . cluster_require_full_coverage ) ;
2012-03-08 06:14:23 -05:00
config_get_bool_field ( " no-appendfsync-on-rewrite " ,
server . aof_no_fsync_on_rewrite ) ;
config_get_bool_field ( " slave-serve-stale-data " ,
server . repl_serve_stale_data ) ;
2012-03-20 12:32:48 -04:00
config_get_bool_field ( " slave-read-only " ,
server . repl_slave_ro ) ;
2012-03-08 06:14:23 -05:00
config_get_bool_field ( " stop-writes-on-bgsave-error " ,
server . stop_writes_on_bgsave_err ) ;
config_get_bool_field ( " daemonize " , server . daemonize ) ;
config_get_bool_field ( " rdbcompression " , server . rdb_compression ) ;
2012-04-10 09:47:10 -04:00
config_get_bool_field ( " rdbchecksum " , server . rdb_checksum ) ;
2012-03-08 06:14:23 -05:00
config_get_bool_field ( " activerehashing " , server . activerehashing ) ;
2013-01-31 05:14:15 -05:00
config_get_bool_field ( " repl-disable-tcp-nodelay " ,
server . repl_disable_tcp_nodelay ) ;
2014-10-16 04:22:02 -04:00
config_get_bool_field ( " repl-diskless-sync " ,
server . repl_diskless_sync ) ;
2013-04-24 04:57:07 -04:00
config_get_bool_field ( " aof-rewrite-incremental-fsync " ,
server . aof_rewrite_incremental_fsync ) ;
2014-09-08 03:23:55 -04:00
config_get_bool_field ( " aof-load-truncated " ,
server . aof_load_truncated ) ;
2012-03-08 06:14:23 -05:00
2015-03-11 11:59:56 -04:00
/* Enum values */
config_get_enum_field ( " maxmemory-policy " ,
server . maxmemory_policy , maxmemory_policy_enum ) ;
2015-03-11 18:20:57 -04:00
config_get_enum_field ( " loglevel " ,
server . verbosity , loglevel_enum ) ;
config_get_enum_field ( " supervised " ,
server . supervised_mode , supervised_mode_enum ) ;
config_get_enum_field ( " appendfsync " ,
server . aof_fsync , aof_fsync_enum ) ;
2015-03-12 04:59:10 -04:00
config_get_enum_field ( " syslog-facility " ,
server . syslog_facility , syslog_facility_enum ) ;
2015-03-11 11:59:56 -04:00
2012-03-08 06:14:23 -05:00
/* Everything we can't handle with macros follows. */
if ( stringmatch ( pattern , " appendonly " , 0 ) ) {
addReplyBulkCString ( c , " appendonly " ) ;
addReplyBulkCString ( c , server . aof_state = = REDIS_AOF_OFF ? " no " : " yes " ) ;
matches + + ;
}
2011-02-13 20:51:27 -05:00
if ( stringmatch ( pattern , " dir " , 0 ) ) {
char buf [ 1024 ] ;
2011-07-27 09:03:48 -04:00
if ( getcwd ( buf , sizeof ( buf ) ) = = NULL )
2011-02-21 11:41:25 -05:00
buf [ 0 ] = ' \0 ' ;
2011-07-27 09:03:48 -04:00
addReplyBulkCString ( c , " dir " ) ;
addReplyBulkCString ( c , buf ) ;
2011-02-13 20:51:27 -05:00
matches + + ;
}
2010-06-21 18:07:48 -04:00
if ( stringmatch ( pattern , " save " , 0 ) ) {
sds buf = sdsempty ( ) ;
int j ;
for ( j = 0 ; j < server . saveparamslen ; j + + ) {
2012-10-28 00:33:04 -04:00
buf = sdscatprintf ( buf , " %jd %d " ,
( intmax_t ) server . saveparams [ j ] . seconds ,
2010-06-21 18:07:48 -04:00
server . saveparams [ j ] . changes ) ;
if ( j ! = server . saveparamslen - 1 )
buf = sdscatlen ( buf , " " , 1 ) ;
}
addReplyBulkCString ( c , " save " ) ;
addReplyBulkCString ( c , buf ) ;
sdsfree ( buf ) ;
matches + + ;
}
2012-01-24 04:43:30 -05:00
if ( stringmatch ( pattern , " client-output-buffer-limit " , 0 ) ) {
sds buf = sdsempty ( ) ;
int j ;
2014-06-16 04:43:05 -04:00
for ( j = 0 ; j < REDIS_CLIENT_TYPE_COUNT ; j + + ) {
2012-01-24 04:43:30 -05:00
buf = sdscatprintf ( buf , " %s %llu %llu %ld " ,
2014-06-16 04:43:05 -04:00
getClientTypeName ( j ) ,
2012-01-24 04:43:30 -05:00
server . client_obuf_limits [ j ] . hard_limit_bytes ,
server . client_obuf_limits [ j ] . soft_limit_bytes ,
( long ) server . client_obuf_limits [ j ] . soft_limit_seconds ) ;
2014-06-16 04:43:05 -04:00
if ( j ! = REDIS_CLIENT_TYPE_COUNT - 1 )
2012-01-24 04:43:30 -05:00
buf = sdscatlen ( buf , " " , 1 ) ;
}
addReplyBulkCString ( c , " client-output-buffer-limit " ) ;
addReplyBulkCString ( c , buf ) ;
sdsfree ( buf ) ;
matches + + ;
}
2012-03-08 06:14:23 -05:00
if ( stringmatch ( pattern , " unixsocketperm " , 0 ) ) {
char buf [ 32 ] ;
snprintf ( buf , sizeof ( buf ) , " %o " , server . unixsocketperm ) ;
addReplyBulkCString ( c , " unixsocketperm " ) ;
addReplyBulkCString ( c , buf ) ;
matches + + ;
}
if ( stringmatch ( pattern , " slaveof " , 0 ) ) {
char buf [ 256 ] ;
addReplyBulkCString ( c , " slaveof " ) ;
if ( server . masterhost )
snprintf ( buf , sizeof ( buf ) , " %s %d " ,
server . masterhost , server . masterport ) ;
else
buf [ 0 ] = ' \0 ' ;
addReplyBulkCString ( c , buf ) ;
2012-03-07 12:02:26 -05:00
matches + + ;
}
2013-01-25 07:19:08 -05:00
if ( stringmatch ( pattern , " notify-keyspace-events " , 0 ) ) {
robj * flagsobj = createObject ( REDIS_STRING ,
keyspaceEventsFlagsToString ( server . notify_keyspace_events ) ) ;
addReplyBulkCString ( c , " notify-keyspace-events " ) ;
addReplyBulk ( c , flagsobj ) ;
decrRefCount ( flagsobj ) ;
matches + + ;
}
2013-07-04 12:50:15 -04:00
if ( stringmatch ( pattern , " bind " , 0 ) ) {
sds aux = sdsjoin ( server . bindaddr , server . bindaddr_count , " " ) ;
addReplyBulkCString ( c , " bind " ) ;
addReplyBulkCString ( c , aux ) ;
sdsfree ( aux ) ;
matches + + ;
}
2010-08-30 10:02:06 -04:00
setDeferredMultiBulkLength ( c , replylen , matches * 2 ) ;
2010-06-21 18:07:48 -04:00
}
2013-05-09 18:15:18 -04:00
/*-----------------------------------------------------------------------------
* CONFIG REWRITE implementation
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2013-11-19 11:58:11 -05:00
# define REDIS_CONFIG_REWRITE_SIGNATURE "# Generated by CONFIG REWRITE"
2013-05-09 18:15:18 -04:00
/* We use the following dictionary type to store where a configuration
* option is mentioned in the old configuration file , so it ' s
* like " maxmemory " - > list of line numbers ( first line is zero ) . */
2013-12-19 09:30:06 -05:00
unsigned int dictSdsCaseHash ( const void * key ) ;
int dictSdsKeyCaseCompare ( void * privdata , const void * key1 , const void * key2 ) ;
2013-05-09 18:15:18 -04:00
void dictSdsDestructor ( void * privdata , void * val ) ;
void dictListDestructor ( void * privdata , void * val ) ;
2013-11-19 03:48:12 -05:00
/* Sentinel config rewriting is implemented inside sentinel.c by
* rewriteConfigSentinelOption ( ) . */
void rewriteConfigSentinelOption ( struct rewriteConfigState * state ) ;
2013-05-09 18:15:18 -04:00
dictType optionToLineDictType = {
2013-12-19 09:25:45 -05:00
dictSdsCaseHash , /* hash function */
2013-05-09 18:15:18 -04:00
NULL , /* key dup */
NULL , /* val dup */
2013-12-19 09:25:45 -05:00
dictSdsKeyCaseCompare , /* key compare */
2013-05-09 18:15:18 -04:00
dictSdsDestructor , /* key destructor */
dictListDestructor /* val destructor */
} ;
2013-12-19 09:25:45 -05:00
dictType optionSetDictType = {
dictSdsCaseHash , /* hash function */
NULL , /* key dup */
NULL , /* val dup */
dictSdsKeyCaseCompare , /* key compare */
dictSdsDestructor , /* key destructor */
NULL /* val destructor */
} ;
2013-05-09 18:15:18 -04:00
/* The config rewrite state. */
struct rewriteConfigState {
dict * option_to_line ; /* Option -> list of config file lines map */
2013-12-19 09:25:45 -05:00
dict * rewritten ; /* Dictionary of already processed options */
2013-05-09 18:15:18 -04:00
int numlines ; /* Number of lines in current config */
sds * lines ; /* Current lines as an array of sds strings */
int has_tail ; /* True if we already added directives that were
not present in the original config file . */
} ;
/* Append the new line to the current configuration state. */
void rewriteConfigAppendLine ( struct rewriteConfigState * state , sds line ) {
state - > lines = zrealloc ( state - > lines , sizeof ( char * ) * ( state - > numlines + 1 ) ) ;
state - > lines [ state - > numlines + + ] = line ;
}
/* Populate the option -> list of line numbers map. */
void rewriteConfigAddLineNumberToOption ( struct rewriteConfigState * state , sds option , int linenum ) {
list * l = dictFetchValue ( state - > option_to_line , option ) ;
if ( l = = NULL ) {
l = listCreate ( ) ;
dictAdd ( state - > option_to_line , sdsdup ( option ) , l ) ;
}
listAddNodeTail ( l , ( void * ) ( long ) linenum ) ;
}
2013-12-19 09:25:45 -05:00
/* Add the specified option to the set of processed options.
* This is useful as only unused lines of processed options will be blanked
* in the config file , while options the rewrite process does not understand
* remain untouched . */
2015-03-11 11:59:56 -04:00
void rewriteConfigMarkAsProcessed ( struct rewriteConfigState * state , const char * option ) {
2013-12-19 09:25:45 -05:00
sds opt = sdsnew ( option ) ;
2013-12-19 09:30:06 -05:00
if ( dictAdd ( state - > rewritten , opt , NULL ) ! = DICT_OK ) sdsfree ( opt ) ;
2013-12-19 09:25:45 -05:00
}
2013-05-09 18:15:18 -04:00
/* Read the old file, split it into lines to populate a newly created
* config rewrite state , and return it to the caller .
*
* If it is impossible to read the old file , NULL is returned .
* If the old file does not exist at all , an empty state is returned . */
struct rewriteConfigState * rewriteConfigReadOldFile ( char * path ) {
FILE * fp = fopen ( path , " r " ) ;
struct rewriteConfigState * state = zmalloc ( sizeof ( * state ) ) ;
char buf [ REDIS_CONFIGLINE_MAX + 1 ] ;
int linenum = - 1 ;
if ( fp = = NULL & & errno ! = ENOENT ) return NULL ;
state - > option_to_line = dictCreate ( & optionToLineDictType , NULL ) ;
2013-12-19 09:25:45 -05:00
state - > rewritten = dictCreate ( & optionSetDictType , NULL ) ;
2013-05-09 18:15:18 -04:00
state - > numlines = 0 ;
state - > lines = NULL ;
state - > has_tail = 0 ;
if ( fp = = NULL ) return state ;
/* Read the old file line by line, populate the state. */
while ( fgets ( buf , REDIS_CONFIGLINE_MAX + 1 , fp ) ! = NULL ) {
int argc ;
sds * argv ;
sds line = sdstrim ( sdsnew ( buf ) , " \r \n \t " ) ;
linenum + + ; /* Zero based, so we init at -1 */
/* Handle comments and empty lines. */
if ( line [ 0 ] = = ' # ' | | line [ 0 ] = = ' \0 ' ) {
2013-11-19 11:58:11 -05:00
if ( ! state - > has_tail & & ! strcmp ( line , REDIS_CONFIG_REWRITE_SIGNATURE ) )
state - > has_tail = 1 ;
2013-05-09 18:15:18 -04:00
rewriteConfigAppendLine ( state , line ) ;
continue ;
}
/* Not a comment, split into arguments. */
argv = sdssplitargs ( line , & argc ) ;
if ( argv = = NULL ) {
/* Apparently the line is unparsable for some reason, for
* instance it may have unbalanced quotes . Load it as a
* comment . */
sds aux = sdsnew ( " # ??? " ) ;
aux = sdscatsds ( aux , line ) ;
sdsfree ( line ) ;
rewriteConfigAppendLine ( state , aux ) ;
continue ;
}
sdstolower ( argv [ 0 ] ) ; /* We only want lowercase config directives. */
/* Now we populate the state according to the content of this line.
* Append the line and populate the option - > line numbers map . */
rewriteConfigAppendLine ( state , line ) ;
rewriteConfigAddLineNumberToOption ( state , argv [ 0 ] , linenum ) ;
sdsfreesplitres ( argv , argc ) ;
}
fclose ( fp ) ;
return state ;
}
/* Rewrite the specified configuration option with the new "line".
* It progressively uses lines of the file that were already used for the same
2013-11-18 12:18:04 -05:00
* configuration option in the old version of the file , removing that line from
2013-05-09 18:15:18 -04:00
* the map of options - > line numbers .
*
* If there are lines associated with a given configuration option and
* " force " is non - zero , the line is appended to the configuration file .
* Usually " force " is true when an option has not its default value , so it
* must be rewritten even if not present previously .
2014-06-26 12:48:40 -04:00
*
2013-05-09 18:15:18 -04:00
* The first time a line is appended into a configuration file , a comment
* is added to show that starting from that point the config file was generated
* by CONFIG REWRITE .
*
* " line " is either used , or freed , so the caller does not need to free it
* in any way . */
2015-03-11 11:59:56 -04:00
void rewriteConfigRewriteLine ( struct rewriteConfigState * state , const char * option , sds line , int force ) {
2013-05-09 18:15:18 -04:00
sds o = sdsnew ( option ) ;
list * l = dictFetchValue ( state - > option_to_line , o ) ;
2013-12-19 09:25:45 -05:00
rewriteConfigMarkAsProcessed ( state , option ) ;
2013-05-09 18:15:18 -04:00
if ( ! l & & ! force ) {
/* Option not used previously, and we are not forced to use it. */
sdsfree ( line ) ;
sdsfree ( o ) ;
return ;
}
if ( l ) {
listNode * ln = listFirst ( l ) ;
int linenum = ( long ) ln - > value ;
/* There are still lines in the old configuration file we can reuse
* for this option . Replace the line with the new one . */
listDelNode ( l , ln ) ;
if ( listLength ( l ) = = 0 ) dictDelete ( state - > option_to_line , o ) ;
sdsfree ( state - > lines [ linenum ] ) ;
state - > lines [ linenum ] = line ;
} else {
/* Append a new line. */
if ( ! state - > has_tail ) {
rewriteConfigAppendLine ( state ,
2013-11-19 11:58:11 -05:00
sdsnew ( REDIS_CONFIG_REWRITE_SIGNATURE ) ) ;
2013-05-09 18:15:18 -04:00
state - > has_tail = 1 ;
}
rewriteConfigAppendLine ( state , line ) ;
}
sdsfree ( o ) ;
}
2013-05-15 05:33:02 -04:00
/* Write the long long 'bytes' value as a string in a way that is parsable
* inside redis . conf . If possible uses the GB , MB , KB notation . */
int rewriteConfigFormatMemory ( char * buf , size_t len , long long bytes ) {
int gb = 1024 * 1024 * 1024 ;
int mb = 1024 * 1024 ;
int kb = 1024 ;
if ( bytes & & ( bytes % gb ) = = 0 ) {
return snprintf ( buf , len , " %lldgb " , bytes / gb ) ;
} else if ( bytes & & ( bytes % mb ) = = 0 ) {
return snprintf ( buf , len , " %lldmb " , bytes / mb ) ;
} else if ( bytes & & ( bytes % kb ) = = 0 ) {
return snprintf ( buf , len , " %lldkb " , bytes / kb ) ;
} else {
return snprintf ( buf , len , " %lld " , bytes ) ;
}
}
2013-05-09 18:15:18 -04:00
/* Rewrite a simple "option-name <bytes>" configuration option. */
void rewriteConfigBytesOption ( struct rewriteConfigState * state , char * option , long long value , long long defvalue ) {
2013-05-15 05:33:02 -04:00
char buf [ 64 ] ;
2013-05-09 18:15:18 -04:00
int force = value ! = defvalue ;
2013-05-15 05:33:02 -04:00
sds line ;
2013-05-09 18:15:18 -04:00
2013-05-15 05:33:02 -04:00
rewriteConfigFormatMemory ( buf , sizeof ( buf ) , value ) ;
line = sdscatprintf ( sdsempty ( ) , " %s %s " , option , buf ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2013-05-14 05:17:18 -04:00
/* Rewrite a yes/no option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigYesNoOption ( struct rewriteConfigState * state , char * option , int value , int defvalue ) {
int force = value ! = defvalue ;
sds line = sdscatprintf ( sdsempty ( ) , " %s %s " , option ,
value ? " yes " : " no " ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2013-05-14 05:17:18 -04:00
/* Rewrite a string option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigStringOption ( struct rewriteConfigState * state , char * option , char * value , char * defvalue ) {
int force = 1 ;
sds line ;
/* String options set to NULL need to be not present at all in the
* configuration file to be set to NULL again at the next reboot . */
2013-12-19 09:25:45 -05:00
if ( value = = NULL ) {
rewriteConfigMarkAsProcessed ( state , option ) ;
return ;
}
2013-05-09 18:15:18 -04:00
2014-09-04 05:23:19 -04:00
/* Set force to zero if the value is set to its default. */
2013-05-09 18:15:18 -04:00
if ( defvalue & & strcmp ( value , defvalue ) = = 0 ) force = 0 ;
line = sdsnew ( option ) ;
line = sdscatlen ( line , " " , 1 ) ;
line = sdscatrepr ( line , value , strlen ( value ) ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2013-05-14 05:17:18 -04:00
/* Rewrite a numerical (long long range) option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigNumericalOption ( struct rewriteConfigState * state , char * option , long long value , long long defvalue ) {
int force = value ! = defvalue ;
sds line = sdscatprintf ( sdsempty ( ) , " %s %lld " , option , value ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2013-05-14 05:17:18 -04:00
/* Rewrite a octal option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigOctalOption ( struct rewriteConfigState * state , char * option , int value , int defvalue ) {
int force = value ! = defvalue ;
sds line = sdscatprintf ( sdsempty ( ) , " %s %o " , option , value ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2015-03-11 18:20:57 -04:00
/* Rewrite an enumeration option. It takes as usually state and option name,
* and in addition the enumeration array and the default value for the
* option . */
void rewriteConfigEnumOption ( struct rewriteConfigState * state , char * option , int value , configEnum * ce , int defval ) {
2013-05-09 18:15:18 -04:00
sds line ;
2015-03-11 18:20:57 -04:00
const char * name = configEnumGetNameOrUnknown ( ce , value ) ;
int force = value ! = defval ;
2014-06-26 12:48:40 -04:00
2015-03-11 18:20:57 -04:00
line = sdscatprintf ( sdsempty ( ) , " %s %s " , option , name ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2014-09-25 16:40:11 -04:00
/* Rewrite the syslog-facility option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigSyslogfacilityOption ( struct rewriteConfigState * state ) {
2015-03-11 11:59:56 -04:00
int value = server . syslog_facility ;
2013-05-13 05:11:35 -04:00
int force = value ! = LOG_LOCAL0 ;
2015-03-11 11:59:56 -04:00
const char * name = NULL , * option = " syslog-facility " ;
2013-05-13 05:11:35 -04:00
sds line ;
2015-03-11 11:59:56 -04:00
name = configEnumGetNameOrUnknown ( syslog_facility_enum , value ) ;
2013-05-13 05:11:35 -04:00
line = sdscatprintf ( sdsempty ( ) , " %s %s " , option , name ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
2013-05-09 18:15:18 -04:00
}
2013-05-14 05:17:18 -04:00
/* Rewrite the save option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigSaveOption ( struct rewriteConfigState * state ) {
2013-05-13 05:11:35 -04:00
int j ;
sds line ;
/* Note that if there are no save parameters at all, all the current
* config line with " save " will be detected as orphaned and deleted ,
* resulting into no RDB persistence as expected . */
for ( j = 0 ; j < server . saveparamslen ; j + + ) {
line = sdscatprintf ( sdsempty ( ) , " save %ld %d " ,
2014-03-05 05:26:14 -05:00
( long ) server . saveparams [ j ] . seconds , server . saveparams [ j ] . changes ) ;
2013-05-13 05:11:35 -04:00
rewriteConfigRewriteLine ( state , " save " , line , 1 ) ;
}
2013-12-23 06:48:39 -05:00
/* Mark "save" as processed in case server.saveparamslen is zero. */
rewriteConfigMarkAsProcessed ( state , " save " ) ;
2013-05-09 18:15:18 -04:00
}
2013-05-14 05:17:18 -04:00
/* Rewrite the dir option, always using absolute paths.*/
2013-05-09 18:15:18 -04:00
void rewriteConfigDirOption ( struct rewriteConfigState * state ) {
2013-05-13 05:26:43 -04:00
char cwd [ 1024 ] ;
2013-12-19 09:25:45 -05:00
if ( getcwd ( cwd , sizeof ( cwd ) ) = = NULL ) {
rewriteConfigMarkAsProcessed ( state , " dir " ) ;
return ; /* no rewrite on error. */
}
2013-05-13 05:26:43 -04:00
rewriteConfigStringOption ( state , " dir " , cwd , NULL ) ;
2013-05-09 18:15:18 -04:00
}
2013-05-14 05:17:18 -04:00
/* Rewrite the slaveof option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigSlaveofOption ( struct rewriteConfigState * state ) {
2013-05-13 12:34:18 -04:00
char * option = " slaveof " ;
2013-05-13 05:26:43 -04:00
sds line ;
/* If this is a master, we want all the slaveof config options
2014-01-20 05:10:42 -05:00
* in the file to be removed . Note that if this is a cluster instance
* we don ' t want a slaveof directive inside redis . conf . */
if ( server . cluster_enabled | | server . masterhost = = NULL ) {
2013-12-19 09:25:45 -05:00
rewriteConfigMarkAsProcessed ( state , " slaveof " ) ;
return ;
}
2013-05-13 12:34:18 -04:00
line = sdscatprintf ( sdsempty ( ) , " %s %s %d " , option ,
2013-05-13 05:26:43 -04:00
server . masterhost , server . masterport ) ;
2013-05-13 12:34:18 -04:00
rewriteConfigRewriteLine ( state , option , line , 1 ) ;
2013-05-09 18:15:18 -04:00
}
2013-05-14 05:17:18 -04:00
/* Rewrite the notify-keyspace-events option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigNotifykeyspaceeventsOption ( struct rewriteConfigState * state ) {
2013-05-13 12:34:18 -04:00
int force = server . notify_keyspace_events ! = 0 ;
char * option = " notify-keyspace-events " ;
sds line , flags ;
flags = keyspaceEventsFlagsToString ( server . notify_keyspace_events ) ;
2013-05-15 05:15:18 -04:00
line = sdsnew ( option ) ;
line = sdscatlen ( line , " " , 1 ) ;
line = sdscatrepr ( line , flags , sdslen ( flags ) ) ;
2013-05-13 12:34:18 -04:00
sdsfree ( flags ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
2013-05-09 18:15:18 -04:00
}
2013-05-14 05:17:18 -04:00
/* Rewrite the client-output-buffer-limit option. */
2013-05-09 18:15:18 -04:00
void rewriteConfigClientoutputbufferlimitOption ( struct rewriteConfigState * state ) {
2013-05-13 12:34:18 -04:00
int j ;
char * option = " client-output-buffer-limit " ;
2014-06-16 04:43:05 -04:00
for ( j = 0 ; j < REDIS_CLIENT_TYPE_COUNT ; j + + ) {
2013-05-13 12:34:18 -04:00
int force = ( server . client_obuf_limits [ j ] . hard_limit_bytes ! =
clientBufferLimitsDefaults [ j ] . hard_limit_bytes ) | |
( server . client_obuf_limits [ j ] . soft_limit_bytes ! =
clientBufferLimitsDefaults [ j ] . soft_limit_bytes ) | |
( server . client_obuf_limits [ j ] . soft_limit_seconds ! =
clientBufferLimitsDefaults [ j ] . soft_limit_seconds ) ;
sds line ;
2013-05-15 05:33:02 -04:00
char hard [ 64 ] , soft [ 64 ] ;
rewriteConfigFormatMemory ( hard , sizeof ( hard ) ,
server . client_obuf_limits [ j ] . hard_limit_bytes ) ;
rewriteConfigFormatMemory ( soft , sizeof ( soft ) ,
server . client_obuf_limits [ j ] . soft_limit_bytes ) ;
2013-05-13 12:34:18 -04:00
2013-05-15 05:33:02 -04:00
line = sdscatprintf ( sdsempty ( ) , " %s %s %s %s %ld " ,
2014-06-16 04:43:05 -04:00
option , getClientTypeName ( j ) , hard , soft ,
2013-05-13 12:34:18 -04:00
( long ) server . client_obuf_limits [ j ] . soft_limit_seconds ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2013-05-09 18:15:18 -04:00
}
2013-07-04 12:50:15 -04:00
/* Rewrite the bind option. */
void rewriteConfigBindOption ( struct rewriteConfigState * state ) {
int force = 1 ;
sds line , addresses ;
char * option = " bind " ;
/* Nothing to rewrite if we don't have bind addresses. */
2013-12-19 09:25:45 -05:00
if ( server . bindaddr_count = = 0 ) {
rewriteConfigMarkAsProcessed ( state , option ) ;
return ;
}
2013-07-04 12:50:15 -04:00
/* Rewrite as bind <addr1> <addr2> ... <addrN> */
addresses = sdsjoin ( server . bindaddr , server . bindaddr_count , " " ) ;
line = sdsnew ( option ) ;
line = sdscatlen ( line , " " , 1 ) ;
line = sdscatsds ( line , addresses ) ;
sdsfree ( addresses ) ;
rewriteConfigRewriteLine ( state , option , line , force ) ;
}
2013-05-14 04:22:55 -04:00
/* Glue together the configuration lines in the current configuration
* rewrite state into a single string , stripping multiple empty lines . */
2013-05-09 18:15:18 -04:00
sds rewriteConfigGetContentFromState ( struct rewriteConfigState * state ) {
sds content = sdsempty ( ) ;
2013-05-14 04:22:55 -04:00
int j , was_empty = 0 ;
2013-05-09 18:15:18 -04:00
for ( j = 0 ; j < state - > numlines ; j + + ) {
2013-05-14 04:22:55 -04:00
/* Every cluster of empty lines is turned into a single empty line. */
if ( sdslen ( state - > lines [ j ] ) = = 0 ) {
if ( was_empty ) continue ;
was_empty = 1 ;
} else {
was_empty = 0 ;
}
2013-05-09 18:15:18 -04:00
content = sdscatsds ( content , state - > lines [ j ] ) ;
content = sdscatlen ( content , " \n " , 1 ) ;
}
return content ;
}
2013-05-14 05:17:18 -04:00
/* Free the configuration rewrite state. */
2013-05-09 18:15:18 -04:00
void rewriteConfigReleaseState ( struct rewriteConfigState * state ) {
sdsfreesplitres ( state - > lines , state - > numlines ) ;
dictRelease ( state - > option_to_line ) ;
2013-12-19 09:25:45 -05:00
dictRelease ( state - > rewritten ) ;
2013-05-09 18:15:18 -04:00
zfree ( state ) ;
}
2013-05-14 05:17:18 -04:00
/* At the end of the rewrite process the state contains the remaining
* map between " option name " = > " lines in the original config file " .
* Lines used by the rewrite process were removed by the function
* rewriteConfigRewriteLine ( ) , all the other lines are " orphaned " and
* should be replaced by empty lines .
*
* This function does just this , iterating all the option names and
2013-12-19 09:55:25 -05:00
* blanking all the lines still associated . */
2013-05-14 05:17:18 -04:00
void rewriteConfigRemoveOrphaned ( struct rewriteConfigState * state ) {
dictIterator * di = dictGetIterator ( state - > option_to_line ) ;
dictEntry * de ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
list * l = dictGetVal ( de ) ;
2013-12-19 09:25:45 -05:00
sds option = dictGetKey ( de ) ;
/* Don't blank lines about options the rewrite process
* don ' t understand . */
2013-12-23 06:48:39 -05:00
if ( dictFind ( state - > rewritten , option ) = = NULL ) {
2013-12-19 09:25:45 -05:00
redisLog ( REDIS_DEBUG , " Not rewritten option: %s " , option ) ;
continue ;
}
2013-05-14 05:17:18 -04:00
while ( listLength ( l ) ) {
listNode * ln = listFirst ( l ) ;
int linenum = ( long ) ln - > value ;
sdsfree ( state - > lines [ linenum ] ) ;
state - > lines [ linenum ] = sdsempty ( ) ;
listDelNode ( l , ln ) ;
}
}
dictReleaseIterator ( di ) ;
}
2013-05-14 06:32:25 -04:00
/* This function overwrites the old configuration file with the new content.
*
* 1 ) The old file length is obtained .
* 2 ) If the new content is smaller , padding is added .
* 3 ) A single write ( 2 ) call is used to replace the content of the file .
* 4 ) Later the file is truncated to the length of the new content .
*
* This way we are sure the file is left in a consistent state even if the
* process is stopped between any of the four operations .
*
* The function returns 0 on success , otherwise - 1 is returned and errno
* set accordingly . */
int rewriteConfigOverwriteFile ( char * configfile , sds content ) {
int retval = 0 ;
2013-05-14 06:45:04 -04:00
int fd = open ( configfile , O_RDWR | O_CREAT , 0644 ) ;
2013-05-14 06:32:25 -04:00
int content_size = sdslen ( content ) , padding = 0 ;
struct stat sb ;
sds content_padded ;
/* 1) Open the old file (or create a new one if it does not
* exist ) , get the size . */
if ( fd = = - 1 ) return - 1 ; /* errno set by open(). */
if ( fstat ( fd , & sb ) = = - 1 ) {
close ( fd ) ;
return - 1 ; /* errno set by fstat(). */
}
/* 2) Pad the content at least match the old file size. */
content_padded = sdsdup ( content ) ;
if ( content_size < sb . st_size ) {
/* If the old file was bigger, pad the content with
* a newline plus as many " # " chars as required . */
padding = sb . st_size - content_size ;
content_padded = sdsgrowzero ( content_padded , sb . st_size ) ;
content_padded [ content_size ] = ' \n ' ;
memset ( content_padded + content_size + 1 , ' # ' , padding - 1 ) ;
}
/* 3) Write the new content using a single write(2). */
if ( write ( fd , content_padded , strlen ( content_padded ) ) = = - 1 ) {
retval = - 1 ;
goto cleanup ;
}
/* 4) Truncate the file to the right length if we used padding. */
if ( padding ) {
if ( ftruncate ( fd , content_size ) = = - 1 ) {
/* Non critical error... */
}
}
cleanup :
sdsfree ( content_padded ) ;
close ( fd ) ;
return retval ;
}
2013-05-09 18:15:18 -04:00
/* Rewrite the configuration file at "path".
* If the configuration file already exists , we try at best to retain comments
* and overall structure .
*
* Configuration parameters that are at their default value , unless already
* explicitly included in the old configuration file , are not rewritten .
*
* On error - 1 is returned and errno is set accordingly , otherwise 0. */
int rewriteConfig ( char * path ) {
struct rewriteConfigState * state ;
sds newcontent ;
2013-05-14 06:32:25 -04:00
int retval ;
2013-05-09 18:15:18 -04:00
/* Step 1: read the old config into our rewrite state. */
if ( ( state = rewriteConfigReadOldFile ( path ) ) = = NULL ) return - 1 ;
/* Step 2: rewrite every single option, replacing or appending it inside
* the rewrite state . */
rewriteConfigYesNoOption ( state , " daemonize " , server . daemonize , 0 ) ;
rewriteConfigStringOption ( state , " pidfile " , server . pidfile , REDIS_DEFAULT_PID_FILE ) ;
rewriteConfigNumericalOption ( state , " port " , server . port , REDIS_SERVERPORT ) ;
2014-01-31 08:55:43 -05:00
rewriteConfigNumericalOption ( state , " tcp-backlog " , server . tcp_backlog , REDIS_TCP_BACKLOG ) ;
2013-07-04 12:50:15 -04:00
rewriteConfigBindOption ( state ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigStringOption ( state , " unixsocket " , server . unixsocket , NULL ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigOctalOption ( state , " unixsocketperm " , server . unixsocketperm , REDIS_DEFAULT_UNIX_SOCKET_PERM ) ;
rewriteConfigNumericalOption ( state , " timeout " , server . maxidletime , REDIS_MAXIDLETIME ) ;
rewriteConfigNumericalOption ( state , " tcp-keepalive " , server . tcpkeepalive , REDIS_DEFAULT_TCP_KEEPALIVE ) ;
2015-03-11 18:20:57 -04:00
rewriteConfigEnumOption ( state , " loglevel " , server . verbosity , loglevel_enum , REDIS_DEFAULT_VERBOSITY ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigStringOption ( state , " logfile " , server . logfile , REDIS_DEFAULT_LOGFILE ) ;
rewriteConfigYesNoOption ( state , " syslog-enabled " , server . syslog_enabled , REDIS_DEFAULT_SYSLOG_ENABLED ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigStringOption ( state , " syslog-ident " , server . syslog_ident , REDIS_DEFAULT_SYSLOG_IDENT ) ;
rewriteConfigSyslogfacilityOption ( state ) ;
rewriteConfigSaveOption ( state ) ;
rewriteConfigNumericalOption ( state , " databases " , server . dbnum , REDIS_DEFAULT_DBNUM ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigYesNoOption ( state , " stop-writes-on-bgsave-error " , server . stop_writes_on_bgsave_err , REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR ) ;
rewriteConfigYesNoOption ( state , " rdbcompression " , server . rdb_compression , REDIS_DEFAULT_RDB_COMPRESSION ) ;
rewriteConfigYesNoOption ( state , " rdbchecksum " , server . rdb_checksum , REDIS_DEFAULT_RDB_CHECKSUM ) ;
rewriteConfigStringOption ( state , " dbfilename " , server . rdb_filename , REDIS_DEFAULT_RDB_FILENAME ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigDirOption ( state ) ;
rewriteConfigSlaveofOption ( state ) ;
rewriteConfigStringOption ( state , " masterauth " , server . masterauth , NULL ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigYesNoOption ( state , " slave-serve-stale-data " , server . repl_serve_stale_data , REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA ) ;
rewriteConfigYesNoOption ( state , " slave-read-only " , server . repl_slave_ro , REDIS_DEFAULT_SLAVE_READ_ONLY ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " repl-ping-slave-period " , server . repl_ping_slave_period , REDIS_REPL_PING_SLAVE_PERIOD ) ;
rewriteConfigNumericalOption ( state , " repl-timeout " , server . repl_timeout , REDIS_REPL_TIMEOUT ) ;
2013-05-15 05:39:29 -04:00
rewriteConfigBytesOption ( state , " repl-backlog-size " , server . repl_backlog_size , REDIS_DEFAULT_REPL_BACKLOG_SIZE ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigBytesOption ( state , " repl-backlog-ttl " , server . repl_backlog_time_limit , REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT ) ;
2013-05-15 05:04:53 -04:00
rewriteConfigYesNoOption ( state , " repl-disable-tcp-nodelay " , server . repl_disable_tcp_nodelay , REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY ) ;
2014-10-16 04:22:02 -04:00
rewriteConfigYesNoOption ( state , " repl-diskless-sync " , server . repl_diskless_sync , REDIS_DEFAULT_REPL_DISKLESS_SYNC ) ;
2014-10-27 05:36:30 -04:00
rewriteConfigNumericalOption ( state , " repl-diskless-sync-delay " , server . repl_diskless_sync_delay , REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " slave-priority " , server . slave_priority , REDIS_DEFAULT_SLAVE_PRIORITY ) ;
2013-11-30 10:57:48 -05:00
rewriteConfigNumericalOption ( state , " min-slaves-to-write " , server . repl_min_slaves_to_write , REDIS_DEFAULT_MIN_SLAVES_TO_WRITE ) ;
rewriteConfigNumericalOption ( state , " min-slaves-max-lag " , server . repl_min_slaves_max_lag , REDIS_DEFAULT_MIN_SLAVES_MAX_LAG ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigStringOption ( state , " requirepass " , server . requirepass , NULL ) ;
rewriteConfigNumericalOption ( state , " maxclients " , server . maxclients , REDIS_MAX_CLIENTS ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigBytesOption ( state , " maxmemory " , server . maxmemory , REDIS_DEFAULT_MAXMEMORY ) ;
2015-03-11 18:20:57 -04:00
rewriteConfigEnumOption ( state , " maxmemory-policy " , server . maxmemory_policy , maxmemory_policy_enum , REDIS_DEFAULT_MAXMEMORY_POLICY ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigNumericalOption ( state , " maxmemory-samples " , server . maxmemory_samples , REDIS_DEFAULT_MAXMEMORY_SAMPLES ) ;
2013-11-30 10:57:48 -05:00
rewriteConfigYesNoOption ( state , " appendonly " , server . aof_state ! = REDIS_AOF_OFF , 0 ) ;
rewriteConfigStringOption ( state , " appendfilename " , server . aof_filename , REDIS_DEFAULT_AOF_FILENAME ) ;
2015-03-11 18:20:57 -04:00
rewriteConfigEnumOption ( state , " appendfsync " , server . aof_fsync , aof_fsync_enum , REDIS_DEFAULT_AOF_FSYNC ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigYesNoOption ( state , " no-appendfsync-on-rewrite " , server . aof_no_fsync_on_rewrite , REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " auto-aof-rewrite-percentage " , server . aof_rewrite_perc , REDIS_AOF_REWRITE_PERC ) ;
rewriteConfigBytesOption ( state , " auto-aof-rewrite-min-size " , server . aof_rewrite_min_size , REDIS_AOF_REWRITE_MIN_SIZE ) ;
rewriteConfigNumericalOption ( state , " lua-time-limit " , server . lua_time_limit , REDIS_LUA_TIME_LIMIT ) ;
rewriteConfigYesNoOption ( state , " cluster-enabled " , server . cluster_enabled , 0 ) ;
rewriteConfigStringOption ( state , " cluster-config-file " , server . cluster_configfile , REDIS_DEFAULT_CLUSTER_CONFIG_FILE ) ;
2014-09-17 05:10:09 -04:00
rewriteConfigYesNoOption ( state , " cluster-require-full-coverage " , server . cluster_require_full_coverage , REDIS_CLUSTER_DEFAULT_REQUIRE_FULL_COVERAGE ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " cluster-node-timeout " , server . cluster_node_timeout , REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT ) ;
2014-01-31 05:12:34 -05:00
rewriteConfigNumericalOption ( state , " cluster-migration-barrier " , server . cluster_migration_barrier , REDIS_CLUSTER_DEFAULT_MIGRATION_BARRIER ) ;
2014-05-22 10:57:47 -04:00
rewriteConfigNumericalOption ( state , " cluster-slave-validity-factor " , server . cluster_slave_validity_factor , REDIS_CLUSTER_DEFAULT_SLAVE_VALIDITY ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " slowlog-log-slower-than " , server . slowlog_log_slower_than , REDIS_SLOWLOG_LOG_SLOWER_THAN ) ;
2014-07-02 06:27:24 -04:00
rewriteConfigNumericalOption ( state , " latency-monitor-threshold " , server . latency_monitor_threshold , REDIS_DEFAULT_LATENCY_MONITOR_THRESHOLD ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " slowlog-max-len " , server . slowlog_max_len , REDIS_SLOWLOG_MAX_LEN ) ;
rewriteConfigNotifykeyspaceeventsOption ( state ) ;
rewriteConfigNumericalOption ( state , " hash-max-ziplist-entries " , server . hash_max_ziplist_entries , REDIS_HASH_MAX_ZIPLIST_ENTRIES ) ;
rewriteConfigNumericalOption ( state , " hash-max-ziplist-value " , server . hash_max_ziplist_value , REDIS_HASH_MAX_ZIPLIST_VALUE ) ;
2014-12-16 00:49:14 -05:00
rewriteConfigNumericalOption ( state , " list-max-ziplist-size " , server . list_max_ziplist_size , REDIS_LIST_MAX_ZIPLIST_SIZE ) ;
rewriteConfigNumericalOption ( state , " list-compress-depth " , server . list_compress_depth , REDIS_LIST_COMPRESS_DEPTH ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigNumericalOption ( state , " set-max-intset-entries " , server . set_max_intset_entries , REDIS_SET_MAX_INTSET_ENTRIES ) ;
rewriteConfigNumericalOption ( state , " zset-max-ziplist-entries " , server . zset_max_ziplist_entries , REDIS_ZSET_MAX_ZIPLIST_ENTRIES ) ;
rewriteConfigNumericalOption ( state , " zset-max-ziplist-value " , server . zset_max_ziplist_value , REDIS_ZSET_MAX_ZIPLIST_VALUE ) ;
2014-04-15 11:46:51 -04:00
rewriteConfigNumericalOption ( state , " hll-sparse-max-bytes " , server . hll_sparse_max_bytes , REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES ) ;
2013-05-15 05:09:19 -04:00
rewriteConfigYesNoOption ( state , " activerehashing " , server . activerehashing , REDIS_DEFAULT_ACTIVE_REHASHING ) ;
2013-05-09 18:15:18 -04:00
rewriteConfigClientoutputbufferlimitOption ( state ) ;
rewriteConfigNumericalOption ( state , " hz " , server . hz , REDIS_DEFAULT_HZ ) ;
2013-05-15 04:12:29 -04:00
rewriteConfigYesNoOption ( state , " aof-rewrite-incremental-fsync " , server . aof_rewrite_incremental_fsync , REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC ) ;
2014-09-08 03:23:55 -04:00
rewriteConfigYesNoOption ( state , " aof-load-truncated " , server . aof_load_truncated , REDIS_DEFAULT_AOF_LOAD_TRUNCATED ) ;
2015-03-11 18:20:57 -04:00
rewriteConfigEnumOption ( state , " supervised " , server . supervised_mode , supervised_mode_enum , REDIS_SUPERVISED_NONE ) ;
/* Rewrite Sentinel config if in Sentinel mode. */
2013-11-19 03:48:12 -05:00
if ( server . sentinel_mode ) rewriteConfigSentinelOption ( state ) ;
2013-05-09 18:15:18 -04:00
/* Step 3: remove all the orphaned lines in the old file, that is, lines
* that were used by a config option and are no longer used , like in case
* of multiple " save " options or duplicated options . */
2013-05-14 05:17:18 -04:00
rewriteConfigRemoveOrphaned ( state ) ;
2013-05-09 18:15:18 -04:00
/* Step 4: generate a new configuration file from the modified state
* and write it into the original file . */
newcontent = rewriteConfigGetContentFromState ( state ) ;
2013-05-14 06:32:25 -04:00
retval = rewriteConfigOverwriteFile ( server . configfile , newcontent ) ;
2013-05-09 18:15:18 -04:00
sdsfree ( newcontent ) ;
rewriteConfigReleaseState ( state ) ;
2013-05-14 06:32:25 -04:00
return retval ;
2013-05-09 18:15:18 -04:00
}
/*-----------------------------------------------------------------------------
* CONFIG command entry point
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2010-06-21 18:07:48 -04:00
void configCommand ( redisClient * c ) {
if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " set " ) ) {
if ( c - > argc ! = 4 ) goto badarity ;
configSetCommand ( c ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " get " ) ) {
if ( c - > argc ! = 3 ) goto badarity ;
configGetCommand ( c ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " resetstat " ) ) {
if ( c - > argc ! = 2 ) goto badarity ;
2014-03-19 07:55:49 -04:00
resetServerStats ( ) ;
2011-01-24 04:56:06 -05:00
resetCommandTableStats ( ) ;
2010-06-21 18:07:48 -04:00
addReply ( c , shared . ok ) ;
2013-05-09 18:15:18 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " rewrite " ) ) {
if ( c - > argc ! = 2 ) goto badarity ;
if ( server . configfile = = NULL ) {
addReplyError ( c , " The server is running without a config file " ) ;
return ;
}
if ( rewriteConfig ( server . configfile ) = = - 1 ) {
2014-02-13 08:32:44 -05:00
redisLog ( REDIS_WARNING , " CONFIG REWRITE failed: %s " , strerror ( errno ) ) ;
2013-05-09 18:15:18 -04:00
addReplyErrorFormat ( c , " Rewriting config file: %s " , strerror ( errno ) ) ;
} else {
2014-03-04 10:39:44 -05:00
redisLog ( REDIS_WARNING , " CONFIG REWRITE executed with success. " ) ;
2013-05-09 18:15:18 -04:00
addReply ( c , shared . ok ) ;
}
2010-06-21 18:07:48 -04:00
} else {
2010-09-02 13:52:24 -04:00
addReplyError ( c ,
2013-09-30 05:53:18 -04:00
" CONFIG subcommand must be one of GET, SET, RESETSTAT, REWRITE " ) ;
2010-06-21 18:07:48 -04:00
}
return ;
badarity :
2010-09-02 13:52:24 -04:00
addReplyErrorFormat ( c , " Wrong number of arguments for CONFIG %s " ,
( char * ) c - > argv [ 1 ] - > ptr ) ;
2010-06-21 18:07:48 -04:00
}