summaryrefslogtreecommitdiff
path: root/src/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.c')
-rw-r--r--src/server.c348
1 files changed, 227 insertions, 121 deletions
diff --git a/src/server.c b/src/server.c
index 4784431ba..993260619 100644
--- a/src/server.c
+++ b/src/server.c
@@ -901,7 +901,7 @@ struct redisCommand redisCommandTable[] = {
0,NULL,0,0,0,0,0,0},
{"hello",helloCommand,-1,
- "no-auth no-script fast no-monitor ok-loading ok-stale no-slowlog @connection",
+ "no-auth no-script fast no-monitor ok-loading ok-stale @connection",
0,NULL,0,0,0,0,0,0},
/* EVAL can modify the dataset, however it is not flagged as a write
@@ -1091,7 +1091,7 @@ struct redisCommand redisCommandTable[] = {
0,NULL,0,0,0,0,0,0},
{"acl",aclCommand,-2,
- "admin no-script no-slowlog ok-loading ok-stale",
+ "admin no-script ok-loading ok-stale",
0,NULL,0,0,0,0,0,0},
{"stralgo",stralgoCommand,-2,
@@ -1161,12 +1161,10 @@ void serverLogRaw(int level, const char *msg) {
/* Like serverLogRaw() but with printf-alike support. This is the function that
* is used across the code. The raw version is only used in order to dump
* the INFO output on crash. */
-void serverLog(int level, const char *fmt, ...) {
+void _serverLog(int level, const char *fmt, ...) {
va_list ap;
char msg[LOG_MAX_LEN];
- if ((level&0xff) < server.verbosity) return;
-
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
@@ -1332,21 +1330,14 @@ uint64_t dictEncObjHash(const void *key) {
if (sdsEncodedObject(o)) {
return dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
- } else {
- if (o->encoding == OBJ_ENCODING_INT) {
- char buf[32];
- int len;
+ } else if (o->encoding == OBJ_ENCODING_INT) {
+ char buf[32];
+ int len;
- len = ll2string(buf,32,(long)o->ptr);
- return dictGenHashFunction((unsigned char*)buf, len);
- } else {
- uint64_t hash;
-
- o = getDecodedObject(o);
- hash = dictGenHashFunction(o->ptr, sdslen((sds)o->ptr));
- decrRefCount(o);
- return hash;
- }
+ len = ll2string(buf,32,(long)o->ptr);
+ return dictGenHashFunction((unsigned char*)buf, len);
+ } else {
+ serverPanic("Unknown string encoding");
}
}
@@ -1621,6 +1612,7 @@ void resetChildState() {
server.child_type = CHILD_TYPE_NONE;
server.child_pid = -1;
server.stat_current_cow_bytes = 0;
+ server.stat_current_cow_updated = 0;
server.stat_current_save_keys_processed = 0;
server.stat_module_progress = 0;
server.stat_current_save_keys_total = 0;
@@ -1729,33 +1721,17 @@ int clientsCronResizeQueryBuffer(client *c) {
* When we want to know what was recently the peak memory usage, we just scan
* such few slots searching for the maximum value. */
#define CLIENTS_PEAK_MEM_USAGE_SLOTS 8
-size_t ClientsPeakMemInput[CLIENTS_PEAK_MEM_USAGE_SLOTS];
-size_t ClientsPeakMemOutput[CLIENTS_PEAK_MEM_USAGE_SLOTS];
+size_t ClientsPeakMemInput[CLIENTS_PEAK_MEM_USAGE_SLOTS] = {0};
+size_t ClientsPeakMemOutput[CLIENTS_PEAK_MEM_USAGE_SLOTS] = {0};
-int clientsCronTrackExpansiveClients(client *c) {
+int clientsCronTrackExpansiveClients(client *c, int time_idx) {
size_t in_usage = sdsZmallocSize(c->querybuf) + c->argv_len_sum +
(c->argv ? zmalloc_size(c->argv) : 0);
size_t out_usage = getClientOutputBufferMemoryUsage(c);
- int i = server.unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS;
- int zeroidx = (i+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS;
-
- /* Always zero the next sample, so that when we switch to that second, we'll
- * only register samples that are greater in that second without considering
- * the history of such slot.
- *
- * Note: our index may jump to any random position if serverCron() is not
- * called for some reason with the normal frequency, for instance because
- * some slow command is called taking multiple seconds to execute. In that
- * case our array may end containing data which is potentially older
- * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem
- * since here we want just to track if "recently" there were very expansive
- * clients from the POV of memory usage. */
- ClientsPeakMemInput[zeroidx] = 0;
- ClientsPeakMemOutput[zeroidx] = 0;
/* Track the biggest values observed so far in this slot. */
- if (in_usage > ClientsPeakMemInput[i]) ClientsPeakMemInput[i] = in_usage;
- if (out_usage > ClientsPeakMemOutput[i]) ClientsPeakMemOutput[i] = out_usage;
+ if (in_usage > ClientsPeakMemInput[time_idx]) ClientsPeakMemInput[time_idx] = in_usage;
+ if (out_usage > ClientsPeakMemOutput[time_idx]) ClientsPeakMemOutput[time_idx] = out_usage;
return 0; /* This function never terminates the client. */
}
@@ -1828,6 +1804,24 @@ void clientsCron(void) {
iterations = (numclients < CLIENTS_CRON_MIN_ITERATIONS) ?
numclients : CLIENTS_CRON_MIN_ITERATIONS;
+
+ int curr_peak_mem_usage_slot = server.unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS;
+ /* Always zero the next sample, so that when we switch to that second, we'll
+ * only register samples that are greater in that second without considering
+ * the history of such slot.
+ *
+ * Note: our index may jump to any random position if serverCron() is not
+ * called for some reason with the normal frequency, for instance because
+ * some slow command is called taking multiple seconds to execute. In that
+ * case our array may end containing data which is potentially older
+ * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem
+ * since here we want just to track if "recently" there were very expansive
+ * clients from the POV of memory usage. */
+ int zeroidx = (curr_peak_mem_usage_slot+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS;
+ ClientsPeakMemInput[zeroidx] = 0;
+ ClientsPeakMemOutput[zeroidx] = 0;
+
+
while(listLength(server.clients) && iterations--) {
client *c;
listNode *head;
@@ -1843,7 +1837,7 @@ void clientsCron(void) {
* terminated. */
if (clientsCronHandleTimeout(c,now)) continue;
if (clientsCronResizeQueryBuffer(c)) continue;
- if (clientsCronTrackExpansiveClients(c)) continue;
+ if (clientsCronTrackExpansiveClients(c, curr_peak_mem_usage_slot)) continue;
if (clientsCronTrackClientsMemUsage(c)) continue;
}
}
@@ -1934,11 +1928,11 @@ void updateCachedTime(int update_daylight_info) {
}
void checkChildrenDone(void) {
- int statloc;
+ int statloc = 0;
pid_t pid;
- if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
- int exitcode = WEXITSTATUS(statloc);
+ if ((pid = waitpid(-1, &statloc, WNOHANG)) != 0) {
+ int exitcode = WIFEXITED(statloc) ? WEXITSTATUS(statloc) : -1;
int bysignal = 0;
if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);
@@ -1946,15 +1940,14 @@ void checkChildrenDone(void) {
/* sigKillChildHandler catches the signal and calls exit(), but we
* must make sure not to flag lastbgsave_status, etc incorrectly.
* We could directly terminate the child process via SIGUSR1
- * without handling it, but in this case Valgrind will log an
- * annoying error. */
+ * without handling it */
if (exitcode == SERVER_CHILD_NOERROR_RETVAL) {
bysignal = SIGUSR1;
exitcode = 1;
}
if (pid == -1) {
- serverLog(LL_WARNING,"wait3() returned an error: %s. "
+ serverLog(LL_WARNING,"waitpid() returned an error: %s. "
"child_type: %s, child_pid = %d",
strerror(errno),
strChildType(server.child_type),
@@ -2662,6 +2655,7 @@ void initServerConfig(void) {
server.aof_rewrite_scheduled = 0;
server.aof_flush_sleep = 0;
server.aof_last_fsync = time(NULL);
+ atomicSet(server.aof_bio_fsync_status,C_OK);
server.aof_rewrite_time_last = -1;
server.aof_rewrite_time_start = -1;
server.aof_lastbgrewrite_status = C_OK;
@@ -3058,14 +3052,15 @@ int listenToPort(int port, socketFds *sfd) {
sfd->fd[sfd->count] = anetTcpServer(server.neterr,port,addr,server.tcp_backlog);
}
if (sfd->fd[sfd->count] == ANET_ERR) {
+ int net_errno = errno;
serverLog(LL_WARNING,
- "Could not create server TCP listening socket %s:%d: %s",
+ "Warning: Could not create server TCP listening socket %s:%d: %s",
addr, port, server.neterr);
- if (errno == EADDRNOTAVAIL && optional)
+ if (net_errno == EADDRNOTAVAIL && optional)
continue;
- if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
- errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
- errno == EAFNOSUPPORT)
+ if (net_errno == ENOPROTOOPT || net_errno == EPROTONOSUPPORT ||
+ net_errno == ESOCKTNOSUPPORT || net_errno == EPFNOSUPPORT ||
+ net_errno == EAFNOSUPPORT)
continue;
/* Rollback successful listens before exiting */
@@ -3163,6 +3158,7 @@ void initServer(void) {
server.clients_pending_write = listCreate();
server.clients_pending_read = listCreate();
server.clients_timeout_table = raxNew();
+ server.replication_allowed = 1;
server.slaveseldb = -1; /* Force to emit the first SELECT command. */
server.unblocked_clients = listCreate();
server.ready_keys = listCreate();
@@ -3196,11 +3192,15 @@ void initServer(void) {
/* Open the TCP listening socket for the user commands. */
if (server.port != 0 &&
- listenToPort(server.port,&server.ipfd) == C_ERR)
+ listenToPort(server.port,&server.ipfd) == C_ERR) {
+ serverLog(LL_WARNING, "Failed listening on port %u (TCP), aborting.", server.port);
exit(1);
+ }
if (server.tls_port != 0 &&
- listenToPort(server.tls_port,&server.tlsfd) == C_ERR)
+ listenToPort(server.tls_port,&server.tlsfd) == C_ERR) {
+ serverLog(LL_WARNING, "Failed listening on port %u (TLS), aborting.", server.tls_port);
exit(1);
+ }
/* Open the listening Unix domain socket. */
if (server.unixsocket != NULL) {
@@ -3266,6 +3266,7 @@ void initServer(void) {
server.stat_starttime = time(NULL);
server.stat_peak_memory = 0;
server.stat_current_cow_bytes = 0;
+ server.stat_current_cow_updated = 0;
server.stat_current_save_keys_processed = 0;
server.stat_current_save_keys_total = 0;
server.stat_rdb_cow_bytes = 0;
@@ -3507,6 +3508,7 @@ void redisOpArrayFree(redisOpArray *oa) {
zfree(op->argv);
}
zfree(oa->ops);
+ oa->ops = NULL;
}
/* ====================== Commands lookup and execution ===================== */
@@ -3557,6 +3559,9 @@ struct redisCommand *lookupCommandOrOriginal(sds name) {
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
int flags)
{
+ if (!server.replication_allowed)
+ return;
+
/* Propagate a MULTI request once we encounter the first command which
* is a write command.
* This way we'll deliver the MULTI/..../EXEC block as a whole and
@@ -3619,6 +3624,12 @@ void preventCommandPropagation(client *c) {
c->flags |= CLIENT_PREVENT_PROP;
}
+/* Avoid logging any information about this client's arguments
+ * since they contain sensitive information. */
+void preventCommandLogging(client *c) {
+ c->flags |= CLIENT_PREVENT_LOGGING;
+}
+
/* AOF specific version of preventCommandPropagation(). */
void preventCommandAOF(client *c) {
c->flags |= CLIENT_PREVENT_AOF_PROP;
@@ -3629,6 +3640,19 @@ void preventCommandReplication(client *c) {
c->flags |= CLIENT_PREVENT_REPL_PROP;
}
+/* Log the last command a client executed into the slowlog. */
+void slowlogPushCurrentCommand(client *c, struct redisCommand *cmd, ustime_t duration) {
+ /* Some commands may contain sensitive data that should not be available in the slowlog. */
+ if ((c->flags & CLIENT_PREVENT_LOGGING) || (cmd->flags & CMD_SKIP_SLOWLOG))
+ return;
+
+ /* If command argument vector was rewritten, use the original
+ * arguments. */
+ robj **argv = c->original_argv ? c->original_argv : c->argv;
+ int argc = c->original_argv ? c->original_argc : c->argc;
+ slowlogPushEntryIfNeeded(c,argv,argc,duration);
+}
+
/* Call() is the core of Redis execution of a command.
*
* The following flags can be passed:
@@ -3731,27 +3755,31 @@ void call(client *c, int flags) {
server.lua_caller->flags |= CLIENT_FORCE_AOF;
}
- /* Log the command into the Slow log if needed, and populate the
- * per-command statistics that we show in INFO commandstats. */
- if (flags & CMD_CALL_SLOWLOG && !(c->cmd->flags & CMD_SKIP_SLOWLOG)) {
- char *latency_event = (c->cmd->flags & CMD_FAST) ?
- "fast-command" : "command";
+ /* Note: the code below uses the real command that was executed
+ * c->cmd and c->lastcmd may be different, in case of MULTI-EXEC or
+ * re-written commands such as EXPIRE, GEOADD, etc. */
+
+ /* Record the latency this command induced on the main thread.
+ * unless instructed by the caller not to log. (happens when processing
+ * a MULTI-EXEC from inside an AOF). */
+ if (flags & CMD_CALL_SLOWLOG) {
+ char *latency_event = (real_cmd->flags & CMD_FAST) ?
+ "fast-command" : "command";
latencyAddSampleIfNeeded(latency_event,duration/1000);
- /* If command argument vector was rewritten, use the original
- * arguments. */
- robj **argv = c->original_argv ? c->original_argv : c->argv;
- int argc = c->original_argv ? c->original_argc : c->argc;
- /* If the client is blocked we will handle slowlog when it is unblocked . */
- if (!(c->flags & CLIENT_BLOCKED)) {
- slowlogPushEntryIfNeeded(c,argv,argc,duration);
- }
}
- freeClientOriginalArgv(c);
+ /* Log the command into the Slow log if needed.
+ * If the client is blocked we will handle slowlog when it is unblocked. */
+ if ((flags & CMD_CALL_SLOWLOG) && !(c->flags & CLIENT_BLOCKED))
+ slowlogPushCurrentCommand(c, real_cmd, duration);
+
+ /* Clear the original argv.
+ * If the client is blocked we will handle slowlog when it is unblocked. */
+ if (!(c->flags & CLIENT_BLOCKED))
+ freeClientOriginalArgv(c);
+
+ /* populate the per-command statistics that we show in INFO commandstats. */
if (flags & CMD_CALL_STATS) {
- /* use the real command that was executed (cmd and lastamc) may be
- * different, in case of MULTI-EXEC or re-written commands such as
- * EXPIRE, GEOADD, etc. */
real_cmd->microseconds += duration;
real_cmd->calls++;
}
@@ -3916,6 +3944,16 @@ static int cmdHasMovableKeys(struct redisCommand *cmd) {
* other operations can be performed by the caller. Otherwise
* if C_ERR is returned the client was destroyed (i.e. after QUIT). */
int processCommand(client *c) {
+ if (!server.lua_timedout) {
+ /* Both EXEC and EVAL call call() directly so there should be
+ * no way in_exec or in_eval or propagate_in_transaction is 1.
+ * That is unless lua_timedout, in which case client may run
+ * some commands. */
+ serverAssert(!server.propagate_in_transaction);
+ serverAssert(!server.in_exec);
+ serverAssert(!server.in_eval);
+ }
+
moduleCallCommandFilters(c);
/* The QUIT command is handled separately. Normal command procs will
@@ -3974,18 +4012,30 @@ int processCommand(client *c) {
/* Check if the user can run this command according to the current
* ACLs. */
- int acl_keypos;
- int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);
+ int acl_errpos;
+ int acl_retval = ACLCheckAllPerm(c,&acl_errpos);
if (acl_retval != ACL_OK) {
- addACLLogEntry(c,acl_retval,acl_keypos,NULL);
- if (acl_retval == ACL_DENIED_CMD)
+ addACLLogEntry(c,acl_retval,acl_errpos,NULL);
+ switch (acl_retval) {
+ case ACL_DENIED_CMD:
rejectCommandFormat(c,
"-NOPERM this user has no permissions to run "
"the '%s' command or its subcommand", c->cmd->name);
- else
+ break;
+ case ACL_DENIED_KEY:
rejectCommandFormat(c,
"-NOPERM this user has no permissions to access "
"one of the keys used as arguments");
+ break;
+ case ACL_DENIED_CHANNEL:
+ rejectCommandFormat(c,
+ "-NOPERM this user has no permissions to access "
+ "one of the channels used as arguments");
+ break;
+ default:
+ rejectCommandFormat(c, "no permission");
+ break;
+ }
return C_OK;
}
@@ -4144,6 +4194,7 @@ int processCommand(client *c) {
c->cmd->proc != discardCommand &&
c->cmd->proc != watchCommand &&
c->cmd->proc != unwatchCommand &&
+ c->cmd->proc != resetCommand &&
!(c->cmd->proc == shutdownCommand &&
c->argc == 2 &&
tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
@@ -4272,7 +4323,10 @@ int prepareForShutdown(int flags) {
/* Append only file: flush buffers and fsync() the AOF at exit */
serverLog(LL_NOTICE,"Calling fsync() on the AOF file.");
flushAppendOnlyFile(1);
- redis_fsync(server.aof_fd);
+ if (redis_fsync(server.aof_fd) == -1) {
+ serverLog(LL_WARNING,"Fail to fsync the AOF file: %s.",
+ strerror(errno));
+ }
}
/* Create a new RDB file before exiting. */
@@ -4335,13 +4389,20 @@ int writeCommandsDeniedByDiskError(void) {
server.lastbgsave_status == C_ERR)
{
return DISK_ERROR_TYPE_RDB;
- } else if (server.aof_state != AOF_OFF &&
- server.aof_last_write_status == C_ERR)
- {
- return DISK_ERROR_TYPE_AOF;
- } else {
- return DISK_ERROR_TYPE_NONE;
+ } else if (server.aof_state != AOF_OFF) {
+ if (server.aof_last_write_status == C_ERR) {
+ return DISK_ERROR_TYPE_AOF;
+ }
+ /* AOF fsync error. */
+ int aof_bio_fsync_status;
+ atomicGet(server.aof_bio_fsync_status,aof_bio_fsync_status);
+ if (aof_bio_fsync_status == C_ERR) {
+ atomicGet(server.aof_bio_fsync_errno,server.aof_last_write_errno);
+ return DISK_ERROR_TYPE_AOF;
+ }
}
+
+ return DISK_ERROR_TYPE_NONE;
}
/* The PING command. It works in a different way if the client is in
@@ -4801,12 +4862,15 @@ sds genRedisInfoString(const char *section) {
} else if (server.stat_current_save_keys_total) {
fork_perc = ((double)server.stat_current_save_keys_processed / server.stat_current_save_keys_total) * 100;
}
-
+ int aof_bio_fsync_status;
+ atomicGet(server.aof_bio_fsync_status,aof_bio_fsync_status);
+
info = sdscatprintf(info,
"# Persistence\r\n"
"loading:%d\r\n"
"current_cow_size:%zu\r\n"
- "current_fork_perc:%.2f%%\r\n"
+ "current_cow_size_age:%lu\r\n"
+ "current_fork_perc:%.2f\r\n"
"current_save_keys_processed:%zu\r\n"
"current_save_keys_total:%zu\r\n"
"rdb_changes_since_last_save:%lld\r\n"
@@ -4828,6 +4892,7 @@ sds genRedisInfoString(const char *section) {
"module_fork_last_cow_size:%zu\r\n",
(int)server.loading,
server.stat_current_cow_bytes,
+ server.stat_current_cow_updated ? (unsigned long) elapsedMs(server.stat_current_cow_updated) / 1000 : 0,
fork_perc,
server.stat_current_save_keys_processed,
server.stat_current_save_keys_total,
@@ -4846,7 +4911,8 @@ sds genRedisInfoString(const char *section) {
(intmax_t)((server.child_type != CHILD_TYPE_AOF) ?
-1 : time(NULL)-server.aof_rewrite_time_start),
(server.aof_lastbgrewrite_status == C_OK) ? "ok" : "err",
- (server.aof_last_write_status == C_OK) ? "ok" : "err",
+ (server.aof_last_write_status == C_OK &&
+ aof_bio_fsync_status == C_OK) ? "ok" : "err",
server.stat_aof_cow_bytes,
server.child_type == CHILD_TYPE_MODULE,
server.stat_module_cow_bytes);
@@ -5054,13 +5120,16 @@ sds genRedisInfoString(const char *section) {
if (server.repl_state != REPL_STATE_CONNECTED) {
info = sdscatprintf(info,
"master_link_down_since_seconds:%jd\r\n",
- (intmax_t)(server.unixtime-server.repl_down_since));
+ server.repl_down_since ?
+ (intmax_t)(server.unixtime-server.repl_down_since) : -1);
}
info = sdscatprintf(info,
"slave_priority:%d\r\n"
- "slave_read_only:%d\r\n",
+ "slave_read_only:%d\r\n"
+ "replica_announced:%d\r\n",
server.slave_priority,
- server.repl_slave_ro);
+ server.repl_slave_ro,
+ server.replica_announced);
}
info = sdscatprintf(info,
@@ -5800,6 +5869,7 @@ int redisFork(int purpose) {
server.child_pid = childpid;
server.child_type = purpose;
server.stat_current_cow_bytes = 0;
+ server.stat_current_cow_updated = 0;
server.stat_current_save_keys_processed = 0;
server.stat_module_progress = 0;
server.stat_current_save_keys_total = dbTotalServerKeyCount();
@@ -6035,36 +6105,78 @@ int iAmMaster(void) {
(server.cluster_enabled && nodeIsMaster(server.cluster->myself)));
}
+#ifdef REDIS_TEST
+typedef int redisTestProc(int argc, char **argv, int accurate);
+struct redisTest {
+ char *name;
+ redisTestProc *proc;
+ int failed;
+} redisTests[] = {
+ {"ziplist", ziplistTest},
+ {"quicklist", quicklistTest},
+ {"intset", intsetTest},
+ {"zipmap", zipmapTest},
+ {"sha1test", sha1Test},
+ {"util", utilTest},
+ {"endianconv", endianconvTest},
+ {"crc64", crc64Test},
+ {"zmalloc", zmalloc_test},
+ {"sds", sdsTest},
+ {"dict", dictTest}
+};
+redisTestProc *getTestProcByName(const char *name) {
+ int numtests = sizeof(redisTests)/sizeof(struct redisTest);
+ for (int j = 0; j < numtests; j++) {
+ if (!strcasecmp(name,redisTests[j].name)) {
+ return redisTests[j].proc;
+ }
+ }
+ return NULL;
+}
+#endif
+
int main(int argc, char **argv) {
struct timeval tv;
int j;
char config_from_stdin = 0;
#ifdef REDIS_TEST
- if (argc == 3 && !strcasecmp(argv[1], "test")) {
- if (!strcasecmp(argv[2], "ziplist")) {
- return ziplistTest(argc, argv);
- } else if (!strcasecmp(argv[2], "quicklist")) {
- quicklistTest(argc, argv);
- } else if (!strcasecmp(argv[2], "intset")) {
- return intsetTest(argc, argv);
- } else if (!strcasecmp(argv[2], "zipmap")) {
- return zipmapTest(argc, argv);
- } else if (!strcasecmp(argv[2], "sha1test")) {
- return sha1Test(argc, argv);
- } else if (!strcasecmp(argv[2], "util")) {
- return utilTest(argc, argv);
- } else if (!strcasecmp(argv[2], "endianconv")) {
- return endianconvTest(argc, argv);
- } else if (!strcasecmp(argv[2], "crc64")) {
- return crc64Test(argc, argv);
- } else if (!strcasecmp(argv[2], "zmalloc")) {
- return zmalloc_test(argc, argv);
- } else if (!strcasecmp(argv[2], "sds")) {
- return sdsTest(argc, argv);
+ if (argc >= 3 && !strcasecmp(argv[1], "test")) {
+ int accurate = 0;
+ for (j = 3; j < argc; j++) {
+ if (!strcasecmp(argv[j], "--accurate")) {
+ accurate = 1;
+ }
}
- return -1; /* test not found */
+ if (!strcasecmp(argv[2], "all")) {
+ int numtests = sizeof(redisTests)/sizeof(struct redisTest);
+ for (j = 0; j < numtests; j++) {
+ redisTests[j].failed = (redisTests[j].proc(argc,argv,accurate) != 0);
+ }
+
+ /* Report tests result */
+ int failed_num = 0;
+ for (j = 0; j < numtests; j++) {
+ if (redisTests[j].failed) {
+ failed_num++;
+ printf("[failed] Test - %s\n", redisTests[j].name);
+ } else {
+ printf("[ok] Test - %s\n", redisTests[j].name);
+ }
+ }
+
+ printf("%d tests, %d passed, %d failed\n", numtests,
+ numtests-failed_num, failed_num);
+
+ return failed_num == 0 ? 0 : 1;
+ } else {
+ redisTestProc *proc = getTestProcByName(argv[2]);
+ if (!proc) return -1; /* test not found */
+ return proc(argc,argv,accurate);
+ }
+
+ return 0;
}
#endif
@@ -6150,7 +6262,6 @@ int main(int argc, char **argv) {
server.exec_argv[1] = zstrdup(server.configfile);
j = 2; // Skip this arg when parsing options
}
-
while(j < argc) {
/* Either first or last argument - Should we read config from stdin? */
if (argv[j][0] == '-' && argv[j][1] == '\0' && (j == 1 || j == argc-1)) {
@@ -6173,16 +6284,11 @@ int main(int argc, char **argv) {
j++;
}
- if (server.sentinel_mode && ! server.configfile) {
- serverLog(LL_WARNING,
- "Sentinel needs config file on disk to save state. Exiting...");
- exit(1);
- }
loadServerConfig(server.configfile, config_from_stdin, options);
if (server.sentinel_mode) loadSentinelConfigFromQueue();
sdsfree(options);
}
-
+ if (server.sentinel_mode) sentinelCheckConfigFile();
server.supervised = redisIsSupervised(server.supervised_mode);
int background = server.daemonize && !server.supervised;
if (background) daemonize();
@@ -6197,7 +6303,7 @@ int main(int argc, char **argv) {
(int)getpid());
if (argc == 1) {
- serverLog(LL_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
+ serverLog(LL_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/redis.conf", argv[0]);
} else {
serverLog(LL_WARNING, "Configuration loaded");
}
@@ -6251,10 +6357,10 @@ int main(int argc, char **argv) {
if (server.supervised_mode == SUPERVISED_SYSTEMD) {
if (!server.masterhost) {
redisCommunicateSystemd("STATUS=Ready to accept connections\n");
- redisCommunicateSystemd("READY=1\n");
} else {
- redisCommunicateSystemd("STATUS=Waiting for MASTER <-> REPLICA sync\n");
+ redisCommunicateSystemd("STATUS=Ready to accept connections in read-only mode. Waiting for MASTER <-> REPLICA sync\n");
}
+ redisCommunicateSystemd("READY=1\n");
}
} else {
ACLLoadUsersAtStartup();