diff --git a/src/server.c b/src/server.c index 267e585ec1bf5cc2c9b579ad5c1646b739ac1ef0..96cd28d4fbb5d9c7439f1e49b52c97b47ef3ec9a 100644 --- a/src/server.c +++ b/src/server.c @@ -1436,6 +1436,7 @@ void initServerConfig(void) { getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE); server.configfile = NULL; + server.executable = NULL; server.hz = CONFIG_DEFAULT_HZ; server.runid[CONFIG_RUN_ID_SIZE] = '\0'; server.arch_bits = (sizeof(long) == 8) ? 64 : 32; @@ -1595,6 +1596,50 @@ void initServerConfig(void) { server.watchdog_period = 0; } +extern char **environ; + +/* Restart the server, executing the same executable that started this + * instance, with the same arguments and configuration file. + * + * The list of flags, that may be bitwise ORed together, alter the + * behavior of this function: + * + * RESTART_SERVER_NONE No flags. + * RESTART_SERVER_GRACEFULLY Do a proper shutdown before restarting. + * RESTART_SERVER_CONFIG_REWRITE Rewrite the config file before restarting. + * + * On success the function does not return, because the process turns into + * a different process. On error C_ERR is returned. */ +int restartServer(int flags, mstime_t delay) { + int j; + + /* Check if we still have accesses to the executable that started this + * server instance. */ + if (access(server.executable,X_OK) == -1) return C_ERR; + + /* Config rewriting. */ + if (flags & RESTART_SERVER_CONFIG_REWRITE && + server.configfile && + rewriteConfig(server.configfile) == -1) return C_ERR; + + /* Perform a proper shutdown. */ + if (flags & RESTART_SERVER_GRACEFULLY && + prepareForShutdown(SHUTDOWN_NOFLAGS) != C_OK) return C_ERR; + + /* Close all file descriptors, with the exception of stdin, stdout, strerr + * which are useful if we restart a Redis server which is not daemonized. */ + for (j = 3; j < (int)server.maxclients + 1024; j++) close(j); + + /* Execute the server with the original command line. */ + if (delay) usleep(delay*1000); + execve(server.executable,server.exec_argv,environ); + + /* If an error occurred here, there is nothing we can do, but exit. */ + _exit(1); + + return C_ERR; /* Never reached. */ +} + /* This function will try to raise the max number of open files accordingly to * the configured max number of clients. It also reserves a number of file * descriptors (CONFIG_MIN_RESERVED_FDS) for extra operations of @@ -2730,6 +2775,7 @@ sds genRedisInfoString(char *section) { "uptime_in_days:%jd\r\n" "hz:%d\r\n" "lru_clock:%ld\r\n" + "executable:%s\r\n" "config_file:%s\r\n", REDIS_VERSION, redisGitSHA1(), @@ -2751,6 +2797,7 @@ sds genRedisInfoString(char *section) { (intmax_t)(uptime/(3600*24)), server.hz, (unsigned long) server.lruclock, + server.executable ? server.executable : "", server.configfile ? server.configfile : ""); } @@ -3793,6 +3840,7 @@ int redisIsSupervised(int mode) { int main(int argc, char **argv) { struct timeval tv; + int j; #ifdef REDIS_TEST if (argc == 3 && !strcasecmp(argv[1], "test")) { @@ -3833,6 +3881,13 @@ int main(int argc, char **argv) { server.sentinel_mode = checkForSentinelMode(argc,argv); initServerConfig(); + /* Store the executable path and arguments in a safe place in order + * to be able to restart the server later. */ + server.executable = getAbsolutePath(argv[0]); + server.exec_argv = zmalloc(sizeof(char*)*(argc+1)); + server.exec_argv[argc] = NULL; + for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]); + /* We need to init sentinel right now as parsing the configuration file * in sentinel mode will have the effect of populating the sentinel * data structures with master nodes to monitor. */ @@ -3848,7 +3903,7 @@ int main(int argc, char **argv) { exit(redis_check_rdb_main(argv,argc)); if (argc >= 2) { - int j = 1; /* First option to parse in argv[] */ + j = 1; /* First option to parse in argv[] */ sds options = sdsempty(); char *configfile = NULL; @@ -3869,8 +3924,16 @@ int main(int argc, char **argv) { } /* First argument is the config file name? */ - if (argv[j][0] != '-' || argv[j][1] != '-') - configfile = argv[j++]; + if (argv[j][0] != '-' || argv[j][1] != '-') { + configfile = argv[j]; + server.configfile = getAbsolutePath(configfile); + /* Replace the config file in server.exec_argv with + * its absoulte path. */ + zfree(server.exec_argv[j]); + server.exec_argv[j] = zstrdup(server.configfile); + j++; + } + /* All the other options are parsed and conceptually appended to the * configuration file. For instance --port 6380 will generate the * string "port 6380\n" to be parsed after the actual file name @@ -3900,7 +3963,6 @@ int main(int argc, char **argv) { "Sentinel needs config file on disk to save state. Exiting..."); exit(1); } - if (configfile) server.configfile = getAbsolutePath(configfile); resetServerSaveParams(); loadServerConfig(configfile,options); sdsfree(options); diff --git a/src/server.h b/src/server.h index 9f3e9b0d2570079f18b90aa601f3792023157043..6d9e70028c699c1d7fd9ce3e6ae2cf66713c38a4 100644 --- a/src/server.h +++ b/src/server.h @@ -694,6 +694,8 @@ struct redisServer { /* General */ pid_t pid; /* Main process pid. */ char *configfile; /* Absolute config file path, or NULL */ + char *executable; /* Absolute executable file path. */ + char **exec_argv; /* Executable argv vector (copy). */ int hz; /* serverCron() calls frequency in hertz */ redisDb *db; dict *commands; /* Command table */ @@ -1321,6 +1323,11 @@ void resetServerStats(void); unsigned int getLRUClock(void); const char *maxmemoryToString(void); +#define RESTART_SERVER_NONE 0 +#define RESTART_SERVER_GRACEFULLY (1<<0) /* Do proper shutdown. */ +#define RESTART_SERVER_CONFIG_REWRITE (1<<1) /* CONFIG REWRITE before restart.*/ +int restartServer(int flags, mstime_t delay); + /* Set data type */ robj *setTypeCreate(sds value); int setTypeAdd(robj *subject, sds value);