From e6d499a446e1d34ceb9b393ddd79d38877aede67 Mon Sep 17 00:00:00 2001 From: rebx Date: Wed, 18 Apr 2012 11:04:37 +1200 Subject: [PATCH 1/4] Create PID file even if in foreground Previously, Redis only wrote the pid file if it was daemonizing, but many times it's useful to have the pid written out even if you're in the foreground. Some background for this is: I usually run redis via daemontools. That entails running redis-server on the foreground. Given that, I'd also want redis-server to create a pidfile so other processes (e.g. nagios) can run checks for that. Closes #463 --- src/redis.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/redis.c b/src/redis.c index a4d9e562..52e00b56 100644 --- a/src/redis.c +++ b/src/redis.c @@ -2374,7 +2374,7 @@ int prepareForShutdown(int flags) { return REDIS_ERR; } } - if (server.daemonize) { + if (server.daemonize || server.pidfile) { redisLog(REDIS_NOTICE,"Removing the pid file."); unlink(server.pidfile); } @@ -3759,9 +3759,10 @@ int main(int argc, char **argv) { } server.supervised = redisIsSupervised(); - if (server.daemonize && server.supervised == 0) daemonize(); + int background = server.daemonize && server.supervised == 0; + if (background) daemonize(); initServer(); - if (server.daemonize && server.supervised == 0) createPidFile(); + if (background || server.pidfile) createPidFile(); redisSetProcTitle(argv[0]); redisAsciiArt(); From 5e8b7e4f356c2908bb3389e2df09473e290acae8 Mon Sep 17 00:00:00 2001 From: Matt Stancliff Date: Mon, 25 Aug 2014 17:07:37 -0400 Subject: [PATCH 2/4] Define default pidfile when creating pid We want pidfile to be NULL on startup so we can detect if the user set an explicit value versus only using the default value. Closes #1967 Fixes #2076 --- src/redis.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/redis.c b/src/redis.c index 52e00b56..fd0d39bb 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1433,7 +1433,7 @@ void initServerConfig(void) { server.aof_flush_postponed_start = 0; server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC; server.aof_load_truncated = REDIS_DEFAULT_AOF_LOAD_TRUNCATED; - server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE); + server.pidfile = NULL; server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME); server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME); server.requirepass = NULL; @@ -3393,6 +3393,10 @@ void linuxMemoryWarnings(void) { #endif /* __linux__ */ void createPidFile(void) { + /* If pidfile requested, but no pidfile defined, use + * default pidfile path */ + if (!server.pidfile) server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE); + /* Try to write the pid file in a best-effort way. */ FILE *fp = fopen(server.pidfile,"w"); if (fp) { From 36a3b75355bd5aea9e34889918c5b78fb79586e4 Mon Sep 17 00:00:00 2001 From: Matt Stancliff Date: Thu, 8 Jan 2015 15:22:33 -0500 Subject: [PATCH 3/4] Supervise redis processes only if configured Adds configuration option 'supervised [no | upstart | systemd | auto]' Also removed 'bzero' from the previous implementation because it's 2015. (We could actually statically initialize those structs, but clang throws an invalid warning when we try, so it looks bad even though it isn't bad.) Fixes #2264 --- redis.conf | 11 ++++++++ src/config.c | 49 +++++++++++++++++++++++++++++++++++ src/redis.c | 73 ++++++++++++++++++++++++++++++++++++---------------- src/redis.h | 9 ++++++- 4 files changed, 119 insertions(+), 23 deletions(-) diff --git a/redis.conf b/redis.conf index b2ee853c..38e25869 100644 --- a/redis.conf +++ b/redis.conf @@ -36,6 +36,17 @@ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized. daemonize no +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous liveness pings back to your supervisor. +supervised no + # When running daemonized, Redis writes a pid file in /var/run/redis.pid by # default. You can specify a custom pid file location here. pidfile /var/run/redis.pid diff --git a/src/config.c b/src/config.c index b0245954..8255a56b 100644 --- a/src/config.c +++ b/src/config.c @@ -60,6 +60,8 @@ clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_TYPE_COUNT] = { * Config file parsing *----------------------------------------------------------------------------*/ +int supervisedToMode(const char *str); + int yesnotoi(char *s) { if (!strcasecmp(s,"yes")) return 1; else if (!strcasecmp(s,"no")) return 0; @@ -533,6 +535,15 @@ void loadServerConfigFromString(char *config) { goto loaderr; } server.notify_keyspace_events = flags; + } else if (!strcasecmp(argv[0],"supervised") && argc == 2) { + int mode = supervisedToMode(argv[1]); + + if (mode == -1) { + err = "Invalid option for 'supervised'. " + "Allowed values: 'upstart', 'systemd', 'auto', or 'no'"; + goto loaderr; + } + server.supervised_mode = mode; } else if (!strcasecmp(argv[0],"sentinel")) { /* argc == 1 is handled by main() as we need to enter the sentinel * mode ASAP. */ @@ -1022,6 +1033,33 @@ char *maxmemoryToString() { return s; } +int supervisedToMode(const char *str) { + int mode; + if (!strcasecmp(str,"upstart")) { + mode = REDIS_SUPERVISED_UPSTART; + } else if (!strcasecmp(str,"systemd")) { + mode = REDIS_SUPERVISED_SYSTEMD; + } else if (!strcasecmp(str,"auto")) { + mode = REDIS_SUPERVISED_AUTODETECT; + } else if (!strcasecmp(str,"no")) { + mode = REDIS_SUPERVISED_NONE; + } else { + mode = -1; + } + return mode; +} + +char *supervisedToString(void) { + char *s; + switch(server.supervised_mode) { + case REDIS_SUPERVISED_UPSTART: s = "upstart"; break; + case REDIS_SUPERVISED_SYSTEMD: s = "systemd"; break; + case REDIS_SUPERVISED_AUTODETECT: s = "auto"; break; + case REDIS_SUPERVISED_NONE: s = "no"; break; + default: s = "no"; break; + } + return s; +} void configGetCommand(redisClient *c) { robj *o = c->argv[2]; void *replylen = addDeferredMultiBulkLength(c); @@ -1177,6 +1215,11 @@ void configGetCommand(redisClient *c) { addReplyBulkCString(c,s); matches++; } + if (stringmatch(pattern,"supervised",0)) { + addReplyBulkCString(c,"supervised"); + addReplyBulkCString(c,supervisedToString()); + matches++; + } if (stringmatch(pattern,"client-output-buffer-limit",0)) { sds buf = sdsempty(); int j; @@ -1872,6 +1915,12 @@ int rewriteConfig(char *path) { rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ); rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC); rewriteConfigYesNoOption(state,"aof-load-truncated",server.aof_load_truncated,REDIS_DEFAULT_AOF_LOAD_TRUNCATED); + rewriteConfigEnumOption(state,"supervised",server.supervised_mode, + "upstart", REDIS_SUPERVISED_UPSTART, + "systemd", REDIS_SUPERVISED_SYSTEMD, + "auto", REDIS_SUPERVISED_AUTODETECT, + "no", REDIS_SUPERVISED_NONE, + NULL, REDIS_SUPERVISED_NONE); if (server.sentinel_mode) rewriteConfigSentinelOption(state); /* Step 3: remove all the orphaned lines in the old file, that is, lines diff --git a/src/redis.c b/src/redis.c index fd0d39bb..13df8d28 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1416,6 +1416,7 @@ void initServerConfig(void) { server.syslog_facility = LOG_LOCAL0; server.daemonize = REDIS_DEFAULT_DAEMONIZE; server.supervised = 0; + server.supervised_mode = REDIS_SUPERVISED_NONE; server.aof_state = REDIS_AOF_OFF; server.aof_fsync = REDIS_DEFAULT_AOF_FSYNC; server.aof_no_fsync_on_rewrite = REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE; @@ -3591,8 +3592,23 @@ void redisSetProcTitle(char *title) { /* * Check whether systemd or upstart have been used to start redis. */ -int redisIsSupervised(void) { + +int redisSupervisedUpstart(void) { const char *upstart_job = getenv("UPSTART_JOB"); + + if (!upstart_job) { + redisLog(REDIS_WARNING, + "upstart supervision requested, but UPSTART_JOB not found"); + return 0; + } + + redisLog(REDIS_NOTICE, "supervised by upstart, will stop to signal readyness"); + raise(SIGSTOP); + unsetenv("UPSTART_JOB"); + return 1; +} + +int redisSupervisedSystemd(void) { const char *notify_socket = getenv("NOTIFY_SOCKET"); int fd = 1; struct sockaddr_un su; @@ -3600,31 +3616,24 @@ int redisIsSupervised(void) { struct msghdr hdr; int sendto_flags = 0; - if (upstart_job == NULL && notify_socket == NULL) + if (!notify_socket) { + redisLog(REDIS_WARNING, + "systemd supervision requested, but NOTIFY_SOCKET not found"); return 0; - - if (upstart_job != NULL) { - redisLog(REDIS_NOTICE, "supervised by upstart, will stop to signal readyness"); - raise(SIGSTOP); - unsetenv("UPSTART_JOB"); - - return 1; } - /* - * If we got here, we're supervised by systemd. - */ - if ((strchr("@/", notify_socket[0])) == NULL || - strlen(notify_socket) < 2) + if ((strchr("@/", notify_socket[0])) == NULL || strlen(notify_socket) < 2) { return 0; + } redisLog(REDIS_NOTICE, "supervised by systemd, will signal readyness"); - if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { - redisLog(REDIS_WARNING, "cannot contact systemd socket %s", notify_socket); + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { + redisLog(REDIS_WARNING, + "Can't connect to systemd socket %s", notify_socket); return 0; } - bzero(&su, sizeof(su)); + memset(&su, 0, sizeof(su)); su.sun_family = AF_UNIX; strncpy (su.sun_path, notify_socket, sizeof(su.sun_path) -1); su.sun_path[sizeof(su.sun_path) - 1] = '\0'; @@ -3632,11 +3641,11 @@ int redisIsSupervised(void) { if (notify_socket[0] == '@') su.sun_path[0] = '\0'; - bzero(&iov, sizeof(iov)); + memset(&iov, 0, sizeof(iov)); iov.iov_base = "READY=1"; iov.iov_len = strlen("READY=1"); - bzero(&hdr, sizeof(hdr)); + memset(&hdr, 0, sizeof(hdr)); hdr.msg_name = &su; hdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(notify_socket); @@ -3648,7 +3657,7 @@ int redisIsSupervised(void) { sendto_flags |= MSG_NOSIGNAL; #endif if (sendmsg(fd, &hdr, sendto_flags) < 0) { - redisLog(REDIS_WARNING, "Cannot send notification to systemd"); + redisLog(REDIS_WARNING, "Can't send notification to systemd"); close(fd); return 0; } @@ -3656,6 +3665,26 @@ int redisIsSupervised(void) { return 1; } +int redisIsSupervised(int mode) { + if (mode == REDIS_SUPERVISED_AUTODETECT) { + const char *upstart_job = getenv("UPSTART_JOB"); + const char *notify_socket = getenv("NOTIFY_SOCKET"); + + if (upstart_job) { + redisSupervisedUpstart(); + } else if (notify_socket) { + redisSupervisedSystemd(); + } + } else if (mode == REDIS_SUPERVISED_UPSTART) { + return redisSupervisedUpstart(); + } else if (mode == REDIS_SUPERVISED_SYSTEMD) { + return redisSupervisedSystemd(); + } + + return 0; +} + + int main(int argc, char **argv) { struct timeval tv; @@ -3762,8 +3791,8 @@ int main(int argc, char **argv) { redisLog(REDIS_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"); } - server.supervised = redisIsSupervised(); - int background = server.daemonize && server.supervised == 0; + server.supervised = redisIsSupervised(server.supervised_mode); + int background = server.daemonize && !server.supervised; if (background) daemonize(); initServer(); if (background || server.pidfile) createPidFile(); diff --git a/src/redis.h b/src/redis.h index 54046ef2..89d97f55 100644 --- a/src/redis.h +++ b/src/redis.h @@ -313,6 +313,12 @@ typedef long long mstime_t; /* millisecond time type. */ #define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */ #define REDIS_DEFAULT_VERBOSITY REDIS_NOTICE +/* Supervision options */ +#define REDIS_SUPERVISED_NONE 0 +#define REDIS_SUPERVISED_AUTODETECT 1 +#define REDIS_SUPERVISED_SYSTEMD 2 +#define REDIS_SUPERVISED_UPSTART 3 + /* Anti-warning macro... */ #define REDIS_NOTUSED(V) ((void) V) @@ -740,7 +746,8 @@ struct redisServer { int active_expire_enabled; /* Can be disabled for testing purposes. */ size_t client_max_querybuf_len; /* Limit for client query buffer length */ int dbnum; /* Total number of configured DBs */ - int supervised; /* True if supervised by upstart or systemd */ + int supervised; /* 1 if supervised, 0 otherwise. */ + int supervised_mode; /* See REDIS_SUPERVISED_* */ int daemonize; /* True if running as a daemon */ clientBufferLimitsConfig client_obuf_limits[REDIS_CLIENT_TYPE_COUNT]; /* AOF persistence */ From eb7d67a3ab1e9a41e406f582676526194575f958 Mon Sep 17 00:00:00 2001 From: Matt Stancliff Date: Thu, 8 Jan 2015 16:23:48 -0500 Subject: [PATCH 4/4] Remove RDB AUX memory leaks --- src/rdb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rdb.c b/src/rdb.c index e33a5143..36ba151c 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -1324,8 +1324,8 @@ int rdbLoad(char *filename) { auxkey->ptr); } - zfree(auxkey); - zfree(auxval); + decrRefCount(auxkey); + decrRefCount(auxval); continue; /* Read type again. */ }