6 Star 3 Fork 5

Gitee 极速下载/repmgr

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/2ndQuadrant/repmgr/
克隆/下载
configfile.c 60.33 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321
/*
* configfile.c - parse repmgr.conf and other configuration-related functionality
*
* Copyright (c) EnterpriseDB Corporation, 2010-2021
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h> /* for stat() */
#include "repmgr.h"
#include "configfile.h"
#include "log.h"
#include <utils/elog.h>
#if (PG_ACTUAL_VERSION_NUM >= 100000)
#include <storage/fd.h> /* for durable_rename() */
#endif
const static char *_progname = NULL;
char config_file_path[MAXPGPATH] = "";
static bool config_file_provided = false;
bool config_file_found = false;
static void parse_config(bool terse);
static void _parse_config(ItemList *error_list, ItemList *warning_list);
static void _parse_line(char *buf, char *name, char *value);
static void parse_event_notifications_list(EventNotificationList *event_notifications, const char *arg);
static void clear_event_notification_list(EventNotificationList *event_notifications);
static void copy_config_file_options(t_configuration_options *original, t_configuration_options *copy);
static void tablespace_list_append(TablespaceList *tablespace_mapping, const char *arg);
static void tablespace_list_copy(t_configuration_options *original, t_configuration_options *copy);
static void tablespace_list_free(t_configuration_options *options);
static void exit_with_config_file_errors(ItemList *config_errors, ItemList *config_warnings, bool terse);
void
set_progname(const char *argv0)
{
_progname = get_progname(argv0);
}
const char *
progname(void)
{
return _progname;
}
void
load_config(const char *config_file, bool verbose, bool terse, char *argv0)
{
struct stat stat_config;
/*
* If a configuration file was provided, check it exists, otherwise emit
* an error and terminate. We assume that if a user explicitly provides a
* configuration file, they'll want to make sure it's used and not fall
* back to any of the defaults.
*/
if (config_file != NULL && config_file[0] != '\0')
{
strncpy(config_file_path, config_file, MAXPGPATH);
canonicalize_path(config_file_path);
/* relative path supplied - convert to absolute path */
if (config_file_path[0] != '/')
{
PQExpBufferData fullpath;
char *pwd = NULL;
initPQExpBuffer(&fullpath);
/*
* we'll attempt to use $PWD to derive the effective path; getcwd()
* will likely resolve symlinks, which may result in a path which
* isn't permanent (e.g. if filesystem mountpoints change).
*/
pwd = getenv("PWD");
if (pwd != NULL)
{
appendPQExpBufferStr(&fullpath, pwd);
}
else
{
/* $PWD not available - fall back to getcwd() */
char cwd[MAXPGPATH] = "";
if (getcwd(cwd, MAXPGPATH) == NULL)
{
log_error(_("unable to execute getcwd()"));
log_detail("%s", strerror(errno));
termPQExpBuffer(&fullpath);
exit(ERR_BAD_CONFIG);
}
appendPQExpBufferStr(&fullpath, cwd);
}
appendPQExpBuffer(&fullpath,
"/%s", config_file_path);
log_debug("relative configuration file converted to:\n \"%s\"",
fullpath.data);
strncpy(config_file_path, fullpath.data, MAXPGPATH);
termPQExpBuffer(&fullpath);
canonicalize_path(config_file_path);
}
if (stat(config_file_path, &stat_config) != 0)
{
log_error(_("provided configuration file \"%s\" not found"),
config_file);
log_detail("%s", strerror(errno));
exit(ERR_BAD_CONFIG);
}
if (verbose == true)
{
log_notice(_("using provided configuration file \"%s\""), config_file);
}
config_file_provided = true;
config_file_found = true;
}
/*-----------
* If no configuration file was provided, attempt to find a default file
* in this order:
* - location provided by packager
* - current directory
* - /etc/repmgr.conf
* - default sysconfdir
*
* here we just check for the existence of the file; parse_config() will
* handle read errors etc.
*
*-----------
*/
if (config_file_provided == false)
{
/* packagers: if feasible, patch configuration file path into "package_conf_file" */
char package_conf_file[MAXPGPATH] = "";
char my_exec_path[MAXPGPATH] = "";
char sysconf_etc_path[MAXPGPATH] = "";
/* 1. location provided by packager */
if (package_conf_file[0] != '\0')
{
if (verbose == true)
fprintf(stdout, _("INFO: checking for package configuration file \"%s\"\n"), package_conf_file);
if (stat(package_conf_file, &stat_config) == 0)
{
strncpy(config_file_path, package_conf_file, MAXPGPATH);
config_file_found = true;
goto end_search;
}
}
/* 2 "./repmgr.conf" */
log_verbose(LOG_INFO, _("looking for configuration file in current directory\n"));
maxpath_snprintf(config_file_path, "./%s", CONFIG_FILE_NAME);
canonicalize_path(config_file_path);
if (stat(config_file_path, &stat_config) == 0)
{
config_file_found = true;
goto end_search;
}
/* 3. "/etc/repmgr.conf" */
if (verbose == true)
fprintf(stdout, _("INFO: looking for configuration file in /etc\n"));
maxpath_snprintf(config_file_path, "/etc/%s", CONFIG_FILE_NAME);
if (stat(config_file_path, &stat_config) == 0)
{
config_file_found = true;
goto end_search;
}
/* 4. default sysconfdir */
if (find_my_exec(argv0, my_exec_path) < 0)
{
fprintf(stderr, _("ERROR: %s: could not find own program executable\n"), argv0);
exit(EXIT_FAILURE);
}
get_etc_path(my_exec_path, sysconf_etc_path);
if (verbose == true)
fprintf(stdout, _("INFO: looking for configuration file in \"%s\"\n"), sysconf_etc_path);
maxpath_snprintf(config_file_path, "%s/%s", sysconf_etc_path, CONFIG_FILE_NAME);
if (stat(config_file_path, &stat_config) == 0)
{
config_file_found = true;
goto end_search;
}
end_search:
if (verbose == true)
{
if (config_file_found == true)
{
fprintf(stdout, _("INFO: configuration file found at: \"%s\"\n"), config_file_path);
}
else
{
fprintf(stdout, _("INFO: no configuration file provided or found\n"));
}
}
}
parse_config(terse);
return;
}
static void
parse_config(bool terse)
{
/* Collate configuration file errors here for friendlier reporting */
static ItemList config_errors = {NULL, NULL};
static ItemList config_warnings = {NULL, NULL};
_parse_config(&config_errors, &config_warnings);
/* errors found - exit after printing details, and any warnings */
if (config_errors.head != NULL)
{
exit_with_config_file_errors(&config_errors, &config_warnings, terse);
}
if (terse == false && config_warnings.head != NULL)
{
log_warning(_("the following problems were found in the configuration file:"));
print_item_list(&config_warnings);
}
return;
}
static void
_parse_config(ItemList *error_list, ItemList *warning_list)
{
FILE *fp;
char base_directory[MAXPGPATH];
bool config_ok;
ConfigFileSetting *setting;
int i = 0;
/*
* Clear lists pointing to allocated memory
*/
clear_event_notification_list(&config_file_options.event_notifications);
tablespace_list_free(&config_file_options);
/*
* Initialise with default values
*/
setting = &config_file_settings[0];
do {
switch (setting->type)
{
case CONFIG_INT:
*setting->val.intptr = setting->defval.intdefault;
break;
case CONFIG_BOOL:
*setting->val.boolptr = setting->defval.booldefault;
break;
case CONFIG_STRING:
{
memset((char *)setting->val.strptr, 0, setting->maxval.strmaxlen);
if (setting->defval.strdefault != NULL)
strncpy((char *)setting->val.strptr, setting->defval.strdefault, setting->maxval.strmaxlen);
break;
}
case CONFIG_FAILOVER_MODE:
*setting->val.failovermodeptr = setting->defval.failovermodedefault;
break;
case CONFIG_CONNECTION_CHECK_TYPE:
*setting->val.checktypeptr = setting->defval.checktypedefault;
break;
case CONFIG_REPLICATION_TYPE:
*setting->val.replicationtypeptr = setting->defval.replicationtypedefault;
break;
case CONFIG_EVENT_NOTIFICATION_LIST:
case CONFIG_TABLESPACE_MAPPING:
/* no default for these types; lists cleared above */
break;
default:
/* this should never happen */
log_error("unhandled setting type %i", (int)setting->type);
}
i++;
setting = &config_file_settings[i];
} while (setting->name != NULL);
/*
* If no configuration file available (user didn't specify and none found
* in the default locations), return with default values
*/
if (config_file_found == false)
{
log_verbose(LOG_NOTICE,
_("no configuration file provided and no default file found - "
"continuing with default values"));
return;
}
/*
* A configuration file has been found, either provided by the user or
* found in one of the default locations. Sanity check whether we
* can open it, and fail with an error about the nature of the file
* (provided or default) if not. We do this here rather than having
* to teach the configuration file parser the difference.
*/
fp = fopen(config_file_path, "r");
if (fp == NULL)
{
if (config_file_provided)
{
log_error(_("unable to open provided configuration file \"%s\"; terminating"),
config_file_path);
}
else
{
log_error(_("unable to open default configuration file \"%s\"; terminating"),
config_file_path);
}
exit(ERR_BAD_CONFIG);
}
fclose(fp);
strncpy(base_directory, config_file_path, MAXPGPATH);
canonicalize_path(base_directory);
get_parent_directory(base_directory);
config_ok = ProcessRepmgrConfigFile(config_file_path, base_directory, error_list, warning_list);
/*
* Perform some more complex checks which the file processing step can't do,
* including checking for required parameters and sanity-checking parameters
* with dependencies on other parameters.
*/
if (config_ok == true)
{
/* check required parameters */
if (config_file_options.node_id == UNKNOWN_NODE_ID)
{
item_list_append(error_list, _("\"node_id\": required parameter was not found"));
}
if (!strlen(config_file_options.node_name))
{
item_list_append(error_list, _("\"node_name\": required parameter was not found"));
}
if (!strlen(config_file_options.data_directory))
{
item_list_append(error_list, _("\"data_directory\": required parameter was not found"));
}
if (!strlen(config_file_options.conninfo))
{
item_list_append(error_list, _("\"conninfo\": required parameter was not found"));
}
else
{
/*
* Basic sanity check of provided conninfo string; this will catch any
* invalid parameters (but not values).
*/
char *conninfo_errmsg = NULL;
if (validate_conninfo_string(config_file_options.conninfo, &conninfo_errmsg) == false)
{
PQExpBufferData error_message_buf;
initPQExpBuffer(&error_message_buf);
appendPQExpBuffer(&error_message_buf,
_("\"conninfo\": %s (provided: \"%s\")"),
conninfo_errmsg,
config_file_options.conninfo);
item_list_append(error_list, error_message_buf.data);
termPQExpBuffer(&error_message_buf);
}
}
/* set values for parameters which default to other parameters */
/*
* From 4.1, "repmgrd_standby_startup_timeout" replaces "standby_reconnect_timeout"
* in repmgrd; fall back to "standby_reconnect_timeout" if no value explicitly provided
*/
if (config_file_options.repmgrd_standby_startup_timeout == -1)
{
config_file_options.repmgrd_standby_startup_timeout = config_file_options.standby_reconnect_timeout;
}
/* add warning about changed "barman_" parameter meanings */
if ((config_file_options.barman_host[0] == '\0' && config_file_options.barman_server[0] != '\0') ||
(config_file_options.barman_host[0] != '\0' && config_file_options.barman_server[0] == '\0'))
{
item_list_append(error_list,
_("use \"barman_host\" for the hostname of the Barman server"));
item_list_append(error_list,
_("use \"barman_server\" for the name of the [server] section in the Barman configuration file"));
}
/* other sanity checks */
if (config_file_options.archive_ready_warning >= config_file_options.archive_ready_critical)
{
item_list_append(error_list,
_("\"archive_ready_critical\" must be greater than \"archive_ready_warning\""));
}
if (config_file_options.replication_lag_warning >= config_file_options.replication_lag_critical)
{
item_list_append(error_list,
_("\"replication_lag_critical\" must be greater than \"replication_lag_warning\""));
}
if (config_file_options.standby_reconnect_timeout < config_file_options.node_rejoin_timeout)
{
item_list_append(error_list,
_("\"standby_reconnect_timeout\" must be equal to or greater than \"node_rejoin_timeout\""));
}
}
}
void
parse_configuration_item(ItemList *error_list, ItemList *warning_list, const char *name, const char *value)
{
ConfigFileSetting *setting = &config_file_settings[0];
int i = 0;
do {
if (strcmp(name, setting->name) == 0)
{
switch (setting->type)
{
/* Generic types */
case CONFIG_BOOL:
{
*(bool *)setting->val.boolptr = parse_bool(value, name, error_list);
break;
}
case CONFIG_INT:
{
*(int *)setting->val.intptr = repmgr_atoi(value, name, error_list, setting->minval.intminval);
break;
}
case CONFIG_STRING:
{
if (strlen(value) > setting->maxval.strmaxlen)
{
item_list_append_format(error_list,
_("value for \"%s\" must contain fewer than %i characters (current length: %i)"),
name,
setting->maxval.strmaxlen,
(int)strlen(value));
}
else
{
/* custom function for processing this string value */
if (setting->process.process_func != NULL)
{
(*setting->process.process_func)(name, value, (char *)setting->val.strptr, error_list);
}
/* otherwise copy as-is */
else
{
strncpy((char *)setting->val.strptr, value, setting->maxval.strmaxlen);
}
/* post-processing, e.g. path canonicalisation */
if (setting->process.postprocess_func != NULL)
{
(*setting->process.postprocess_func)(name, value, (char *)setting->val.strptr, error_list);
}
if (setting->process.providedptr != NULL)
{
*(bool *)setting->process.providedptr = true;
}
}
break;
}
/* repmgr types */
case CONFIG_FAILOVER_MODE:
{
if (strcmp(value, "manual") == 0)
{
*(failover_mode_opt *)setting->val.failovermodeptr = FAILOVER_MANUAL;
}
else if (strcmp(value, "automatic") == 0)
{
*(failover_mode_opt *)setting->val.failovermodeptr = FAILOVER_AUTOMATIC;
}
else
{
item_list_append_format(error_list,
_("value for \"%s\" must be \"automatic\" or \"manual\"\n"),
name);
}
break;
}
case CONFIG_CONNECTION_CHECK_TYPE:
{
if (strcasecmp(value, "ping") == 0)
{
*(ConnectionCheckType *)setting->val.checktypeptr = CHECK_PING;
}
else if (strcasecmp(value, "connection") == 0)
{
*(ConnectionCheckType *)setting->val.checktypeptr = CHECK_CONNECTION;
}
else if (strcasecmp(value, "query") == 0)
{
*(ConnectionCheckType *)setting->val.checktypeptr = CHECK_QUERY;
}
else
{
item_list_append_format(error_list,
_("value for \"%s\" must be \"ping\", \"connection\" or \"query\"\n"),
name);
}
break;
}
case CONFIG_REPLICATION_TYPE:
{
if (strcasecmp(value, "physical") == 0)
{
*(ReplicationType *)setting->val.replicationtypeptr = REPLICATION_TYPE_PHYSICAL;
}
else
{
item_list_append_format(error_list,
_("value for \"%s\" must be \"physical\"\n"),
name);
}
break;
}
case CONFIG_EVENT_NOTIFICATION_LIST:
{
parse_event_notifications_list((EventNotificationList *)setting->val.notificationlistptr,
value);
break;
}
case CONFIG_TABLESPACE_MAPPING:
{
tablespace_list_append((TablespaceList *)setting->val.tablespacemappingptr, value);
break;
}
default:
/* this should never happen */
log_error("encountered unknown configuration type %i when processing \"%s\"",
(int)setting->type,
setting->name);
}
/* Configuration item found - we can stop processing here */
return;
}
i++;
setting = &config_file_settings[i];
} while (setting->name);
/* If we reach here, the configuration item is either deprecated or unknown */
if (strcmp(name, "cluster") == 0)
{
item_list_append(warning_list,
_("parameter \"cluster\" is deprecated and will be ignored"));
}
else if (strcmp(name, "node") == 0)
{
item_list_append(warning_list,
_("parameter \"node\" has been renamed to \"node_id\""));
}
else if (strcmp(name, "upstream_node") == 0)
{
item_list_append(warning_list,
_("parameter \"upstream_node\" has been removed; use \"--upstream-node-id\" when cloning a standby"));
}
else if (strcmp(name, "loglevel") == 0)
{
item_list_append(warning_list,
_("parameter \"loglevel\" has been renamed to \"log_level\""));
}
else if (strcmp(name, "logfacility") == 0)
{
item_list_append(warning_list,
_("parameter \"logfacility\" has been renamed to \"log_facility\""));
}
else if (strcmp(name, "logfile") == 0)
{
item_list_append(warning_list,
_("parameter \"logfile\" has been renamed to \"log_file\""));
}
else if (strcmp(name, "master_reponse_timeout") == 0)
{
item_list_append(warning_list,
_("parameter \"master_reponse_timeout\" has been removed; use \"async_query_timeout\" instead"));
}
else if (strcmp(name, "retry_promote_interval_secs") == 0)
{
item_list_append(warning_list,
_("parameter \"retry_promote_interval_secs\" has been removed; use \"primary_notification_timeout\" instead"));
}
else
{
item_list_append_format(warning_list,
_("%s='%s': unknown name/value pair provided; ignoring"), name, value);
}
}
bool
parse_recovery_conf(const char *data_dir, t_recovery_conf *conf)
{
char recovery_conf_path[MAXPGPATH] = "";
FILE *fp;
char *s = NULL,
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);
else if (strcmp(name, "recovery_min_apply_delay") == 0)
parse_time_unit_parameter(name, value, conf->recovery_min_apply_delay, NULL);
}
fclose(fp);
return true;
}
void
_parse_line(char *buf, char *name, char *value)
{
int i = 0;
int j = 0;
/*
* Extract parameter name, if present
*/
for (; i < MAXLEN; ++i)
{
if (buf[i] == '=')
break;
switch (buf[i])
{
/* Ignore whitespace */
case ' ':
case '\n':
case '\r':
case '\t':
continue;
default:
name[j++] = buf[i];
}
}
name[j] = '\0';
/*
* Ignore any whitespace following the '=' sign
*/
for (; i < MAXLEN; ++i)
{
if (buf[i + 1] == ' ')
continue;
if (buf[i + 1] == '\t')
continue;
break;
}
/*
* Extract parameter value
*/
j = 0;
for (++i; i < MAXLEN; ++i)
if (buf[i] == '\'')
continue;
else if (buf[i] == '#')
break;
else if (buf[i] != '\n')
value[j++] = buf[i];
else
break;
value[j] = '\0';
trim(value);
}
void
parse_time_unit_parameter(const char *name, const char *value, char *dest, ItemList *errors)
{
char *ptr = NULL;
int targ = strtol(value, &ptr, 10);
if (targ < 0)
{
if (errors != NULL)
{
item_list_append_format(errors,
_("invalid value \"%s\" provided for \"%s\""),
value,
name);
}
return;
}
if (ptr && *ptr)
{
if (strcmp(ptr, "ms") != 0 && strcmp(ptr, "s") != 0 &&
strcmp(ptr, "min") != 0 && strcmp(ptr, "h") != 0 &&
strcmp(ptr, "d") != 0)
{
if (errors != NULL)
{
item_list_append_format(
errors,
_("value for \"%s\" must be one of ms/s/min/h/d (provided: \"%s\")"),
name,
value);
return;
}
}
}
strncpy(dest, value, MAXLEN);
}
/*
* reload_config()
*
* This is only called by repmgrd after receiving a SIGHUP or when a monitoring
* loop is started up; it therefore only needs to reload options required
* by repmgrd, which are as follows:
*
* changeable options (keep the list in "doc/repmgrd-configuration.xml" in sync
* with these):
*
* - async_query_timeout
* - child_nodes_check_interval
* - child_nodes_connected_min_count
* - child_nodes_connected_include_witness
* - child_nodes_disconnect_command
* - child_nodes_disconnect_min_count
* - child_nodes_disconnect_timeout
* - connection_check_type
* - conninfo
* - degraded_monitoring_timeout
* - event_notification_command
* - event_notifications
* - failover
* - failover_validation_command
* - follow_command
* - log_facility
* - log_file
* - log_level
* - log_status_interval
* - monitor_interval_secs
* - monitoring_history
* - primary_notification_timeout
* - primary_visibility_consensus
* - always_promote
* - promote_command
* - reconnect_attempts
* - reconnect_interval
* - repmgrd_standby_startup_timeout
* - retry_promote_interval_secs
* - sibling_nodes_disconnect_timeout
* - standby_disconnect_on_failover
*
*
* Not publicly documented:
* - promote_delay
*
* non-changeable options (repmgrd references these from the "repmgr.nodes"
* table, not the configuration file)
*
* - node_id
* - node_name
* - data_directory
* - location
* - priority
* - replication_type
*
* extract with something like:
* grep config_file_options\\. repmgrd*.c | perl -n -e '/config_file_options\.([\w_]+)/ && print qq|$1\n|;' | sort | uniq
*
* Returns "true" if the configuration was successfully changed, otherwise "false".
*/
bool
reload_config(t_server_type server_type)
{
bool log_config_changed = false;
ItemList config_errors = {NULL, NULL};
ItemList config_warnings = {NULL, NULL};
ItemList config_changes = {NULL, NULL};
t_configuration_options orig_config_file_options;
copy_config_file_options(&config_file_options, &orig_config_file_options);
log_info(_("reloading configuration file"));
log_detail(_("using file \"%s\""), config_file_path);
/*
* _parse_config() will sanity-check the provided values and put any
* errors/warnings in the provided lists; no need to add further sanity
* checks here. We do still need to check for repmgrd-specific
* requirements.
*/
_parse_config(&config_errors, &config_warnings);
if (config_file_options.failover == FAILOVER_AUTOMATIC
&& (server_type == PRIMARY || server_type == STANDBY))
{
if (config_file_options.promote_command[0] == '\0')
{
item_list_append(&config_errors, _("\"promote_command\": required parameter was not found"));
}
if (config_file_options.follow_command[0] == '\0')
{
item_list_append(&config_errors, _("\"follow_command\": required parameter was not found"));
}
}
/* The following options cannot be changed */
if (config_file_options.node_id != orig_config_file_options.node_id)
{
item_list_append_format(&config_errors,
_("\"node_id\" cannot be changed, retaining current configuration %i %i"),
config_file_options.node_id,
orig_config_file_options.node_id);
}
if (strncmp(config_file_options.node_name, orig_config_file_options.node_name, sizeof(config_file_options.node_name)) != 0)
{
item_list_append(&config_errors,
_("\"node_name\" cannot be changed, keeping current configuration"));
}
/*
* conninfo
*
* _parse_config() will already have sanity-checked the string; we do that here
* again so we can avoid trying to connect with a known bad string
*/
if (strncmp(config_file_options.conninfo, orig_config_file_options.conninfo, sizeof(config_file_options.conninfo)) != 0 && validate_conninfo_string(config_file_options.conninfo, NULL))
{
PGconn *conn;
/* Test conninfo string works */
conn = establish_db_connection(config_file_options.conninfo, false);
if (!conn || (PQstatus(conn) != CONNECTION_OK))
{
item_list_append_format(&config_errors,
_("provided \"conninfo\" string \"%s\" is not valid"),
config_file_options.conninfo);
}
else
{
item_list_append_format(&config_changes,
_("\"conninfo\" changed from \"%s\" to \"%s\""),
orig_config_file_options.conninfo,
config_file_options.conninfo);
}
PQfinish(conn);
}
/*
* If any issues encountered, raise an error and roll back to the original
* configuration
*/
if (config_errors.head != NULL)
{
ItemListCell *cell = NULL;
PQExpBufferData errors;
log_error(_("one or more errors encountered while parsing the configuration file"));
initPQExpBuffer(&errors);
appendPQExpBufferStr(&errors,
"following errors were detected:\n");
for (cell = config_errors.head; cell; cell = cell->next)
{
appendPQExpBuffer(&errors,
" %s\n", cell->string);
}
log_detail("%s", errors.data);
termPQExpBuffer(&errors);
log_notice(_("the current configuration has been retained unchanged"));
copy_config_file_options(&orig_config_file_options, &config_file_options);
return false;
}
/*
* No configuration problems detected - log any changed values.
*
* NB: keep these in the same order as in configfile.h to make it easier
* to manage them
*/
/* async_query_timeout */
if (config_file_options.async_query_timeout != orig_config_file_options.async_query_timeout)
{
item_list_append_format(&config_changes,
_("\"async_query_timeout\" changed from \"%i\" to \"%i\""),
orig_config_file_options.async_query_timeout,
config_file_options.async_query_timeout);
}
/* child_nodes_check_interval */
if (config_file_options.child_nodes_check_interval != orig_config_file_options.child_nodes_check_interval)
{
item_list_append_format(&config_changes,
_("\"child_nodes_check_interval\" changed from \"%i\" to \"%i\""),
orig_config_file_options.child_nodes_check_interval,
config_file_options.child_nodes_check_interval);
}
/* child_nodes_disconnect_command */
if (strncmp(config_file_options.child_nodes_disconnect_command, orig_config_file_options.child_nodes_disconnect_command, sizeof(config_file_options.child_nodes_disconnect_command)) != 0)
{
item_list_append_format(&config_changes,
_("\"child_nodes_disconnect_command\" changed from \"%s\" to \"%s\""),
orig_config_file_options.child_nodes_disconnect_command,
config_file_options.child_nodes_disconnect_command);
}
/* child_nodes_disconnect_min_count */
if (config_file_options.child_nodes_disconnect_min_count != orig_config_file_options.child_nodes_disconnect_min_count)
{
item_list_append_format(&config_changes,
_("\"child_nodes_disconnect_min_count\" changed from \"%i\" to \"%i\""),
orig_config_file_options.child_nodes_disconnect_min_count,
config_file_options.child_nodes_disconnect_min_count);
}
/* child_nodes_connected_min_count */
if (config_file_options.child_nodes_connected_min_count != orig_config_file_options.child_nodes_connected_min_count)
{
item_list_append_format(&config_changes,
_("\"child_nodes_connected_min_count\" changed from \"%i\" to \"%i\""),
orig_config_file_options.child_nodes_connected_min_count,
config_file_options.child_nodes_connected_min_count);
}
/* child_nodes_connected_include_witness */
if (config_file_options.child_nodes_connected_include_witness != orig_config_file_options.child_nodes_connected_include_witness)
{
item_list_append_format(&config_changes,
_("\"child_nodes_connected_include_witness\" changed from \"%s\" to \"%s\""),
format_bool(orig_config_file_options.child_nodes_connected_include_witness),
format_bool(config_file_options.child_nodes_connected_include_witness));
}
/* child_nodes_disconnect_timeout */
if (config_file_options.child_nodes_disconnect_timeout != orig_config_file_options.child_nodes_disconnect_timeout)
{
item_list_append_format(&config_changes,
_("\"child_nodes_disconnect_timeout\" changed from \"%i\" to \"%i\""),
orig_config_file_options.child_nodes_disconnect_timeout,
config_file_options.child_nodes_disconnect_timeout);
}
/* degraded_monitoring_timeout */
if (config_file_options.degraded_monitoring_timeout != orig_config_file_options.degraded_monitoring_timeout)
{
item_list_append_format(&config_changes,
_("\"degraded_monitoring_timeout\" changed from \"%i\" to \"%i\""),
orig_config_file_options.degraded_monitoring_timeout,
config_file_options.degraded_monitoring_timeout);
}
/* event_notification_command */
if (strncmp(config_file_options.event_notification_command, orig_config_file_options.event_notification_command, sizeof(config_file_options.event_notification_command)) != 0)
{
item_list_append_format(&config_changes,
_("\"event_notification_command\" changed from \"%s\" to \"%s\""),
orig_config_file_options.event_notification_command,
config_file_options.event_notification_command);
}
/* event_notifications */
if (strncmp(config_file_options.event_notifications_orig, orig_config_file_options.event_notifications_orig, sizeof(config_file_options.event_notifications_orig)) != 0)
{
item_list_append_format(&config_changes,
_("\"event_notifications\" changed from \"%s\" to \"%s\""),
orig_config_file_options.event_notifications_orig,
config_file_options.event_notifications_orig);
}
/* failover */
if (config_file_options.failover != orig_config_file_options.failover)
{
item_list_append_format(&config_changes,
_("\"failover\" changed from \"%s\" to \"%s\""),
format_failover_mode(orig_config_file_options.failover),
format_failover_mode(config_file_options.failover));
}
/* follow_command */
if (strncmp(config_file_options.follow_command, orig_config_file_options.follow_command, sizeof(config_file_options.follow_command)) != 0)
{
item_list_append_format(&config_changes,
_("\"follow_command\" changed from \"%s\" to \"%s\""),
orig_config_file_options.follow_command,
config_file_options.follow_command);
}
/* monitor_interval_secs */
if (config_file_options.monitor_interval_secs != orig_config_file_options.monitor_interval_secs)
{
item_list_append_format(&config_changes,
_("\"monitor_interval_secs\" changed from \"%i\" to \"%i\""),
orig_config_file_options.monitor_interval_secs,
config_file_options.monitor_interval_secs);
}
/* monitoring_history */
if (config_file_options.monitoring_history != orig_config_file_options.monitoring_history)
{
item_list_append_format(&config_changes,
_("\"monitoring_history\" changed from \"%s\" to \"%s\""),
format_bool(orig_config_file_options.monitoring_history),
format_bool(config_file_options.monitoring_history));
}
/* primary_notification_timeout */
if (config_file_options.primary_notification_timeout != orig_config_file_options.primary_notification_timeout)
{
item_list_append_format(&config_changes,
_("\"primary_notification_timeout\" changed from \"%i\" to \"%i\""),
orig_config_file_options.primary_notification_timeout,
config_file_options.primary_notification_timeout);
}
/* promote_command */
if (strncmp(config_file_options.promote_command, orig_config_file_options.promote_command, sizeof(config_file_options.promote_command)) != 0)
{
item_list_append_format(&config_changes,
_("\"promote_command\" changed from \"%s\" to \"%s\""),
orig_config_file_options.promote_command,
config_file_options.promote_command);
}
/* promote_delay (for testing use only; not documented */
if (config_file_options.promote_delay != orig_config_file_options.promote_delay)
{
item_list_append_format(&config_changes,
_("\"promote_delay\" changed from \"%i\" to \"%i\""),
orig_config_file_options.promote_delay,
config_file_options.promote_delay);
}
/* reconnect_attempts */
if (config_file_options.reconnect_attempts != orig_config_file_options.reconnect_attempts)
{
item_list_append_format(&config_changes,
_("\"reconnect_attempts\" changed from \"%i\" to \"%i\""),
orig_config_file_options.reconnect_attempts,
config_file_options.reconnect_attempts);
}
/* reconnect_interval */
if (config_file_options.reconnect_interval != orig_config_file_options.reconnect_interval)
{
item_list_append_format(&config_changes,
_("\"reconnect_interval\" changed from \"%i\" to \"%i\""),
orig_config_file_options.reconnect_interval,
config_file_options.reconnect_interval);
}
/* repmgrd_standby_startup_timeout */
if (config_file_options.repmgrd_standby_startup_timeout != orig_config_file_options.repmgrd_standby_startup_timeout)
{
item_list_append_format(&config_changes,
_("\"repmgrd_standby_startup_timeout\" changed from \"%i\" to \"%i\""),
orig_config_file_options.repmgrd_standby_startup_timeout,
config_file_options.repmgrd_standby_startup_timeout);
}
/* standby_disconnect_on_failover */
if (config_file_options.standby_disconnect_on_failover != orig_config_file_options.standby_disconnect_on_failover)
{
item_list_append_format(&config_changes,
_("\"standby_disconnect_on_failover\" changed from \"%s\" to \"%s\""),
format_bool(orig_config_file_options.standby_disconnect_on_failover),
format_bool(config_file_options.standby_disconnect_on_failover));
}
/* sibling_nodes_disconnect_timeout */
if (config_file_options.sibling_nodes_disconnect_timeout != orig_config_file_options.sibling_nodes_disconnect_timeout)
{
item_list_append_format(&config_changes,
_("\"sibling_nodes_disconnect_timeout\" changed from \"%i\" to \"%i\""),
orig_config_file_options.sibling_nodes_disconnect_timeout,
config_file_options.sibling_nodes_disconnect_timeout);
}
/* connection_check_type */
if (config_file_options.connection_check_type != orig_config_file_options.connection_check_type)
{
item_list_append_format(&config_changes,
_("\"connection_check_type\" changed from \"%s\" to \"%s\""),
print_connection_check_type(orig_config_file_options.connection_check_type),
print_connection_check_type(config_file_options.connection_check_type));
}
/* primary_visibility_consensus */
if (config_file_options.primary_visibility_consensus != orig_config_file_options.primary_visibility_consensus)
{
item_list_append_format(&config_changes,
_("\"primary_visibility_consensus\" changed from \"%s\" to \"%s\""),
format_bool(orig_config_file_options.primary_visibility_consensus),
format_bool(config_file_options.primary_visibility_consensus));
}
/* always_promote */
if (config_file_options.always_promote != orig_config_file_options.always_promote)
{
item_list_append_format(&config_changes,
_("\"always_promote\" changed from \"%s\" to \"%s\""),
format_bool(orig_config_file_options.always_promote),
format_bool(config_file_options.always_promote));
}
/* failover_validation_command */
if (strncmp(config_file_options.failover_validation_command, orig_config_file_options.failover_validation_command, sizeof(config_file_options.failover_validation_command)) != 0)
{
item_list_append_format(&config_changes,
_("\"failover_validation_command\" changed from \"%s\" to \"%s\""),
orig_config_file_options.failover_validation_command,
config_file_options.failover_validation_command);
}
/*
* Handle changes to logging configuration
*/
/* log_facility */
if (strncmp(config_file_options.log_facility, orig_config_file_options.log_facility, sizeof(config_file_options.log_facility)) != 0)
{
item_list_append_format(&config_changes,
_("\"log_facility\" changed from \"%s\" to \"%s\""),
orig_config_file_options.log_facility,
config_file_options.log_facility);
}
/* log_file */
if (strncmp(config_file_options.log_file, orig_config_file_options.log_file, sizeof(config_file_options.log_file)) != 0)
{
item_list_append_format(&config_changes,
_("\"log_file\" changed from \"%s\" to \"%s\""),
orig_config_file_options.log_file,
config_file_options.log_file);
}
/* log_level */
if (strncmp(config_file_options.log_level, orig_config_file_options.log_level, sizeof(config_file_options.log_level)) != 0)
{
item_list_append_format(&config_changes,
_("\"log_level\" changed from \"%s\" to \"%s\""),
orig_config_file_options.log_level,
config_file_options.log_level);
}
/* log_status_interval */
if (config_file_options.log_status_interval != orig_config_file_options.log_status_interval)
{
item_list_append_format(&config_changes,
_("\"log_status_interval\" changed from \"%i\" to \"%i\""),
orig_config_file_options.log_status_interval,
config_file_options.log_status_interval);
}
if (log_config_changed == true)
{
log_notice(_("restarting logging with changed parameters"));
logger_shutdown();
logger_init(&config_file_options, progname());
log_notice(_("configuration file reloaded with changed parameters"));
}
if (config_changes.head != NULL)
{
ItemListCell *cell = NULL;
PQExpBufferData detail;
log_notice(_("configuration was successfully changed"));
initPQExpBuffer(&detail);
appendPQExpBufferStr(&detail,
_("following configuration items were changed:\n"));
for (cell = config_changes.head; cell; cell = cell->next)
{
appendPQExpBuffer(&detail,
" %s\n", cell->string);
}
log_detail("%s", detail.data);
termPQExpBuffer(&detail);
}
else
{
log_info(_("configuration has not changed"));
}
/*
* parse_configuration_item() (called from _parse_config()) will add warnings
* about any deprecated configuration parameters; we'll dump these here as a reminder.
*/
if (config_warnings.head != NULL)
{
ItemListCell *cell = NULL;
PQExpBufferData detail;
log_warning(_("configuration file contains deprecated parameters"));
initPQExpBuffer(&detail);
appendPQExpBufferStr(&detail,
_("following parameters are deprecated:\n"));
for (cell = config_warnings.head; cell; cell = cell->next)
{
appendPQExpBuffer(&detail,
" %s\n", cell->string);
}
log_detail("%s", detail.data);
termPQExpBuffer(&detail);
}
return config_changes.head == NULL ? false : true;
}
/*
* Dump the parsed configuration
*/
void
dump_config(void)
{
ConfigFileSetting *setting;
int i = 0;
setting = &config_file_settings[0];
do {
printf("%s|", setting->name);
switch (setting->type)
{
case CONFIG_INT:
printf("%i", *setting->val.intptr);
break;
case CONFIG_BOOL:
printf("%s", format_bool(*setting->val.boolptr));
break;
case CONFIG_STRING:
printf("%s", setting->val.strptr);
break;
case CONFIG_FAILOVER_MODE:
printf("%s", format_failover_mode(*setting->val.failovermodeptr));
break;
case CONFIG_CONNECTION_CHECK_TYPE:
printf("%s", print_connection_check_type(*setting->val.checktypeptr));
break;
case CONFIG_REPLICATION_TYPE:
printf("%s", print_replication_type(*setting->val.replicationtypeptr));
break;
case CONFIG_EVENT_NOTIFICATION_LIST:
{
char *list = print_event_notification_list(setting->val.notificationlistptr);
printf("%s", list);
pfree(list);
}
break;
case CONFIG_TABLESPACE_MAPPING:
{
char *list = print_tablespace_mapping(setting->val.tablespacemappingptr);
printf("%s", list);
pfree(list);
}
break;
default:
/* this should never happen */
log_error("unhandled setting type %i", (int)setting->type);
}
puts("");
i++;
setting = &config_file_settings[i];
} while (setting->name != NULL);
}
static void
exit_with_config_file_errors(ItemList *config_errors, ItemList *config_warnings, bool terse)
{
log_error(_("following errors were found in the configuration file:"));
print_item_list(config_errors);
item_list_free(config_errors);
if (terse == false && config_warnings->head != NULL)
{
puts("");
log_warning(_("the following problems were also found in the configuration file:"));
print_item_list(config_warnings);
item_list_free(config_warnings);
}
if (config_file_provided == false)
log_detail(_("configuration file is: \"%s\""), config_file_path);
exit(ERR_BAD_CONFIG);
}
void
exit_with_cli_errors(ItemList *error_list, const char *repmgr_command)
{
fprintf(stderr, _("The following command line errors were encountered:\n"));
print_item_list(error_list);
if (repmgr_command != NULL)
{
fprintf(stderr, _("Try \"%s --help\" or \"%s %s --help\" for more information.\n"),
progname(),
progname(),
repmgr_command);
}
else
{
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname());
}
exit(ERR_BAD_CONFIG);
}
void
print_item_list(ItemList *item_list)
{
ItemListCell *cell = NULL;
for (cell = item_list->head; cell; cell = cell->next)
{
fprintf(stderr, " %s\n", cell->string);
}
}
/*
* Convert provided string to an integer using strtol;
* on error, if a callback is provided, pass the error message to that,
* otherwise exit
*/
int
repmgr_atoi(const char *value, const char *config_item, ItemList *error_list, int minval)
{
char *endptr = NULL;
long longval = 0;
PQExpBufferData errors;
initPQExpBuffer(&errors);
/*
* It's possible that some versions of strtol() don't treat an empty
* string as an error.
*/
if (*value == '\0')
{
/* don't log here - empty values will be caught later */
return 0;
}
else
{
errno = 0;
longval = strtol(value, &endptr, 10);
if (value == endptr || errno)
{
appendPQExpBuffer(&errors,
_("\"%s\": invalid value (provided: \"%s\")"),
config_item, value);
}
else if ((int32) longval < longval)
{
appendPQExpBuffer(&errors,
_("\"%s\": must be a positive signed 32 bit integer, i.e. 2147483647 or less (provided: \"%s\")"),
config_item,
value);
}
else if ((int32) longval < minval)
/* Disallow negative values for most parameters */
{
appendPQExpBuffer(&errors,
_("\"%s\": must be %i or greater (provided: \"%s\")"),
config_item,
minval,
value);
}
}
/* Error message buffer is set */
if (errors.data[0] != '\0')
{
if (error_list == NULL)
{
log_error("%s", errors.data);
termPQExpBuffer(&errors);
exit(ERR_BAD_CONFIG);
}
item_list_append(error_list, errors.data);
}
termPQExpBuffer(&errors);
return (int32) longval;
}
void
repmgr_canonicalize_path(const char *name, const char *value, char *config_item, ItemList *errors)
{
/* NOTE: canonicalize_path does not produce any errors */
canonicalize_path(config_item);
}
/*
* Interpret a parameter value as a boolean. Currently accepts:
*
* - true/false
* - 1/0
* - on/off
* - yes/no
* Returns 'false' if unable to determine the booleanness of the value
* and adds an entry to the error list, which will result in the program
* erroring out before it proceeds to do anything.
*
* TODO: accept "any unambiguous prefix of one of these" as per postgresql.conf:
*
* https://www.postgresql.org/docs/current/config-setting.html
*/
bool
parse_bool(const char *s, const char *config_item, ItemList *error_list)
{
PQExpBufferData errors;
if (s == NULL)
return true;
if (strcasecmp(s, "0") == 0)
return false;
if (strcasecmp(s, "1") == 0)
return true;
if (strcasecmp(s, "false") == 0)
return false;
if (strcasecmp(s, "true") == 0)
return true;
if (strcasecmp(s, "off") == 0)
return false;
if (strcasecmp(s, "on") == 0)
return true;
if (strcasecmp(s, "no") == 0)
return false;
if (strcasecmp(s, "yes") == 0)
return true;
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);
}
return false;
}
/*
* Copy a configuration file struct
*/
void
copy_config_file_options(t_configuration_options *original, t_configuration_options *copy)
{
memcpy(copy, original, (int)sizeof(t_configuration_options));
/* Copy structures which point to allocated memory */
if (original->event_notifications.head != NULL)
{
/* For the event notifications, we can just reparse the string */
parse_event_notifications_list(&copy->event_notifications, original->event_notifications_orig);
}
if (original->tablespace_mapping.head != NULL)
{
/*
* We allow multiple instances of "tablespace_mapping" in the configuration file
* which are appended to the list as they're encountered.
*/
tablespace_list_copy(original, copy);
}
}
/*
* Split argument into old_dir and new_dir and append to tablespace mapping
* list.
*
* Adapted from pg_basebackup.c
*/
static void
tablespace_list_append(TablespaceList *tablespace_mapping, const char *arg)
{
TablespaceListCell *cell = NULL;
char *dst = NULL;
char *dst_ptr = NULL;
const char *arg_ptr = NULL;
cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
if (cell == NULL)
{
log_error(_("unable to allocate memory; terminating"));
exit(ERR_BAD_CONFIG);
}
dst_ptr = dst = cell->old_dir;
for (arg_ptr = arg; *arg_ptr; arg_ptr++)
{
if (dst_ptr - dst >= MAXPGPATH)
{
log_error(_("directory name too long"));
exit(ERR_BAD_CONFIG);
}
if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
; /* skip backslash escaping = */
else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
{
if (*cell->new_dir)
{
log_error(_("multiple \"=\" signs in tablespace mapping"));
exit(ERR_BAD_CONFIG);
}
else
{
dst = dst_ptr = cell->new_dir;
}
}
else
*dst_ptr++ = *arg_ptr;
}
if (!*cell->old_dir || !*cell->new_dir)
{
log_error(_("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\""),
arg);
exit(ERR_BAD_CONFIG);
}
canonicalize_path(cell->old_dir);
canonicalize_path(cell->new_dir);
if (tablespace_mapping->tail)
tablespace_mapping->tail->next = cell;
else
tablespace_mapping->head = cell;
tablespace_mapping->tail = cell;
}
static void
tablespace_list_copy(t_configuration_options *original, t_configuration_options *copy)
{
TablespaceListCell *orig_cell = original->tablespace_mapping.head;
for (orig_cell = config_file_options.tablespace_mapping.head; orig_cell; orig_cell = orig_cell->next)
{
TablespaceListCell *copy_cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
strncpy(copy_cell->old_dir, orig_cell->old_dir, sizeof(copy_cell->old_dir));
strncpy(copy_cell->new_dir, orig_cell->new_dir, sizeof(copy_cell->new_dir));
if (copy->tablespace_mapping.tail)
copy->tablespace_mapping.tail->next = copy_cell;
else
copy->tablespace_mapping.head = copy_cell;
copy->tablespace_mapping.tail = copy_cell;
}
}
static void
tablespace_list_free(t_configuration_options *options)
{
TablespaceListCell *cell = NULL;
TablespaceListCell *next_cell = NULL;
cell = options->tablespace_mapping.head;
while (cell != NULL)
{
next_cell = cell->next;
pfree(cell);
cell = next_cell;
}
options->tablespace_mapping.head = NULL;
options->tablespace_mapping.tail = NULL;
}
bool
modify_auto_conf(const char *data_dir, KeyValueList *items)
{
PQExpBufferData auto_conf;
PQExpBufferData auto_conf_tmp;
PQExpBufferData auto_conf_contents;
FILE *fp;
mode_t um;
struct stat data_dir_st;
KeyValueList config = {NULL, NULL};
KeyValueListCell *cell = NULL;
bool success = true;
initPQExpBuffer(&auto_conf);
appendPQExpBuffer(&auto_conf, "%s/%s",
data_dir, PG_AUTOCONF_FILENAME);
success = ProcessPostgresConfigFile(auto_conf.data,
NULL,
false, /* we don't care if the file does not exist */
&config,
NULL,
NULL);
if (success == false)
{
fprintf(stderr, "unable to process \"%s\"\n",
auto_conf.data);
termPQExpBuffer(&auto_conf);
return false;
}
/*
* Append requested items to any items extracted from the existing file.
*/
for (cell = items->head; cell; cell = cell->next)
{
key_value_list_replace_or_set(&config,
cell->key,
cell->value);
}
initPQExpBuffer(&auto_conf_tmp);
appendPQExpBuffer(&auto_conf_tmp, "%s.tmp",
auto_conf.data);
initPQExpBuffer(&auto_conf_contents);
/*
* Keep this in sync with src/backend/utils/misc/guc.c:write_auto_conf_file()
*/
appendPQExpBufferStr(&auto_conf_contents,
"# Do not edit this file manually!\n"
"# It will be overwritten by the ALTER SYSTEM command.\n");
for (cell = config.head; cell; cell = cell->next)
{
appendPQExpBuffer(&auto_conf_contents,
"%s = '%s'\n",
cell->key, cell->value);
}
/* stat the data directory for the file mode */
if (stat(data_dir, &data_dir_st) != 0)
{
/*
* This is highly unlikely to happen, but if it does (e.g. freak
* race condition with some rogue process which is messing about
* with the data directory), there's not a lot we can do.
*/
log_error(_("error encountered when checking \"%s\""),
data_dir);
log_detail("%s", strerror(errno));
exit(ERR_BAD_CONFIG);
}
/*
* Set umask so the temporary file is created in the same mode as the data
* directory. In PostgreSQL 11 and later this can be 0700 or 0750.
*/
um = umask(~(data_dir_st.st_mode));
fp = fopen(auto_conf_tmp.data, "w");
umask(um);
if (fp == NULL)
{
fprintf(stderr, "unable to open \"%s\" for writing: %s\n",
auto_conf_tmp.data,
strerror(errno));
success = false;
}
else
{
if (fwrite(auto_conf_contents.data, strlen(auto_conf_contents.data), 1, fp) != 1)
{
fprintf(stderr, "unable to write to \"%s\": %s\n",
auto_conf_tmp.data,
strerror(errno));
fclose(fp);
success = false;
}
else
{
fclose(fp);
/*
* Note: durable_rename() is not exposed to frontend code before Pg 10.
* We only really need to be modifying postgresql.auto.conf from Pg 12,
* but provide backwards compatibility for Pg 9.6 and earlier for the
* (unlikely) event that a repmgr built against one of those versions
* is being used against Pg 12 and later.
*/
#if (PG_ACTUAL_VERSION_NUM >= 100000)
if (durable_rename(auto_conf_tmp.data, auto_conf.data, LOG) != 0)
{
success = false;
}
#else
if (rename(auto_conf_tmp.data, auto_conf.data) < 0)
{
success = false;
}
#endif
}
}
termPQExpBuffer(&auto_conf);
termPQExpBuffer(&auto_conf_tmp);
termPQExpBuffer(&auto_conf_contents);
key_value_list_free(&config);
return success;
}
/*
* parse_event_notifications_list()
*
*
*/
static void
parse_event_notifications_list(EventNotificationList *event_notifications, const char *arg)
{
const char *arg_ptr = NULL;
char event_type_buf[MAXLEN] = "";
char *dst_ptr = event_type_buf;
for (arg_ptr = arg; arg_ptr <= (arg + strlen(arg)); arg_ptr++)
{
/* ignore whitespace */
if (*arg_ptr == ' ' || *arg_ptr == '\t')
{
continue;
}
/*
* comma (or end-of-string) should mark the end of an event type -
* just as long as there was something preceding it
*/
if ((*arg_ptr == ',' || *arg_ptr == '\0') && event_type_buf[0] != '\0')
{
EventNotificationListCell *cell;
cell = (EventNotificationListCell *) pg_malloc0(sizeof(EventNotificationListCell));
if (cell == NULL)
{
log_error(_("unable to allocate memory; terminating"));
exit(ERR_BAD_CONFIG);
}
strncpy(cell->event_type, event_type_buf, MAXLEN);
if (event_notifications->tail)
{
event_notifications->tail->next = cell;
}
else
{
event_notifications->head = cell;
}
event_notifications->tail = cell;
memset(event_type_buf, 0, MAXLEN);
dst_ptr = event_type_buf;
}
/* ignore duplicated commas */
else if (*arg_ptr == ',')
{
continue;
}
else
{
*dst_ptr++ = *arg_ptr;
}
}
}
static void
clear_event_notification_list(EventNotificationList *event_notifications)
{
if (event_notifications->head != NULL)
{
EventNotificationListCell *cell;
EventNotificationListCell *next_cell;
cell = event_notifications->head;
while (cell != NULL)
{
next_cell = cell->next;
pfree(cell);
cell = next_cell;
}
}
event_notifications->head = NULL;
event_notifications->tail = NULL;
}
int
parse_output_to_argv(const char *string, char ***argv_array)
{
int options_len = 0;
char *options_string = NULL;
char *options_string_ptr = NULL;
int c = 1,
argc_item = 1;
char *argv_item = NULL;
char **local_argv_array = NULL;
ItemListCell *cell;
/*
* Add parsed options to this list, then copy to an array to pass to
* getopt
*/
ItemList option_argv = {NULL, NULL};
options_len = strlen(string) + 1;
options_string = pg_malloc0(options_len);
options_string_ptr = options_string;
/* Copy the string before operating on it with strtok() */
strncpy(options_string, string, options_len);
/* Extract arguments into a list and keep a count of the total */
while ((argv_item = strtok(options_string_ptr, " ")) != NULL)
{
item_list_append(&option_argv, trim(argv_item));
argc_item++;
if (options_string_ptr != NULL)
options_string_ptr = NULL;
}
pfree(options_string);
/*
* Array of argument values to pass to getopt_long - this will need to
* include an empty string as the first value (normally this would be the
* program name)
*/
local_argv_array = pg_malloc0(sizeof(char *) * (argc_item + 2));
/* Insert a blank dummy program name at the start of the array */
local_argv_array[0] = pg_malloc0(1);
/*
* Copy the previously extracted arguments from our list to the array
*/
for (cell = option_argv.head; cell; cell = cell->next)
{
int argv_len = strlen(cell->string) + 1;
local_argv_array[c] = (char *)pg_malloc0(argv_len);
strncpy(local_argv_array[c], cell->string, argv_len);
c++;
}
local_argv_array[c] = NULL;
item_list_free(&option_argv);
*argv_array = local_argv_array;
return argc_item;
}
void
free_parsed_argv(char ***argv_array)
{
char **local_argv_array = *argv_array;
int i = 0;
while (local_argv_array[i] != NULL)
{
pfree((char *)local_argv_array[i]);
i++;
}
pfree((char **)local_argv_array);
*argv_array = NULL;
}
bool
parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options, int server_version_num, ItemList *error_list)
{
bool backup_options_ok = true;
int c = 0,
argc_item = 0;
char **argv_array = NULL;
int optindex = 0;
struct option *long_options = NULL;
/*
* We're only interested in these options.
*/
static struct option long_options_10[] =
{
{"slot", required_argument, NULL, 'S'},
{"wal-method", required_argument, NULL, 'X'},
{"waldir", required_argument, NULL, 1},
{"no-slot", no_argument, NULL, 2},
{NULL, 0, NULL, 0}
};
/*
* Pre-PostgreSQL 10 options
*/
static struct option long_options_legacy[] =
{
{"slot", required_argument, NULL, 'S'},
{"xlog-method", required_argument, NULL, 'X'},
{"xlogdir", required_argument, NULL, 1},
{NULL, 0, NULL, 0}
};
/* Don't attempt to tokenise an empty string */
if (!strlen(pg_basebackup_options))
return backup_options_ok;
if (server_version_num >= 100000)
long_options = long_options_10;
else
long_options = long_options_legacy;
argc_item = parse_output_to_argv(pg_basebackup_options, &argv_array);
/* Reset getopt's optind variable */
optind = 0;
/* Prevent getopt from emitting errors */
opterr = 0;
while ((c = getopt_long(argc_item, argv_array, "S:X:", long_options,
&optindex)) != -1)
{
switch (c)
{
case 'S':
strncpy(backup_options->slot, optarg, MAXLEN);
break;
case 'X':
strncpy(backup_options->wal_method, optarg, MAXLEN);
break;
case 1:
strncpy(backup_options->waldir, optarg, MAXPGPATH);
break;
case 2:
backup_options->no_slot = true;
break;
case '?':
if (server_version_num >= 100000 && optopt == 2)
{
if (error_list != NULL)
{
item_list_append(error_list, "invalid use of --no-slot");
}
backup_options_ok = false;
}
break;
}
}
if (backup_options->no_slot == true && backup_options->slot[0] != '\0')
{
if (error_list != NULL)
{
item_list_append(error_list, "--no-slot cannot be used with -S/--slot");
}
backup_options_ok = false;
}
/*
* If --waldir/--xlogdir provided, check it's an absolute path.
*/
if (backup_options->waldir[0] != '\0')
{
canonicalize_path(backup_options->waldir);
if (!is_absolute_path(backup_options->waldir))
{
if (error_list != NULL)
{
item_list_append_format(error_list,
"--%s must be provided with an absolute path",
server_version_num >= 100000 ? "waldir" : "xlogdir");
}
backup_options_ok = false;
}
}
free_parsed_argv(&argv_array);
return backup_options_ok;
}
const char *
print_replication_type(ReplicationType type)
{
switch (type)
{
case REPLICATION_TYPE_PHYSICAL:
return "physical";
}
/* should never reach here */
return "UNKNOWN";
}
const char *
print_connection_check_type(ConnectionCheckType type)
{
switch (type)
{
case CHECK_PING:
return "ping";
case CHECK_QUERY:
return "query";
case CHECK_CONNECTION:
return "connection";
}
/* should never reach here */
return "UNKNOWN";
}
char *
print_event_notification_list(EventNotificationList *list)
{
PQExpBufferData buf;
char *ptr;
EventNotificationListCell *cell;
int ptr_len;
initPQExpBuffer(&buf);
cell = list->head;
while (cell != NULL)
{
appendPQExpBufferStr(&buf, cell->event_type);
if (cell->next)
appendPQExpBufferChar(&buf, ',');
cell = cell->next;
}
ptr_len = strlen(buf.data);
ptr = palloc0(ptr_len + 1);
strncpy(ptr, buf.data, ptr_len);
termPQExpBuffer(&buf);
return ptr;
}
char *
print_tablespace_mapping(TablespaceList *tablespace_mapping)
{
TablespaceListCell *cell;
bool first = true;
PQExpBufferData buf;
char *ptr;
initPQExpBuffer(&buf);
for (cell = tablespace_mapping->head; cell; cell = cell->next)
{
if (first == true)
first = false;
else
appendPQExpBufferChar(&buf, ',');
appendPQExpBuffer(&buf, "%s=%s",
cell->old_dir, cell->new_dir);
}
ptr = palloc0(strlen(buf.data) + 1);
strncpy(ptr, buf.data, strlen(buf.data));
termPQExpBuffer(&buf);
return ptr;
}
const char *
format_failover_mode(failover_mode_opt failover)
{
switch (failover)
{
case FAILOVER_MANUAL:
return "manual";
case FAILOVER_AUTOMATIC:
return "automatic";
default:
return "unknown failover mode";
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors/repmgr.git
git@gitee.com:mirrors/repmgr.git
mirrors
repmgr
repmgr
master

搜索帮助