diff --git a/errcode.h b/errcode.h index 6f9239c2..e9eed0ad 100644 --- a/errcode.h +++ b/errcode.h @@ -11,6 +11,7 @@ #define SUCCESS 0 #define ERR_BAD_CONFIG 1 #define ERR_BAD_RSYNC 2 +#define ERR_BAD_PIDFILE 3 #define ERR_NO_RESTART 4 #define ERR_DB_CONN 6 #define ERR_DB_QUERY 7 diff --git a/repmgrd.c b/repmgrd.c index a8caad2e..7bba6efe 100644 --- a/repmgrd.c +++ b/repmgrd.c @@ -9,12 +9,56 @@ #include -static void do_help(void); +#include + + +#define OPT_HELP 1 + +static char *config_file = NULL; +static bool verbose = false; +static char *pid_file = NULL; +static bool daemonize = false; + +t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER; + +static t_node_info local_node_info; + +static void show_help(void); +static void show_usage(void); +static void daemonize_process(void); +static void check_and_create_pid_file(const char *pid_file); int main(int argc, char **argv) { - /* Disallow running as root to prevent directory ownership problems */ + int optindex; + int c; + bool monitoring_history = false; + + static struct option long_options[] = + { +/* general options */ + {"help", no_argument, NULL, OPT_HELP}, + {"version", no_argument, NULL, 'V'}, + +/* configuration options */ + {"config-file", required_argument, NULL, 'f'}, + +/* daemon options */ + {"daemonize", no_argument, NULL, 'd'}, + {"pid-file", required_argument, NULL, 'p'}, + +/* logging options */ + {"verbose", no_argument, NULL, 'v'}, + +/* legacy options */ + {"monitoring-history", no_argument, NULL, 'm'}, + {NULL, 0, NULL, 0} + }; + + set_progname(argv[0]); + + /* Disallow running as root */ if (geteuid() == 0) { fprintf(stderr, @@ -27,12 +71,250 @@ main(int argc, char **argv) exit(1); } - do_help(); - return 0; + while ((c = getopt_long(argc, argv, "?Vf:vdp:m", long_options, &optindex)) != -1) + { + switch (c) + { + + /* general options */ + + case '?': + /* Actual help option given */ + if (strcmp(argv[optind - 1], "-?") == 0) + { + show_help(); + exit(SUCCESS); + } + /* unknown option reported by getopt */ + goto unknown_option; + break; + + case OPT_HELP: + show_help(); + exit(SUCCESS); + + case 'V': + /* + * in contrast to repmgr3 and earlier, we only display the repmgr version + * as it's not specific to a particular PostgreSQL version + */ + printf("%s %s\n", progname(), REPMGR_VERSION); + exit(SUCCESS); + + /* configuration options */ + + case 'f': + config_file = optarg; + break; + + /* daemon options */ + + case 'd': + daemonize = true; + break; + + case 'p': + pid_file = optarg; + break; + + /* logging options */ + + case 'v': + verbose = true; + break; + + /* legacy options */ + + case 'm': + monitoring_history = true; + break; + + default: + unknown_option: + show_usage(); + exit(ERR_BAD_CONFIG); + } + } + + /* + * Tell the logger we're a daemon - this will ensure any output logged + * before the logger is initialized will be formatted correctly + */ + logger_output_mode = OM_DAEMON; + + + /* + * Parse the configuration file, if provided. If no configuration file + * was provided, or one was but was incomplete, parse_config() will + * abort anyway, with an appropriate message. + */ + load_config(config_file, verbose, false, &config_file_options, argv[0]); + + /* + * -m/--monitoring-history, if provided, will override repmgr.conf's + * monitoring_history; this is for backwards compatibility as it's + * possible this may be baked into various startup scripts. + */ + + if (monitoring_history == true) + { + config_file_options.monitoring_history = true; + } + + if (daemonize == true) + { + daemonize_process(); + } + + if (pid_file != NULL) + { + check_and_create_pid_file(pid_file); + } + + while(1) { + sleep(1); + } + /* shut down logging system */ + logger_shutdown(); + + return SUCCESS; +} + + +static void +daemonize_process(void) +{ + char *ptr, + path[MAXLEN]; + pid_t pid = fork(); + int ret; + + switch (pid) + { + case -1: + log_error(_("error in fork():\n %s"), strerror(errno)); + exit(ERR_SYS_FAILURE); + break; + + case 0: /* child process */ + pid = setsid(); + if (pid == (pid_t) -1) + { + log_error(_("error in setsid():\n %s"), strerror(errno)); + exit(ERR_SYS_FAILURE); + } + + /* ensure that we are no longer able to open a terminal */ + pid = fork(); + + if (pid == -1) /* error case */ + { + log_error(_("error in fork():\n %s"), strerror(errno)); + exit(ERR_SYS_FAILURE); + } + + if (pid != 0) /* parent process */ + { + exit(0); + } + + /* a child just flows along */ + + memset(path, 0, MAXLEN); + + for (ptr = config_file + strlen(config_file); ptr > config_file; --ptr) + { + if (*ptr == '/') + { + strncpy(path, config_file, ptr - config_file); + } + } + + if (*path == '\0') + { + *path = '/'; + } + + ret = chdir(path); + if (ret != 0) + { + log_error(_("error changing directory to '%s':\n %s"), path, + strerror(errno)); + } + + break; + + default: /* parent process */ + exit(0); + } +} + +static void +check_and_create_pid_file(const char *pid_file) +{ + struct stat st; + FILE *fd; + char buff[MAXLEN]; + pid_t pid; + size_t nread; + + if (stat(pid_file, &st) != -1) + { + memset(buff, 0, MAXLEN); + + fd = fopen(pid_file, "r"); + + if (fd == NULL) + { + log_error(_("PID file %s exists but could not opened for reading"), pid_file); + log_hint(_("if repmgrd is no longer alive, remove the file and restart repmgrd")); + exit(ERR_BAD_PIDFILE); + } + + nread = fread(buff, MAXLEN - 1, 1, fd); + + if (nread == 0 && ferror(fd)) + { + log_error(_("error reading PID file '%s', aborting"), pid_file); + exit(ERR_BAD_PIDFILE); + } + + fclose(fd); + + pid = atoi(buff); + + if (pid != 0) + { + if (kill(pid, 0) != -1) + { + log_error(_("PID file %s exists and seems to contain a valid PID"), pid_file); + log_hint(_("if repmgrd is no longer alive, remove the file and restart repmgrd")); + exit(ERR_BAD_PIDFILE); + } + } + } + + fd = fopen(pid_file, "w"); + if (fd == NULL) + { + log_error(_("could not open PID file %s"), pid_file); + exit(ERR_BAD_CONFIG); + } + + fprintf(fd, "%d", getpid()); + fclose(fd); +} + + +void +show_usage(void) +{ + fprintf(stderr, _("%s: replication management daemon for PostgreSQL\n"), progname()); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname()); } void -do_help(void) +show_help(void) { printf(_("%s: replication management daemon for PostgreSQL\n"), progname()); puts("");