From ac3100bc3bd6ec50d44c0ae11d22eab4e9bb4152 Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 26 Feb 2013 11:52:12 +0100 Subject: [PATCH] Set process name in ps output to make operations safer. This commit allows Redis to set a process name that includes the binding address and the port number in order to make operations simpler. Redis children processes doing AOF rewrites or RDB saving change the name into redis-aof-rewrite and redis-rdb-bgsave respectively. This in general makes harder to kill the wrong process because of an error and makes simpler to identify saving children. This feature was suggested by Arnaud GRANAL in the Redis Google Group, Arnaud also pointed me to the setproctitle.c implementation includeed in this commit. This feature should work on all the Linux, OSX, and all the three major BSD systems. --- src/Makefile | 2 +- src/Makefile.dep | 1 + src/aof.c | 1 + src/config.h | 12 +++ src/rdb.c | 1 + src/redis.c | 8 ++ src/redis.h | 1 + src/setproctitle.c | 259 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/setproctitle.c diff --git a/src/Makefile b/src/Makefile index 0aa409de..9202fb4b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -99,7 +99,7 @@ endif REDIS_SERVER_NAME= redis-server REDIS_SENTINEL_NAME= redis-sentinel -REDIS_SERVER_OBJ= adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o migrate.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o +REDIS_SERVER_OBJ= adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o migrate.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o REDIS_CLI_NAME= redis-cli REDIS_CLI_OBJ= anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o REDIS_BENCHMARK_NAME= redis-benchmark diff --git a/src/Makefile.dep b/src/Makefile.dep index 35025070..784b272a 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -82,6 +82,7 @@ sentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \ ziplist.h intset.h version.h util.h rdb.h rio.h \ ../deps/hiredis/hiredis.h ../deps/hiredis/async.h \ ../deps/hiredis/hiredis.h +setproctitle.o: setproctitle.c sha1.o: sha1.c sha1.h config.h slowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \ ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \ diff --git a/src/aof.c b/src/aof.c index 2265222c..c4526501 100644 --- a/src/aof.c +++ b/src/aof.c @@ -961,6 +961,7 @@ int rewriteAppendOnlyFileBackground(void) { /* Child */ if (server.ipfd > 0) close(server.ipfd); if (server.sofd > 0) close(server.sofd); + redisSetProcTitle("redis-aof-rewrite"); snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid()); if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) { size_t private_dirty = zmalloc_get_private_dirty(); diff --git a/src/config.h b/src/config.h index 68e99b42..961503c6 100644 --- a/src/config.h +++ b/src/config.h @@ -105,6 +105,18 @@ #define rdb_fsync_range(fd,off,size) fsync(fd) #endif +/* Check if we can use setproctitle(). + * BSD systems have support for it, we provide an implementation for + * Linux and osx. */ +#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) +#define USE_SETPROCTITLE +#endif + +#if (defined __linux || defined __APPLE__) +#define USE_SETPROCTITLE +void setproctitle(const char *fmt, ...); +#endif + /* Byte ordering detection */ #include /* This will likely define BYTE_ORDER */ diff --git a/src/rdb.c b/src/rdb.c index a5e4c47a..e327cd8c 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -730,6 +730,7 @@ int rdbSaveBackground(char *filename) { /* Child */ if (server.ipfd > 0) close(server.ipfd); if (server.sofd > 0) close(server.sofd); + redisSetProcTitle("redis-rdb-bgsave"); retval = rdbSave(filename); if (retval == REDIS_OK) { size_t private_dirty = zmalloc_get_private_dirty(); diff --git a/src/redis.c b/src/redis.c index 4575e271..4f12ecd7 100644 --- a/src/redis.c +++ b/src/redis.c @@ -2585,6 +2585,13 @@ void redisOutOfMemoryHandler(size_t allocation_size) { redisPanic("OOM"); } +void redisSetProcTitle(char *title) { + setproctitle("%s %s:%d", + title, + server.bindaddr ? server.bindaddr : "*", + server.port); +} + int main(int argc, char **argv) { struct timeval tv; @@ -2655,6 +2662,7 @@ int main(int argc, char **argv) { if (server.daemonize) daemonize(); initServer(); if (server.daemonize) createPidFile(); + redisSetProcTitle(argv[0]); redisAsciiArt(); if (!server.sentinel_mode) { diff --git a/src/redis.h b/src/redis.h index 9a5e05a8..d2a6a4fb 100644 --- a/src/redis.h +++ b/src/redis.h @@ -815,6 +815,7 @@ long long mstime(void); void getRandomHexChars(char *p, unsigned int len); uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); void exitFromChild(int retcode); +void redisSetProcTitle(char *title); /* networking.c -- Networking and Client related operations */ redisClient *createClient(int fd); diff --git a/src/setproctitle.c b/src/setproctitle.c new file mode 100644 index 00000000..399205c0 --- /dev/null +++ b/src/setproctitle.c @@ -0,0 +1,259 @@ +/* ========================================================================== + * setproctitle.c - Linux/Darwin setproctitle. + * -------------------------------------------------------------------------- + * Copyright (C) 2010 William Ahern + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * ========================================================================== + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include /* NULL size_t */ +#include /* va_list va_start va_end */ +#include /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */ +#include /* vsnprintf(3) snprintf(3) */ + +#include /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */ + +#include /* errno program_invocation_name program_invocation_short_name */ + +#if !defined(HAVE_SETPROCTITLE) +#define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) +#endif + + +#if !HAVE_SETPROCTITLE +#if (defined __linux || defined __APPLE__) && defined __GNUC__ + + +static struct { + /* original value */ + const char *arg0; + + /* title space available */ + char *base, *end; + + /* pointer to original nul character within base */ + char *nul; + + _Bool reset; + int error; +} SPT; + + +#ifndef SPT_MIN +#define SPT_MIN(a, b) (((a) < (b))? (a) : (b)) +#endif + +static inline size_t spt_min(size_t a, size_t b) { + return SPT_MIN(a, b); +} /* spt_min() */ + + +/* + * For discussion on the portability of the various methods, see + * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html + */ +static int spt_clearenv(void) { +#if __GLIBC__ + clearenv(); + + return 0; +#else + extern char **environ; + char **tmp; + + if (!(tmp = malloc(sizeof *tmp))) + return errno; + + tmp[0] = NULL; + environ = tmp; + + return 0; +#endif +} /* spt_clearenv() */ + + +static int spt_copyenv(char *oldenv[]) { + extern char **environ; + char *eq; + int i, error; + + if (environ != oldenv) + return 0; + + if ((error = spt_clearenv())) + goto error; + + for (i = 0; oldenv[i]; i++) { + if (!(eq = strchr(oldenv[i], '='))) + continue; + + *eq = '\0'; + error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0; + *eq = '='; + + if (error) + goto error; + } + + return 0; +error: + environ = oldenv; + + return error; +} /* spt_copyenv() */ + + +static int spt_copyargs(int argc, char *argv[]) { + char *tmp; + int i; + + for (i = 1; i < argc || (i >= argc && argv[i]); i++) { + if (!argv[i]) + continue; + + if (!(tmp = strdup(argv[i]))) + return errno; + + argv[i] = tmp; + } + + return 0; +} /* spt_copyargs() */ + + +void spt_init(int argc, char *argv[], char *envp[]) __attribute__((constructor)); + +void spt_init(int argc, char *argv[], char *envp[]) { + char *base, *end, *nul, *tmp; + int i, error; + + if (!(base = argv[0])) + return; + + nul = &base[strlen(base)]; + end = nul + 1; + + for (i = 0; i < argc || (i >= argc && argv[i]); i++) { + if (!argv[i] || argv[i] < end) + continue; + + end = argv[i] + strlen(argv[i]) + 1; + } + + for (i = 0; envp[i]; i++) { + if (envp[i] < end) + continue; + + end = envp[i] + strlen(envp[i]) + 1; + } + + if (!(SPT.arg0 = strdup(argv[0]))) + goto syerr; + +#if __GLIBC__ + if (!(tmp = strdup(program_invocation_name))) + goto syerr; + + program_invocation_name = tmp; + + if (!(tmp = strdup(program_invocation_short_name))) + goto syerr; + + program_invocation_short_name = tmp; +#elif __APPLE__ + if (!(tmp = strdup(getprogname()))) + goto syerr; + + setprogname(tmp); +#endif + + + if ((error = spt_copyenv(envp))) + goto error; + + if ((error = spt_copyargs(argc, argv))) + goto error; + + SPT.nul = nul; + SPT.base = base; + SPT.end = end; + + return; +syerr: + error = errno; +error: + SPT.error = error; +} /* spt_init() */ + + +#ifndef SPT_MAXTITLE +#define SPT_MAXTITLE 255 +#endif + +void setproctitle(const char *fmt, ...) { + char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */ + va_list ap; + char *nul; + int len, error; + + if (!SPT.base) + return; + + if (fmt) { + va_start(ap, fmt); + len = vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + } else { + len = snprintf(buf, sizeof buf, "%s", SPT.arg0); + } + + if (len <= 0) + { error = errno; goto error; } + + if (!SPT.reset) { + memset(SPT.base, 0, SPT.end - SPT.base); + SPT.reset = 1; + } else { + memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base)); + } + + len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); + memcpy(SPT.base, buf, len); + nul = &SPT.base[len]; + + if (nul < SPT.nul) { + *SPT.nul = '.'; + } else if (nul == SPT.nul && &nul[1] < SPT.end) { + *SPT.nul = ' '; + *++nul = '\0'; + } + + return; +error: + SPT.error = error; +} /* setproctitle() */ + + +#endif /* __linux || __APPLE__ */ +#endif /* !HAVE_SETPROCTITLE */ -- GitLab