diff --git a/.gitignore b/.gitignore index 55c4573..00973a7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ corvus_test .*.sw[pon] .cache +tags +src/tags +*.patch diff --git a/corvus.conf b/corvus.conf index a61dfcc..e7eddbd 100644 --- a/corvus.conf +++ b/corvus.conf @@ -2,6 +2,14 @@ bind 12345 node localhost:8000,localhost:8001,localhost:8002 thread 4 +# pidfile settings +pidfile /tmp/corvus.pid + +# daemon setting +# 0: not daemon +# 1: run as a daemon +daemon 0 + # debug, info, warn, error loglevel debug syslog 0 diff --git a/src/config.c b/src/config.c index e779e48..1d24629 100644 --- a/src/config.c +++ b/src/config.c @@ -38,6 +38,8 @@ const char * CONFIG_OPTIONS[] = { "slowlog-log-slower-than", "slowlog-max-len", "slowlog-statsd-enabled", + "pidfile", + "daemon", }; void config_init() @@ -60,6 +62,8 @@ void config_init() config.slowlog_max_len = 1024; config.slowlog_log_slower_than = -1; config.slowlog_statsd_enabled = 0; + config.pidfile = 0; + config.daemon = 0; memset(config.statsd_addr, 0, sizeof(config.statsd_addr)); config.metric_interval = 10; @@ -282,6 +286,13 @@ int config_add(char *name, char *value) config.slowlog_max_len = val; } else if (strcmp(name, "slowlog-statsd-enabled") == 0) { config_boolean(&config.slowlog_statsd_enabled, value); + } else if (strcmp(name, "pidfile") == 0) { + if (strlen(value) > 0) { + config.pidfile = cv_calloc(strlen(value) + 1, sizeof(char)); + memcpy(config.pidfile, value, strlen(value)); + } + } else if (strcmp(name, "daemon") == 0) { + config_boolean(&config.daemon, value); } return CORVUS_OK; } @@ -335,6 +346,14 @@ int config_get(const char *name, char *value, size_t max_len) snprintf(value, max_len, "%d", config.slowlog_max_len); } else if (strcmp(name, "slowlog-statsd-enabled") == 0) { strncpy(value, BOOL_STR(config.slowlog_statsd_enabled), max_len); + } else if (strcmp(name, "pidfile") == 0) { + if (config.pidfile) { + strncpy(value, config.pidfile, max_len); + } else { + strncpy(value, "", max_len); + } + } else if (strcmp(name, "daemon") == 0) { + strncpy(value, BOOL_STR(config.daemon), max_len); } else { return CORVUS_ERR; } diff --git a/src/config.h b/src/config.h index ccb4228..227d810 100644 --- a/src/config.h +++ b/src/config.h @@ -5,6 +5,7 @@ #define CLUSTER_NAME_SIZE 127 #define CONFIG_FILE_PATH_SIZE 256 +#define PIDFILE_MAX_LEN 24 struct node_conf { struct address *addr; @@ -32,6 +33,8 @@ struct corvus_config { int slowlog_log_slower_than; int slowlog_max_len; bool slowlog_statsd_enabled; + char *pidfile; + bool daemon; } config; void config_init(); @@ -47,4 +50,4 @@ void config_node_dec_ref(struct node_conf *node); int config_add(char *name, char *value); bool config_option_changable(const char *option); -#endif /* end of include guard: CONFIG_H */ \ No newline at end of file +#endif /* end of include guard: CONFIG_H */ diff --git a/src/corvus.c b/src/corvus.c index bcef61e..41f3cb3 100644 --- a/src/corvus.c +++ b/src/corvus.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "corvus.h" #include "mbuf.h" #include "slot.h" @@ -17,6 +19,7 @@ #include "dict.h" #include "timer.h" #include "alloc.h" +#include "daemon.h" static pthread_spinlock_t signal_lock; static struct context *contexts; @@ -324,6 +327,8 @@ static const struct option opts[] = { {"slowlog-time", required_argument, NULL, 'g'}, {"slowlog-max", required_argument, NULL, 'G'}, {"slowlog-statsd", required_argument, NULL, 'E'}, + {"pidfile", optional_argument, NULL, 'p'}, + {"daemon", no_argument, NULL, 'd'}, { 0, 0, 0, 0 }, }; @@ -344,6 +349,8 @@ static const char *opts_desc[] = { "slowlog time in microseconds", "slowlog max len", "slowlog whether writing to statsd", + "set pid file", + "run as a daemon", }; static void usage(const char *cmd) @@ -363,7 +370,7 @@ static int parameter_init(int argc, const char *argv[]) { int ch; opterr = optind = 0; do { - ch = getopt_long(argc, (char * const *)argv, ":n:t:c:b:l:s:B:C:S:A:m:L:P:g:G:E:", opts, NULL); + ch = getopt_long(argc, (char * const *)argv, ":n:t:c:b:l:s:B:C:S:A:m:L:P:g:G:E:p:d:", opts, NULL); if (ch < 0) { break; } @@ -416,6 +423,12 @@ static int parameter_init(int argc, const char *argv[]) { case 'E': config_add("slowlog-statsd-enabled", optarg); break; + case 'p': + config_add("pidfile", optarg); + break; + case 'd': + config_add("daemon", optarg); + break; default: fprintf(stderr, "unknow option: %c\n", ch); return CORVUS_ERR; @@ -424,6 +437,38 @@ static int parameter_init(int argc, const char *argv[]) { return CORVUS_OK; } + +int create_pidfile() { + char pid[PIDFILE_MAX_LEN]; + int fd, pid_len; + fd = open(config.pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + LOG(ERROR, "open pidfile failed: %s", strerror(errno)); + return CORVUS_ERR; + } + + pid_len = snprintf(pid, PIDFILE_MAX_LEN, "%d", getpid()); + + ssize_t n; + n = write(fd, pid, pid_len); + if (n < 0) { + LOG(ERROR, "write to pidfile failed: %s", strerror(errno)); + return CORVUS_ERR; + } + + close(fd); + + return CORVUS_OK; +} +void remove_pidfile() { + int status; + status= unlink(config.pidfile); + if (status < 0) { + LOG(ERROR, "unlink pid file '%s' failed: %s", + config.pidfile, strerror(errno)); + } +} + int main(int argc, const char *argv[]) { int i, err; @@ -464,6 +509,12 @@ int main(int argc, const char *argv[]) openlog(NULL, LOG_NDELAY | LOG_NOWAIT, LOG_USER); } + if (config.daemon) + daemonize(); + + if (config.pidfile) + create_pidfile(); + // allocate memory for `contexts` build_contexts(); @@ -529,6 +580,10 @@ int main(int argc, const char *argv[]) destroy_contexts(); cmd_map_destroy(); cv_free(config.requirepass); + if (config.pidfile) { + remove_pidfile(); + cv_free(config.pidfile); + } config_free(); if (config.syslog) closelog(); return EXIT_SUCCESS; diff --git a/src/daemon.c b/src/daemon.c new file mode 100644 index 0000000..23afa49 --- /dev/null +++ b/src/daemon.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include "corvus.h" +#include "logging.h" +#include "daemon.h" + +int daemonize() { + int status; + pid_t pid, sid; + int fd; + + pid = fork(); + switch (pid) { + case -1: + LOG(ERROR, "fork() failed: %s", strerror(errno)); + return CORVUS_ERR; + case 0: + break; + default: + /* parent */ + exit(0); + } + + /* The first child is the session leader */ + sid = setsid(); + if (sid < 0) { + LOG(ERROR, "setsid() failed: %s", strerror(errno)); + return CORVUS_ERR; + } + + pid = fork(); + switch (pid) { + case -1: + LOG(ERROR, "fork() failed: %s", strerror(errno)); + return CORVUS_ERR; + case 0: + break; + default: + /* the first child terminates */ + exit(0); + } + + /* the second child continues */ + umask(0); + + /* redirect stdin,stdout,stderr to /dev/null */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) { + LOG(ERROR, "open(\"/dev/null\") failed: %s", strerror(errno)); + return CORVUS_ERR; + } + + status = dup2(fd, STDIN_FILENO); + if (status < 0) { + LOG(ERROR, "dup2(%d, STDIN) failed: %s", fd, strerror(errno)); + close(fd); + return CORVUS_ERR; + } + status = dup2(fd, STDOUT_FILENO); + if (status < 0) { + LOG(ERROR, "dup2(%d, STDOUT) failed: %s", fd, strerror(errno)); + close(fd); + return CORVUS_ERR; + } + status = dup2(fd, STDERR_FILENO); + if (status < 0) { + LOG(ERROR, "dup2(%d, STDERR) failed: %s", fd, strerror(errno)); + close(fd); + return CORVUS_ERR; + } + if (fd > STDERR_FILENO) { + status = close(fd); + if (status < 0) { + LOG(ERROR, "close(%d) failed: %s", fd, strerror(errno)); + close(fd); + return CORVUS_ERR; + } + } + return CORVUS_OK; +} diff --git a/src/daemon.h b/src/daemon.h new file mode 100644 index 0000000..ba1e537 --- /dev/null +++ b/src/daemon.h @@ -0,0 +1,6 @@ +#ifndef DAEMON_H +#define DAEMON_H + +int daemonize(); + +#endif /* end of include guard: DAEMON_H */