Пример #1
0
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;
}
Пример #2
0
/*
 * 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;
}