#include <float.h>
#include <math.h>
#include <sys/resource.h>
+#include <sys/utsname.h>
/* Our shared "common" objects */
/*================================= Globals ================================= */
+/* Alternate stack for SIGSEGV/etc handlers */
+char altstack[SIGSTKSZ];
+
/* Global vars */
struct redisServer server; /* server global state */
struct redisCommand *commandTable;
{"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
{"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
{"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
- {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0},
+ {"slaveof",slaveofCommand,3,"as",0,NULL,0,0,0,0,0},
{"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
void redisLogRaw(int level, const char *msg) {
const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
const char *c = ".-*#";
- time_t now = time(NULL);
FILE *fp;
char buf[64];
int rawmode = (level & REDIS_LOG_RAW);
if (rawmode) {
fprintf(fp,"%s",msg);
} else {
- strftime(buf,sizeof(buf),"%d %b %H:%M:%S",localtime(&now));
+ int off;
+ struct timeval tv;
+
+ gettimeofday(&tv,NULL);
+ off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec));
+ snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg);
}
fflush(fp);
redisLogRaw(level,msg);
}
+/* Log a fixed message without printf-alike capabilities, in a way that is
+ * safe to call from a signal handler.
+ *
+ * We actually use this only for signals that are not fatal from the point
+ * of view of Redis. Signals that are going to kill the server anyway and
+ * where we need printf-alike features are served by redisLog(). */
+void redisLogFromHandler(int level, const char *msg) {
+ int fd;
+ char buf[64];
+
+ if ((level&0xff) < server.verbosity ||
+ (server.logfile == NULL && server.daemonize)) return;
+ fd = server.logfile ?
+ open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) :
+ STDOUT_FILENO;
+ if (fd == -1) return;
+ ll2string(buf,sizeof(buf),getpid());
+ if (write(fd,"[",1) == -1) goto err;
+ if (write(fd,buf,strlen(buf)) == -1) goto err;
+ if (write(fd," | signal handler] (",20) == -1) goto err;
+ ll2string(buf,sizeof(buf),time(NULL));
+ if (write(fd,buf,strlen(buf)) == -1) goto err;
+ if (write(fd,") ",2) == -1) goto err;
+ if (write(fd,msg,strlen(msg)) == -1) goto err;
+ if (write(fd,"\n",1) == -1) goto err;
+err:
+ if (server.logfile) close(fd);
+}
+
/* Redis generally does not try to recover from out of memory conditions
* when allocating objects or strings, it is not clear if it will be possible
* to report this condition to the client since the networking layer itself
return ustime()/1000;
}
+/* After an RDB dump or AOF rewrite we exit from children using _exit() instead of
+ * exit(), because the latter may interact with the same file objects used by
+ * the parent process. However if we are testing the coverage normal exit() is
+ * used in order to obtain the right coverage information. */
+void exitFromChild(int retcode) {
+#ifdef COVERAGE_TEST
+ exit(retcode);
+#else
+ _exit(retcode);
+#endif
+}
+
/*====================== Hash table type implementation ==================== */
/* This is an hash table type that uses the SDS dynamic strings libary as
}
void updateLRUClock(void) {
- server.lruclock = (time(NULL)/REDIS_LRU_CLOCK_RESOLUTION) &
+ server.lruclock = (server.unixtime/REDIS_LRU_CLOCK_RESOLUTION) &
REDIS_LRU_CLOCK_MAX;
}
REDIS_NOTUSED(id);
REDIS_NOTUSED(clientData);
+ /* Software watchdog: deliver the SIGALRM that will reach the signal
+ * handler if we don't return here fast enough. */
+ if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);
+
/* We take a cached value of the unix time in the global state because
* with virtual memory and aging there is to store the current time
* in objects at every object access, and accuracy is not needed.
updateDictResizePolicy();
}
} else {
- time_t now = time(NULL);
-
/* If there is not a background saving/rewrite in progress check if
* we have to save/rewrite now */
for (j = 0; j < server.saveparamslen; j++) {
struct saveparam *sp = server.saveparams+j;
if (server.dirty >= sp->changes &&
- now-server.lastsave > sp->seconds) {
+ server.unixtime-server.lastsave > sp->seconds) {
redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
sp->changes, sp->seconds);
rdbSaveBackground(server.rdb_filename);
shared.space = createObject(REDIS_STRING,sdsnew(" "));
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
shared.plus = createObject(REDIS_STRING,sdsnew("+"));
- shared.select0 = createStringObject("select 0\r\n",10);
- shared.select1 = createStringObject("select 1\r\n",10);
- shared.select2 = createStringObject("select 2\r\n",10);
- shared.select3 = createStringObject("select 3\r\n",10);
- shared.select4 = createStringObject("select 4\r\n",10);
- shared.select5 = createStringObject("select 5\r\n",10);
- shared.select6 = createStringObject("select 6\r\n",10);
- shared.select7 = createStringObject("select 7\r\n",10);
- shared.select8 = createStringObject("select 8\r\n",10);
- shared.select9 = createStringObject("select 9\r\n",10);
+
+ for (j = 0; j < REDIS_SHARED_SELECT_CMDS; j++) {
+ shared.select[j] = createObject(REDIS_STRING,
+ sdscatprintf(sdsempty(),"select %d\r\n", j));
+ }
shared.messagebulk = createStringObject("$7\r\nmessage\r\n",13);
shared.pmessagebulk = createStringObject("$8\r\npmessage\r\n",14);
shared.subscribebulk = createStringObject("$9\r\nsubscribe\r\n",15);
server.aof_rewrite_base_size = 0;
server.aof_rewrite_scheduled = 0;
server.aof_last_fsync = time(NULL);
+ server.aof_delayed_fsync = 0;
server.aof_fd = -1;
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
server.aof_filename = zstrdup("appendonly.aof");
server.requirepass = NULL;
server.rdb_compression = 1;
+ server.rdb_checksum = 1;
server.activerehashing = 1;
server.maxclients = REDIS_MAX_CLIENTS;
server.bpop_blocked_clients = 0;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = 1;
server.repl_slave_ro = 1;
- server.repl_down_since = -1;
+ server.repl_down_since = time(NULL);
/* Client output buffer limits */
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].hard_limit_bytes = 0;
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
- /* Assert */
+ /* Debugging */
server.assert_failed = "<no assertion failed>";
server.assert_file = "<no file>";
server.assert_line = 0;
server.bug_report_start = 0;
+ server.watchdog_period = 0;
}
/* This function will try to raise the max number of open files accordingly to
/* Set the max number of files if the current limit is not enough
* for our needs. */
if (oldlimit < maxfiles) {
- limit.rlim_cur = maxfiles;
- limit.rlim_max = maxfiles;
- if (setrlimit(RLIMIT_NOFILE,&limit) == -1) {
- server.maxclients = oldlimit-32;
+ rlim_t f;
+
+ f = maxfiles;
+ while(f > oldlimit) {
+ limit.rlim_cur = f;
+ limit.rlim_max = f;
+ if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
+ f -= 128;
+ }
+ if (f < oldlimit) f = oldlimit;
+ if (f != maxfiles) {
+ server.maxclients = f-32;
redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.",
(int) maxfiles, strerror(errno), (int) server.maxclients);
} else {
* on memory corruption problems. */
sds genRedisInfoString(char *section) {
sds info = sdsempty();
- time_t uptime = time(NULL)-server.stat_starttime;
+ time_t uptime = server.unixtime-server.stat_starttime;
int j, numcommands;
struct rusage self_ru, c_ru;
unsigned long lol, bib;
/* Server */
if (allsections || defsections || !strcasecmp(section,"server")) {
+ struct utsname name;
+
if (sections++) info = sdscat(info,"\r\n");
+ uname(&name);
info = sdscatprintf(info,
"# Server\r\n"
"redis_version:%s\r\n"
"redis_git_sha1:%s\r\n"
"redis_git_dirty:%d\r\n"
+ "os:%s %s %s\r\n"
"arch_bits:%d\r\n"
"multiplexing_api:%s\r\n"
"gcc_version:%d.%d.%d\r\n"
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
+ name.sysname, name.release, name.machine,
server.arch_bits,
aeGetApiName(),
#ifdef __GNUC__
"bgsave_in_progress:%d\r\n"
"last_save_time:%ld\r\n"
"last_bgsave_status:%s\r\n"
- "bgrewriteaof_in_progress:%d\r\n",
+ "bgrewriteaof_in_progress:%d\r\n"
+ "bgrewriteaof_scheduled:%d\r\n",
server.loading,
server.aof_state != REDIS_AOF_OFF,
server.dirty,
server.rdb_child_pid != -1,
server.lastsave,
server.lastbgsave_status == REDIS_OK ? "ok" : "err",
- server.aof_child_pid != -1);
+ server.aof_child_pid != -1,
+ server.aof_rewrite_scheduled);
if (server.aof_state != REDIS_AOF_OFF) {
info = sdscatprintf(info,
"aof_base_size:%lld\r\n"
"aof_pending_rewrite:%d\r\n"
"aof_buffer_length:%zu\r\n"
- "aof_pending_bio_fsync:%llu\r\n",
+ "aof_pending_bio_fsync:%llu\r\n"
+ "aof_delayed_fsync:%lu\r\n",
(long long) server.aof_current_size,
(long long) server.aof_rewrite_base_size,
server.aof_rewrite_scheduled,
sdslen(server.aof_buf),
- bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC));
+ bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC),
+ server.aof_delayed_fsync);
}
if (server.loading) {
perc = ((double)server.loading_loaded_bytes /
server.loading_total_bytes) * 100;
- elapsed = time(NULL)-server.loading_start_time;
+ elapsed = server.unixtime-server.loading_start_time;
if (elapsed == 0) {
eta = 1; /* A fake 1 second figure if we don't have
enough info */
(server.repl_state == REDIS_REPL_CONNECTED) ?
"up" : "down",
server.master ?
- ((int)(time(NULL)-server.master->lastinteraction)) : -1,
+ ((int)(server.unixtime-server.master->lastinteraction)) : -1,
server.repl_state == REDIS_REPL_TRANSFER
);
"master_sync_left_bytes:%ld\r\n"
"master_sync_last_io_seconds_ago:%d\r\n"
,(long)server.repl_transfer_left,
- (int)(time(NULL)-server.repl_transfer_lastio)
+ (int)(server.unixtime-server.repl_transfer_lastio)
);
}
if (server.repl_state != REDIS_REPL_CONNECTED) {
info = sdscatprintf(info,
"master_link_down_since_seconds:%ld\r\n",
- (long)time(NULL)-server.repl_down_since);
+ (long)server.unixtime-server.repl_down_since);
}
}
info = sdscatprintf(info,
}
}
- /* Clusetr */
+ /* Cluster */
if (allsections || defsections || !strcasecmp(section,"cluster")) {
if (sections++) info = sdscat(info,"\r\n");
info = sdscatprintf(info,
static void sigtermHandler(int sig) {
REDIS_NOTUSED(sig);
- redisLog(REDIS_WARNING,"Received SIGTERM, scheduling shutdown...");
+ redisLogFromHandler(REDIS_WARNING,"Received SIGTERM, scheduling shutdown...");
server.shutdown_asap = 1;
}
void setupSignalHandlers(void) {
struct sigaction act;
+ stack_t stack;
+
+ stack.ss_sp = altstack;
+ stack.ss_flags = 0;
+ stack.ss_size = SIGSTKSZ;
+
+ sigaltstack(&stack, NULL);
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
* Otherwise, sa_handler is used. */
sigemptyset(&act.sa_mask);
- act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
+ act.sa_flags = 0;
act.sa_handler = sigtermHandler;
sigaction(SIGTERM, &act, NULL);
#ifdef HAVE_BACKTRACE
+ /* Use alternate stack so we don't clobber stack in case of segv, or when we run out of stack ..
+ * also resethand & nodefer so we can get interrupted (and killed) if we cause SEGV during SEGV handler */
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
act.sa_sigaction = sigsegvHandler;