From f972aec19801cf44be326da552aae988662a0f8b Mon Sep 17 00:00:00 2001 From: Ian Barwick Date: Thu, 10 Aug 2017 23:58:16 +0900 Subject: [PATCH] Parse recovery.conf file This will be useful for various kinds of diagnostics. --- configfile.c | 140 +++++++++++++++++++++++++++++++++---------- configfile.h | 53 ++++++++++++++++ repmgr-action-node.c | 29 +++++---- strutil.c | 28 +++++++++ strutil.h | 2 + 5 files changed, 208 insertions(+), 44 deletions(-) diff --git a/configfile.c b/configfile.c index 04eeca60..5042ef05 100644 --- a/configfile.c +++ b/configfile.c @@ -24,7 +24,6 @@ static void _parse_line(char *buf, char *name, char *value); static void parse_event_notifications_list(t_configuration_options *options, const char *arg); static void tablespace_list_append(t_configuration_options *options, const char *arg); -static char *trim(char *s); static void exit_with_config_file_errors(ItemList *config_errors, ItemList *config_warnings, bool terse); @@ -556,6 +555,7 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * } } + fclose(fp); /* check required parameters */ if (node_id_found == false) @@ -631,6 +631,103 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * } + + +bool +parse_recovery_conf(const char *data_dir, t_recovery_conf *conf) +{ + char recovery_conf_path[MAXPGPATH] = ""; + FILE *fp; + char *s, + buf[MAXLINELENGTH]; + char name[MAXLEN]; + char value[MAXLEN]; + + snprintf(recovery_conf_path, MAXPGPATH, + "%s/%s", + data_dir, + RECOVERY_COMMAND_FILE); + + fp = fopen(recovery_conf_path, "r"); + + if (fp == NULL) + { + return false; + } + + /* Read file */ + while ((s = fgets(buf, sizeof buf, fp)) != NULL) + { + + /* Parse name/value pair from line */ + _parse_line(buf, name, value); + + /* Skip blank lines */ + if (!strlen(name)) + continue; + + /* Skip comments */ + if (name[0] == '#') + continue; + + /* archive recovery settings */ + if (strcmp(name, "restore_command") == 0) + strncpy(conf->restore_command, value, MAXLEN); + else if (strcmp(name, "archive_cleanup_command") == 0) + strncpy(conf->archive_cleanup_command, value, MAXLEN); + else if (strcmp(name, "recovery_end_command") == 0) + strncpy(conf->recovery_end_command, value, MAXLEN); + /* recovery target settings */ + else if (strcmp(name, "recovery_target_name") == 0) + strncpy(conf->recovery_target_name, value, MAXLEN); + else if (strcmp(name, "recovery_target_time") == 0) + strncpy(conf->recovery_target_time, value, MAXLEN); + else if (strcmp(name, "recovery_target_xid") == 0) + strncpy(conf->recovery_target_xid, value, MAXLEN); + else if (strcmp(name, "recovery_target_inclusive") == 0) + conf->recovery_target_inclusive = parse_bool(value, NULL, NULL); + else if (strcmp(name, "recovery_target_timeline") == 0) + { + if (strncmp(value, "latest", MAXLEN) == 0) + { + conf->recovery_target_timeline = TARGET_TIMELINE_LATEST; + } + else + { + conf->recovery_target_timeline = atoi(value); + } + } + else if (strcmp(name, "recovery_target_action") == 0) + { + if (strcmp(value, "pause") == 0) + conf->recovery_target_action = RTA_PAUSE; + else if (strcmp(value, "promote") == 0) + conf->recovery_target_action = RTA_PROMOTE; + else if (strcmp(value, "shutdown") == 0) + conf->recovery_target_action = RTA_SHUTDOWN; + } + + /* standby server settings */ + + else if (strcmp(name, "standby_mode") == 0) + conf->standby_mode = parse_bool(value, NULL, NULL); + else if (strcmp(name, "primary_conninfo") == 0) + strncpy(conf->primary_conninfo, value, MAXLEN); + else if (strcmp(name, "primary_slot_name") == 0) + strncpy(conf->trigger_file, value, MAXLEN); + else if (strcmp(name, "trigger_file") == 0) + strncpy(conf->trigger_file, value, MAXLEN); + /* TODO: parse values */ + /*else if (strcmp(name, "recovery_min_apply_delay") == 0) + strncpy(conf->, value, MAXLEN);*/ + + } + fclose(fp); + + return true; +} + + void _parse_line(char *buf, char *name, char *value) { @@ -689,32 +786,6 @@ _parse_line(char *buf, char *name, char *value) trim(value); } -static char * -trim(char *s) -{ - /* Initialize start, end pointers */ - char *s1 = s, - *s2 = &s[strlen(s) - 1]; - - /* If string is empty, no action needed */ - if (s2 < s1) - return s; - - /* Trim and delimit right side */ - while ((isspace(*s2)) && (s2 >= s1)) - --s2; - *(s2 + 1) = '\0'; - - /* Trim left side */ - while ((isspace(*s1)) && (s1 < s2)) - ++s1; - - /* Copy finished string */ - memmove(s, s1, s2 - s1); - s[s2 - s1 + 1] = '\0'; - - return s; -} bool @@ -884,13 +955,16 @@ parse_bool(const char *s, const char *config_item, ItemList *error_list) if (strcasecmp(s, "yes") == 0) return true; - initPQExpBuffer(&errors); + if (error_list != NULL) + { + initPQExpBuffer(&errors); - appendPQExpBuffer(&errors, - "\"%s\": unable to interpret '%s' as a boolean value", - config_item, s); - item_list_append(error_list, errors.data); - termPQExpBuffer(&errors); + appendPQExpBuffer(&errors, + "\"%s\": unable to interpret '%s' as a boolean value", + config_item, s); + item_list_append(error_list, errors.data); + termPQExpBuffer(&errors); + } return false; } diff --git a/configfile.h b/configfile.h index d24d9bfd..ca07fc92 100644 --- a/configfile.h +++ b/configfile.h @@ -12,6 +12,9 @@ #define CONFIG_FILE_NAME "repmgr.conf" #define MAXLINELENGTH 4096 +/* magic number for use in t_recovery_conf */ +#define TARGET_TIMELINE_LATEST 0 + extern bool config_file_found; extern char config_file_path[MAXPGPATH]; @@ -167,6 +170,55 @@ typedef struct #define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", false } +typedef enum { + RTA_PAUSE, + RTA_PROMOTE, + RTA_SHUTDOWN +} RecoveryTargetAction; + +/* + * Struct to hold the contents of a parsed recovery.conf file. + * We're only really interested in those related to streaming + * replication (and also "restore_command") but include the + * others for completeness. + * + * NOTE: "recovery_target" not included as it can only have + * one value, "immediate". + */ +typedef struct +{ + /* archive recovery settings */ + char restore_command[MAXLEN]; + char archive_cleanup_command[MAXLEN]; + char recovery_end_command[MAXLEN]; + /* recovery target settings */ + char recovery_target_name[MAXLEN]; + char recovery_target_time[MAXLEN]; + char recovery_target_xid[MAXLEN]; + bool recovery_target_inclusive; + int recovery_target_timeline; + RecoveryTargetAction recovery_target_action; /* default: RTA_PAUSE */ + /* standby server settings */ + bool standby_mode; + char primary_conninfo[MAXLEN]; + char primary_slot_name[MAXLEN]; + char trigger_file[MAXLEN]; + int recovery_min_apply_delay; +} t_recovery_conf; + +#define T_RECOVERY_CONF_INITIALIZER { \ + /* archive recovery settings */ \ + "", "", "", \ + /* recovery target settings */ \ + "", "", "", true, \ + TARGET_TIMELINE_LATEST, \ + RTA_PAUSE, \ + /* standby server settings */ \ + true, \ + "", "", "", 0 \ +} + + void set_progname(const char *argv0); const char *progname(void); @@ -175,6 +227,7 @@ void load_config(const char *config_file, bool verbose, bool terse, t_configura void parse_config(t_configuration_options *options, bool terse); bool reload_config(t_configuration_options *orig_options); +bool parse_recovery_conf(const char *data_dir, t_recovery_conf *conf); int repmgr_atoi(const char *s, const char *config_item, diff --git a/repmgr-action-node.c b/repmgr-action-node.c index fd034538..53ef7c59 100644 --- a/repmgr-action-node.c +++ b/repmgr-action-node.c @@ -46,6 +46,10 @@ do_node_status(void) ItemList warnings = { NULL, NULL }; RecoveryType recovery_type; ReplInfo replication_info = T_REPLINFO_INTIALIZER; + t_recovery_conf recovery_conf = T_RECOVERY_CONF_INITIALIZER; + + char data_dir[MAXPGPATH] = ""; + if (runtime_options.is_shutdown == true) { @@ -57,6 +61,16 @@ do_node_status(void) else conn = establish_db_connection_by_params(&source_conninfo, true); + if (config_file_options.data_directory[0] != '\0') + { + strncpy(data_dir, config_file_options.data_directory, MAXPGPATH); + } + else + { + /* requires superuser */ + get_pg_setting(conn, "data_directory", data_dir); + } + server_version_num = get_server_version(conn, NULL); if (runtime_options.node_id != UNKNOWN_NODE_ID) @@ -180,19 +194,8 @@ do_node_status(void) } { - char data_dir[MAXPGPATH] = ""; int ready_files; - if (config_file_options.data_directory[0] != '\0') - { - strncpy(data_dir, config_file_options.data_directory, MAXPGPATH); - } - else - { - /* requires superuser */ - get_pg_setting(conn, "data_directory", data_dir); - } - ready_files = get_ready_archive_files(conn, data_dir); key_value_list_set_format( @@ -317,6 +320,10 @@ do_node_status(void) key_value_list_set_output_mode(&node_status, "Last replayed LSN", OM_CSV); } + + parse_recovery_conf(data_dir, &recovery_conf); + + /* format output */ initPQExpBuffer(&output); if (runtime_options.output_mode == OM_CSV) diff --git a/strutil.c b/strutil.c index ea024040..259b61e5 100644 --- a/strutil.c +++ b/strutil.c @@ -255,3 +255,31 @@ string_remove_trailing_newlines(char *string) return string; } + + +char * +trim(char *s) +{ + /* Initialize start, end pointers */ + char *s1 = s, + *s2 = &s[strlen(s) - 1]; + + /* If string is empty, no action needed */ + if (s2 < s1) + return s; + + /* Trim and delimit right side */ + while ((isspace(*s2)) && (s2 >= s1)) + --s2; + *(s2 + 1) = '\0'; + + /* Trim left side */ + while ((isspace(*s1)) && (s1 < s2)) + ++s1; + + /* Copy finished string */ + memmove(s, s1, s2 - s1); + s[s2 - s1 + 1] = '\0'; + + return s; +} diff --git a/strutil.h b/strutil.h index ac437e3c..3051fc18 100644 --- a/strutil.h +++ b/strutil.h @@ -99,5 +99,7 @@ string_skip_prefix(const char *prefix, char *string); extern char *string_remove_trailing_newlines(char *string); +extern char *trim(char *s); + #endif /* _STRUTIL_H_ */