From 0fb48762563d06f092441f47c8cf03f348070727 Mon Sep 17 00:00:00 2001 From: Ian Barwick Date: Thu, 20 Apr 2017 09:41:50 +0900 Subject: [PATCH] Initial config file infrastructure Also unify command line option structure/parsing so everything's in the same order in the code. --- config.c | 44 ++++++++++++++++++++ config.h | 17 +++++++- repmgr-client.c | 105 +++++++++++++++++++++++++++++++++++++++++++++--- repmgr-client.h | 36 ++++++++++++----- repmgr.h | 4 ++ 5 files changed, 189 insertions(+), 17 deletions(-) diff --git a/config.c b/config.c index a195e930..85632b54 100644 --- a/config.c +++ b/config.c @@ -10,6 +10,9 @@ #include "config.h" const static char *_progname = NULL; +static void _parse_config(t_configuration_options *options, ItemList *error_list); +static void exit_with_errors(ItemList *config_errors); + void set_progname(const char *argv0) @@ -23,6 +26,47 @@ progname(void) return _progname; } +bool +load_config(const char *config_file, bool verbose, t_configuration_options *options, char *argv0) +{ + return true; +} + +bool +parse_config(t_configuration_options *options) +{ + /* Collate configuration file errors here for friendlier reporting */ + static ItemList config_errors = { NULL, NULL }; + + _parse_config(options, &config_errors); + + if (config_errors.head != NULL) + { + exit_with_errors(&config_errors); + } + + return true; +} + +static void +_parse_config(t_configuration_options *options, ItemList *error_list) +{ + +} + + +bool +reload_config(t_configuration_options *orig_options) +{ + return true; +} + + +static void +exit_with_errors(ItemList *config_errors) +{ +} + void item_list_append(ItemList *item_list, char *error_message) { diff --git a/config.h b/config.h index 9ca9163d..fbbe3ba2 100644 --- a/config.h +++ b/config.h @@ -18,6 +18,17 @@ typedef struct } t_configuration_options; +/* + * The following will initialize the structure with a minimal set of options; + * actual defaults are set in parse_config() before parsing the configuration file + */ + +#define T_CONFIGURATION_OPTIONS_INITIALIZER { \ + /* node settings */ \ + UNKNOWN_NODE_ID, "", \ + /* log settings */ \ + "", "", ""} + typedef struct ItemListCell { struct ItemListCell *next; @@ -34,8 +45,10 @@ typedef struct ItemList void set_progname(const char *argv0); const char *progname(void); +bool load_config(const char *config_file, bool verbose, t_configuration_options *options, char *argv0); +bool parse_config(t_configuration_options *options); +bool reload_config(t_configuration_options *orig_options); + void item_list_append(ItemList *item_list, char *error_message); - - #endif diff --git a/repmgr-client.c b/repmgr-client.c index 7f29b2ae..ee66fab8 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -6,6 +6,10 @@ * This module is a command-line utility to easily setup a cluster of * hot standby servers for an HA environment * + * Commands implemented are: + * + * [ MASTER | PRIMARY ] REGISTER + * */ #include "repmgr.h" @@ -13,6 +17,7 @@ /* global configuration structures */ t_runtime_options runtime_options = T_RUNTIME_OPTIONS_INITIALIZER; +t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER; /* Collate command line errors and warnings here for friendlier reporting */ ItemList cli_errors = { NULL, NULL }; @@ -22,7 +27,7 @@ int main(int argc, char **argv) { int optindex; - int c, targ; + int c; char *repmgr_node_type = NULL; char *repmgr_action = NULL; @@ -30,17 +35,20 @@ main(int argc, char **argv) int action = NO_ACTION; char *dummy_action = ""; + bool config_file_parsed = false; + + set_progname(argv[0]); /* * Tell the logger we're a command-line program - this will * ensure any output logged before the logger is initialized - * will be formatted correctly + * will be formatted correctly. Can be overriden with "--log-to-file". */ logger_output_mode = OM_COMMAND_LINE; - while ((c = getopt_long(argc, argv, "?Vd:h:p:U:S:D:f:R:w:k:FWIvb:rcL:tm:C:", long_options, + while ((c = getopt_long(argc, argv, "?Vf:vtFb:", long_options, &optindex)) != -1) { /* @@ -55,7 +63,7 @@ main(int argc, char **argv) * Options which cause repmgr to exit in this block; * these are the only ones which can be executed as root user */ - case OPT_HELP: + case OPT_HELP: /* --help */ do_help(); exit(SUCCESS); case '?': @@ -69,10 +77,60 @@ main(int argc, char **argv) case 'V': printf("%s %s\n", progname(), REPMGR_VERSION); exit(SUCCESS); - /* general options */ + + /* general configuration options + * ----------------------------- */ + + /* -f/--config-file */ case 'f': strncpy(runtime_options.config_file, optarg, MAXLEN); break; + /* -F/--force */ + case 'F': + runtime_options.force = true; + break; + /* -b/--pg_bindir */ + case 'b': + strncpy(runtime_options.pg_bindir, optarg, MAXLEN); + break; + + /* logging options + * --------------- */ + + /* -L/--log-level */ + case 'L': + { + int detected_log_level = detect_log_level(optarg); + if (detected_log_level != -1) + { + strncpy(runtime_options.loglevel, optarg, MAXLEN); + } + else + { + PQExpBufferData invalid_log_level; + initPQExpBuffer(&invalid_log_level); + appendPQExpBuffer(&invalid_log_level, _("Invalid log level \"%s\" provided"), optarg); + item_list_append(&cli_errors, invalid_log_level.data); + termPQExpBuffer(&invalid_log_level); + } + break; + } + + /* --log-to-file */ + case OPT_LOG_TO_FILE: + runtime_options.log_to_file = true; + logger_output_mode = OM_DAEMON; + break; + /* --terse */ + case 't': + runtime_options.terse = true; + break; + /* --verbose */ + case 'v': + runtime_options.verbose = true; + break; + + } } @@ -179,6 +237,33 @@ main(int argc, char **argv) exit_with_errors(); } + + /* + * The configuration file is not required for some actions (e.g. 'standby clone'), + * however if available we'll parse it anyway for options like 'log_level', + * 'use_replication_slots' etc. + */ + config_file_parsed = load_config(runtime_options.config_file, + runtime_options.verbose, + &config_file_options, + argv[0]); + /* + * Initialize the logger. We've previously requested STDERR logging only + * to ensure the repmgr command doesn't have its output diverted to a logging + * facility (which usually doesn't make sense for a command line program). + * + * If required (e.g. when calling repmgr from repmgrd), this behaviour can be + * overridden with "--log-to-file". + */ + + logger_init(&config_file_options, progname()); + + if (runtime_options.verbose) + logger_set_verbose(); + + if (runtime_options.terse) + logger_set_terse(); + return SUCCESS; } @@ -206,7 +291,17 @@ do_help(void) printf(_(" -V, --version output version information, then exit\n")); puts(""); printf(_("General configuration options:\n")); + printf(_(" -b, --pg_bindir=PATH path to PostgreSQL binaries (optional)\n")); printf(_(" -f, --config-file=PATH path to the configuration file\n")); + printf(_(" -F, --force force potentially dangerous operations to happen\n")); + puts(""); + printf(_("Logging options:\n")); + printf(_(" -L, --log-level set log level (overrides configuration file; default: NOTICE)\n")); + printf(_(" --log-to-file log to file (or logging facility) defined in repmgr.conf\n")); + printf(_(" -t, --terse don't display hints and other non-critical output\n")); + printf(_(" -v, --verbose display additional log output (useful for debugging)\n")); + printf(_("\n")); + puts(""); } diff --git a/repmgr-client.h b/repmgr-client.h index 05c84c57..057bd1d0 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -61,26 +61,32 @@ static struct option long_options[] = { +/* general options */ {"version", no_argument, NULL, 'V'}, {"help", no_argument, NULL, OPT_HELP}, +/* general configuration options */ + {"config-file", required_argument, NULL, 'f'}, + {"force", no_argument, NULL, 'F'}, + {"pg_bindir", required_argument, NULL, 'b'}, + +/* logging options */ + {"log-level", required_argument, NULL, 'L'}, + {"log-to-file", no_argument, NULL, OPT_LOG_TO_FILE}, + {"terse", required_argument, NULL, 't'}, + {"verbose", no_argument, NULL, 'v'}, + {"dbname", required_argument, NULL, 'd'}, {"host", required_argument, NULL, 'h'}, {"port", required_argument, NULL, 'p'}, {"username", required_argument, NULL, 'U'}, {"superuser", required_argument, NULL, 'S'}, {"pgdata", required_argument, NULL, 'D'}, - {"config-file", required_argument, NULL, 'f'}, {"remote-user", required_argument, NULL, 'R'}, {"wal-keep-segments", required_argument, NULL, 'w'}, {"keep-history", required_argument, NULL, 'k'}, - {"force", no_argument, NULL, 'F'}, {"wait", no_argument, NULL, 'W'}, - {"verbose", no_argument, NULL, 'v'}, - {"pg_bindir", required_argument, NULL, 'b'}, {"rsync-only", no_argument, NULL, 'r'}, {"fast-checkpoint", no_argument, NULL, 'c'}, - {"log-level", required_argument, NULL, 'L'}, - {"terse", required_argument, NULL, 't'}, {"mode", required_argument, NULL, 'm'}, {"remote-config-file", required_argument, NULL, 'C'}, {"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG}, @@ -93,7 +99,6 @@ static struct option long_options[] = {"no-upstream-connection", no_argument, NULL, OPT_NO_UPSTREAM_CONNECTION}, {"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES}, {"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT}, - {"log-to-file", no_argument, NULL, OPT_LOG_TO_FILE}, {"upstream-conninfo", required_argument, NULL, OPT_UPSTREAM_CONNINFO}, {"replication-user", required_argument, NULL, OPT_REPLICATION_USER}, {"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD}, @@ -106,13 +111,24 @@ static struct option long_options[] = typedef struct { - /* general repmgr options */ + /* general configuration options */ char config_file[MAXPGPATH]; + bool force; + char pg_bindir[MAXLEN]; /* overrides setting in repmgr.conf */ + /* logging options */ + char loglevel[MAXLEN]; /* overrides setting in repmgr.conf */ + bool log_to_file; + bool terse; + bool verbose; + + } t_runtime_options; #define T_RUNTIME_OPTIONS_INITIALIZER { \ - /* general repmgr options */ \ - ""} + /* general configuration options */ \ + "", false, "", \ + /* logging options */ \ + "", false, false, false} static void do_help(void); diff --git a/repmgr.h b/repmgr.h index cd6fe811..263d34bb 100644 --- a/repmgr.h +++ b/repmgr.h @@ -20,4 +20,8 @@ #define MIN_SUPPORTED_VERSION "9.3" #define MIN_SUPPORTED_VERSION_NUM 90300 +#define NODE_NOT_FOUND -1 +#define NO_UPSTREAM_NODE -1 +#define UNKNOWN_NODE_ID -1 + #endif