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; }
/* * Parse configuration file; if any errors are encountered, * list them and exit. * * Ensure any default values set here are synced with repmgr.conf.sample * and any other documentation. */ bool parse_config(t_configuration_options *options) { FILE *fp; char *s, buff[MAXLINELENGTH]; char name[MAXLEN]; char value[MAXLEN]; /* For sanity-checking provided conninfo string */ PQconninfoOption *conninfo_options; char *conninfo_errmsg = NULL; /* Collate configuration file errors here for friendlier reporting */ static ErrorList config_errors = { NULL, NULL }; fp = fopen(config_file_path, "r"); /* * Since some commands don't require a config file at all, not having one * isn't necessarily a problem. * * If the user explictly provided a configuration file and we can't * read it we'll raise an error. * * If no configuration file was provided, we'll try and read the default\ * file if it exists and is readable, but won't worry if it's not. */ if (fp == NULL) { if (config_file_provided) { log_err(_("unable to open provided configuration file '%s'; terminating\n"), config_file_path); exit(ERR_BAD_CONFIG); } log_notice(_("no configuration file provided and default file '%s' not found - " "continuing with default values\n"), DEFAULT_CONFIG_FILE); return false; } /* Initialize configuration options with sensible defaults * note: the default log level is set in log.c and does not need * to be initialised here */ memset(options->cluster_name, 0, sizeof(options->cluster_name)); options->node = -1; options->upstream_node = NO_UPSTREAM_NODE; options->use_replication_slots = 0; memset(options->conninfo, 0, sizeof(options->conninfo)); options->failover = MANUAL_FAILOVER; options->priority = DEFAULT_PRIORITY; memset(options->node_name, 0, sizeof(options->node_name)); memset(options->promote_command, 0, sizeof(options->promote_command)); memset(options->follow_command, 0, sizeof(options->follow_command)); memset(options->rsync_options, 0, sizeof(options->rsync_options)); memset(options->ssh_options, 0, sizeof(options->ssh_options)); memset(options->pg_bindir, 0, sizeof(options->pg_bindir)); memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options)); memset(options->pg_basebackup_options, 0, sizeof(options->pg_basebackup_options)); /* default master_response_timeout is 60 seconds */ options->master_response_timeout = 60; /* default to 6 reconnection attempts at intervals of 10 seconds */ options->reconnect_attempts = 6; options->reconnect_interval = 10; options->monitor_interval_secs = 2; options->retry_promote_interval_secs = 300; memset(options->event_notification_command, 0, sizeof(options->event_notification_command)); options->tablespace_mapping.head = NULL; options->tablespace_mapping.tail = NULL; /* Read next line */ while ((s = fgets(buff, sizeof buff, fp)) != NULL) { bool known_parameter = true; /* Parse name/value pair from line */ parse_line(buff, name, value); /* Skip blank lines */ if (!strlen(name)) continue; /* Skip comments */ if (name[0] == '#') continue; /* Copy into correct entry in parameters struct */ if (strcmp(name, "cluster") == 0) strncpy(options->cluster_name, value, MAXLEN); else if (strcmp(name, "node") == 0) options->node = repmgr_atoi(value, "node", &config_errors); else if (strcmp(name, "upstream_node") == 0) options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors); else if (strcmp(name, "conninfo") == 0) strncpy(options->conninfo, value, MAXLEN); else if (strcmp(name, "rsync_options") == 0) strncpy(options->rsync_options, value, QUERY_STR_LEN); else if (strcmp(name, "ssh_options") == 0) strncpy(options->ssh_options, value, QUERY_STR_LEN); else if (strcmp(name, "loglevel") == 0) strncpy(options->loglevel, value, MAXLEN); else if (strcmp(name, "logfacility") == 0) strncpy(options->logfacility, value, MAXLEN); else if (strcmp(name, "failover") == 0) { char failoverstr[MAXLEN]; strncpy(failoverstr, value, MAXLEN); if (strcmp(failoverstr, "manual") == 0) { options->failover = MANUAL_FAILOVER; } else if (strcmp(failoverstr, "automatic") == 0) { options->failover = AUTOMATIC_FAILOVER; } else { log_err(_("value for 'failover' must be 'automatic' or 'manual'\n")); exit(ERR_BAD_CONFIG); } } else if (strcmp(name, "priority") == 0) options->priority = repmgr_atoi(value, "priority", &config_errors); else if (strcmp(name, "node_name") == 0) strncpy(options->node_name, value, MAXLEN); else if (strcmp(name, "promote_command") == 0) strncpy(options->promote_command, value, MAXLEN); else if (strcmp(name, "follow_command") == 0) strncpy(options->follow_command, value, MAXLEN); else if (strcmp(name, "master_response_timeout") == 0) options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors); /* 'primary_response_timeout' as synonym for 'master_response_timeout' - * we'll switch terminology in a future release (3.1?) */ else if (strcmp(name, "primary_response_timeout") == 0) options->master_response_timeout = repmgr_atoi(value, "primary_response_timeout", &config_errors); else if (strcmp(name, "reconnect_attempts") == 0) options->reconnect_attempts = repmgr_atoi(value, "reconnect_attempts", &config_errors); else if (strcmp(name, "reconnect_interval") == 0) options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", &config_errors); else if (strcmp(name, "pg_bindir") == 0) strncpy(options->pg_bindir, value, MAXLEN); else if (strcmp(name, "pg_ctl_options") == 0) strncpy(options->pg_ctl_options, value, MAXLEN); else if (strcmp(name, "pg_basebackup_options") == 0) strncpy(options->pg_basebackup_options, value, MAXLEN); else if (strcmp(name, "logfile") == 0) strncpy(options->logfile, value, MAXLEN); else if (strcmp(name, "monitor_interval_secs") == 0) options->monitor_interval_secs = repmgr_atoi(value, "monitor_interval_secs", &config_errors); else if (strcmp(name, "retry_promote_interval_secs") == 0) options->retry_promote_interval_secs = repmgr_atoi(value, "retry_promote_interval_secs", &config_errors); else if (strcmp(name, "use_replication_slots") == 0) /* XXX we should have a dedicated boolean argument format */ options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", &config_errors); else if (strcmp(name, "event_notification_command") == 0) strncpy(options->event_notification_command, value, MAXLEN); else if (strcmp(name, "event_notifications") == 0) parse_event_notifications_list(options, value); else if (strcmp(name, "tablespace_mapping") == 0) tablespace_list_append(options, value); else { known_parameter = false; log_warning(_("%s/%s: unknown name/value pair provided; ignoring\n"), name, value); } /* * Raise an error if a known parameter is provided with an empty value. * Currently there's no reason why empty parameters are needed; if * we want to accept those, we'd need to add stricter default checking, * as currently e.g. an empty `node` value will be converted to '0'. */ if (known_parameter == true && !strlen(value)) { char error_message_buf[MAXLEN] = ""; snprintf(error_message_buf, MAXLEN, _("no value provided for parameter \"%s\""), name); error_list_append(&config_errors, error_message_buf); } } fclose(fp); /* Check config settings */ /* The following checks are for the presence of the parameter */ if (*options->cluster_name == '\0') { error_list_append(&config_errors, _("\"cluster\": parameter was not found\n")); } if (options->node == -1) { error_list_append(&config_errors, _("\"node\": parameter was not found\n")); } if (*options->node_name == '\0') { error_list_append(&config_errors, _("\"node_name\": parameter was not found\n")); } if (*options->conninfo == '\0') { error_list_append(&config_errors, _("\"conninfo\": parameter was not found\n")); } else { /* Sanity check the provided conninfo string * * NOTE: this verifies the string format and checks for valid options * but does not sanity check values */ conninfo_options = PQconninfoParse(options->conninfo, &conninfo_errmsg); if (conninfo_options == NULL) { char error_message_buf[MAXLEN] = ""; snprintf(error_message_buf, MAXLEN, _("\"conninfo\": %s"), conninfo_errmsg); error_list_append(&config_errors, error_message_buf); } PQconninfoFree(conninfo_options); } // exit_with_errors here if (config_errors.head != NULL) { exit_with_errors(&config_errors); } return true; }