1 Star 4 Fork 4

HighgoSoftware / hg_repmgr

Create your Gitee Account
Explore and code with more than 12 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
Clone or Download
repmgr-client.c 84.01 KB
Copy Edit Raw Blame History
movead authored 2020-04-20 09:32 . change some built item
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408
/*
* repmgr-client.c - Command interpreter for the repmgr package
*
* Copyright (c) 2009-2020, HighGo Software Co.,Ltd.
*
* This module is a command-line utility to easily setup a cluster of
* hot standby servers for an HA environment
*
* Commands implemented are:
*
* [ PRIMARY | MASTER ] REGISTER
* [ PRIMARY | MASTER ] UNREGISTER
*
* STANDBY CLONE
* STANDBY REGISTER
* STANDBY UNREGISTER
* STANDBY PROMOTE
* STANDBY FOLLOW
* STANDBY SWITCHOVER
*
* BDR REGISTER
* BDR UNREGISTER
*
* CLUSTER SHOW
* CLUSTER EVENT
* CLUSTER CROSSCHECK
* CLUSTER MATRIX
* CLUSTER CLEANUP
*
* NODE STATUS
* NODE CHECK
* NODE REJOIN
* NODE SERVICE
* NODE CONTROL
*
* DAEMON STATUS
* DAEMON PAUSE
* DAEMON UNPAUSE
* DAEMON START
* DAEMON STOP
*
* 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 <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include "repmgr.h"
#include "compat.h"
#include "controldata.h"
#include "repmgr-client.h"
#include "repmgr-client-global.h"
#include "repmgr-action-primary.h"
#include "repmgr-action-standby.h"
#include "repmgr-action-witness.h"
#include "repmgr-action-bdr.h"
#include "repmgr-action-node.h"
#include "repmgr-action-cluster.h"
#include "repmgr-action-daemon.h"
#include <storage/fd.h> /* for PG_TEMP_FILE_PREFIX */
/* globally available variables *
* ============================ */
t_runtime_options runtime_options = T_RUNTIME_OPTIONS_INITIALIZER;
t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
/* conninfo params for the node we're operating on */
t_conninfo_param_list source_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
bool config_file_required = true;
char pg_bindir[MAXLEN] = "";
char path_buf[MAXLEN] = "";
/*
* if --node-id/--node-name provided, place that node's record here
* for later use
*/
t_node_info target_node_info = T_NODE_INFO_INITIALIZER;
/* Collate command line errors and warnings here for friendlier reporting */
static ItemList cli_errors = {NULL, NULL};
static ItemList cli_warnings = {NULL, NULL};
int
main(int argc, char **argv)
{
t_conninfo_param_list default_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
int optindex = 0;
int c;
char *repmgr_command = NULL;
char *repmgr_action = NULL;
bool valid_repmgr_command_found = true;
int action = NO_ACTION;
char *dummy_action = "";
bool help_option = false;
bool option_error_found = false;
set_progname(argv[0]);
/*
* Tell the logger we're a command-line program - this will ensure any
* output logged before the logger is initialized will be formatted
* correctly. Can be overriden with "--log-to-file".
*/
logger_output_mode = OM_COMMAND_LINE;
/*
* Initialize and pre-populate conninfo parameters; these will be
* overwritten if matching command line parameters are provided.
*
* Only some actions will need these, but we need to do this before the
* command line is parsed.
*
* Note: PQconndefaults() does not provide a default value for "dbname",
* but if none is provided will default to "username" when the connection
* is made. We won't set "dbname" here if no default available, as that
* would break the libpq behaviour if non-default username is provided.
*/
initialize_conninfo_params(&default_conninfo, true);
for (c = 0; c < default_conninfo.size && default_conninfo.keywords[c]; c++)
{
if (strcmp(default_conninfo.keywords[c], "host") == 0 &&
(default_conninfo.values[c] != NULL))
{
strncpy(runtime_options.host, default_conninfo.values[c], MAXLEN);
}
else if (strcmp(default_conninfo.keywords[c], "hostaddr") == 0 &&
(default_conninfo.values[c] != NULL))
{
strncpy(runtime_options.host, default_conninfo.values[c], MAXLEN);
}
else if (strcmp(default_conninfo.keywords[c], "port") == 0 &&
(default_conninfo.values[c] != NULL))
{
strncpy(runtime_options.port, default_conninfo.values[c], MAXLEN);
}
else if (strcmp(default_conninfo.keywords[c], "dbname") == 0 &&
(default_conninfo.values[c] != NULL))
{
strncpy(runtime_options.dbname, default_conninfo.values[c], MAXLEN);
}
else if (strcmp(default_conninfo.keywords[c], "user") == 0 &&
(default_conninfo.values[c] != NULL))
{
strncpy(runtime_options.username, default_conninfo.values[c], MAXLEN);
}
}
free_conninfo_params(&default_conninfo);
initialize_conninfo_params(&source_conninfo, false);
/* set default user for -R/--remote-user */
{
struct passwd *pw = NULL;
pw = getpwuid(geteuid());
if (pw == NULL)
{
fprintf(stderr, _("could not get current user name: %s\n"), strerror(errno));
exit(ERR_BAD_CONFIG);
}
strncpy(runtime_options.username, pw->pw_name, MAXLEN);
}
/* Make getopt emitting errors */
opterr = 1;
while ((c = getopt_long(argc, argv, "?Vb:f:FwWd:h:p:U:R:S:D:ck:L:qtvC:", long_options,
&optindex)) != -1)
{
/*
* NOTE: some integer parameters (e.g. -p/--port) are stored
* internally as strings. We use repmgr_atoi() to check these but
* discard the returned integer; repmgr_atoi() will append the error
* message to the provided list.
*/
switch (c)
{
/*
* Options which cause repmgr to exit in this block; these are
* the only ones which can be executed as root user
*/
case OPT_HELP: /* --help */
help_option = true;
break;
/* -V/--version */
case 'V':
/*
* in contrast to repmgr3 and earlier, we only display the
* repmgr version as it's not specific to a particular
* PostgreSQL version
*/
printf("%s %s\n", progname(), REPMGR_VERSION);
exit(SUCCESS);
/* --version-number */
case OPT_VERSION_NUMBER:
printf("%i\n", REPMGR_VERSION_NUM);
exit(SUCCESS);
/*------------------------------
* general configuration options
*------------------------------
*/
/* -b/--pg_bindir */
case 'b':
strncpy(runtime_options.pg_bindir, optarg, MAXLEN);
break;
/* -f/--config-file */
case 'f':
strncpy(runtime_options.config_file, optarg, MAXLEN);
break;
/* --dry-run */
case OPT_DRY_RUN:
runtime_options.dry_run = true;
break;
/* -F/--force */
case 'F':
runtime_options.force = true;
break;
/* --replication-user (primary/standby register only) */
case OPT_REPLICATION_USER:
strncpy(runtime_options.replication_user, optarg, MAXLEN);
break;
/* -w/--wait */
case 'w':
runtime_options.wait_provided = true;
if (optarg != NULL)
{
runtime_options.wait = repmgr_atoi(optarg, "--wait", &cli_errors, 0);
}
break;
/* -W/--no-wait */
case 'W':
runtime_options.no_wait = true;
break;
/* --compact */
case OPT_COMPACT:
runtime_options.compact = true;
break;
/*----------------------------
* database connection options
*----------------------------
*/
/*
* These are the standard database connection options; with
* the exception of -d/--dbname (which could be a conninfo
* string) we'll also set these values in "source_conninfo"
* (overwriting preset values from environment variables). XXX
* check this is same as psql
*/
/* -d/--dbname */
case 'd':
strncpy(runtime_options.dbname, optarg, MAXLEN);
/*
* dbname will be set in source_conninfo later after checking
* if it's a conninfo string
*/
runtime_options.connection_param_provided = true;
break;
/* -h/--host */
case 'h':
strncpy(runtime_options.host, optarg, MAXLEN);
param_set(&source_conninfo, "host", optarg);
runtime_options.connection_param_provided = true;
runtime_options.host_param_provided = true;
break;
case 'p':
/*
* minimum TCP port number is 1; in practice PostgreSQL
* won't be running on a privileged port, but we don't want
* to be concerned with that level of checking
*/
(void) repmgr_atoi(optarg, "-p/--port", &cli_errors, 1);
param_set(&source_conninfo, "port", optarg);
strncpy(runtime_options.port,
optarg,
MAXLEN);
runtime_options.connection_param_provided = true;
break;
/* -U/--user */
case 'U':
strncpy(runtime_options.username, optarg, MAXLEN);
param_set(&source_conninfo, "user", optarg);
runtime_options.connection_param_provided = true;
break;
/*-------------------------
* other connection options
*-------------------------
*/
/* -R/--remote_user */
case 'R':
strncpy(runtime_options.remote_user, optarg, MAXLEN);
break;
/* -S/--superuser */
case 'S':
strncpy(runtime_options.superuser, optarg, MAXLEN);
break;
/*-------------
* node options
*-------------
*/
/* -D/--pgdata/--data-dir */
case 'D':
strncpy(runtime_options.data_dir, optarg, MAXPGPATH);
break;
/* --node-id */
case OPT_NODE_ID:
runtime_options.node_id = repmgr_atoi(optarg, "--node-id", &cli_errors, MIN_NODE_ID);
break;
/* --node-name */
case OPT_NODE_NAME:
{
if (strlen(optarg) < sizeof(runtime_options.node_name))
strncpy(runtime_options.node_name, optarg, sizeof(runtime_options.node_name));
else
item_list_append_format(&cli_errors,
_("value for \"--node-name\" must contain fewer than %lu characters"),
sizeof(runtime_options.node_name));
break;
}
/* --remote-node-id */
case OPT_REMOTE_NODE_ID:
runtime_options.remote_node_id = repmgr_atoi(optarg, "--remote-node-id", &cli_errors, MIN_NODE_ID);
break;
/*
* standby options * ---------------
*/
/* --upstream-node-id */
case OPT_UPSTREAM_NODE_ID:
runtime_options.upstream_node_id = repmgr_atoi(optarg, "--upstream-node-id", &cli_errors, MIN_NODE_ID);
break;
/*------------------------
* "standby clone" options
*------------------------
*/
/* -c/--fast-checkpoint */
case 'c':
runtime_options.fast_checkpoint = true;
break;
/* --copy-external-config-files(=[samepath|pgdata]) */
case OPT_COPY_EXTERNAL_CONFIG_FILES:
runtime_options.copy_external_config_files = true;
if (optarg != NULL)
{
if (strcmp(optarg, "samepath") == 0)
{
runtime_options.copy_external_config_files_destination = CONFIG_FILE_SAMEPATH;
}
/* allow "data_directory" as synonym for "pgdata" */
else if (strcmp(optarg, "pgdata") == 0 || strcmp(optarg, "data_directory") == 0)
{
runtime_options.copy_external_config_files_destination = CONFIG_FILE_PGDATA;
}
else
{
item_list_append(&cli_errors,
_("value provided for \"--copy-external-config-files\" must be \"samepath\" or \"pgdata\""));
}
}
break;
/* --no-upstream-connection */
case OPT_NO_UPSTREAM_CONNECTION:
runtime_options.no_upstream_connection = true;
break;
case OPT_UPSTREAM_CONNINFO:
strncpy(runtime_options.upstream_conninfo, optarg, MAXLEN);
break;
case OPT_WITHOUT_BARMAN:
runtime_options.without_barman = true;
break;
case OPT_RECOVERY_CONF_ONLY:
runtime_options.recovery_conf_only = true;
break;
/*---------------------------
* "standby register" options
*---------------------------
*/
case OPT_WAIT_START:
runtime_options.wait_start = repmgr_atoi(optarg, "--wait-start", &cli_errors, 0);
break;
case OPT_WAIT_SYNC:
runtime_options.wait_register_sync = true;
if (optarg != NULL)
{
runtime_options.wait_register_sync_seconds = repmgr_atoi(optarg, "--wait-sync", &cli_errors, 0);
}
break;
/*-----------------------------
* "standby switchover" options
*-----------------------------
*/
case OPT_ALWAYS_PROMOTE:
runtime_options.always_promote = true;
break;
case OPT_FORCE_REWIND:
runtime_options.force_rewind_used = true;
if (optarg != NULL)
{
strncpy(runtime_options.force_rewind_path, optarg, MAXPGPATH);
}
break;
case OPT_SIBLINGS_FOLLOW:
runtime_options.siblings_follow = true;
break;
case OPT_REPMGRD_NO_PAUSE:
runtime_options.repmgrd_no_pause = true;
break;
/*----------------------
* "node status" options
*----------------------
*/
case OPT_IS_SHUTDOWN_CLEANLY:
runtime_options.is_shutdown_cleanly = true;
break;
/*---------------------
* "node check" options
*--------------------
*/
case OPT_ARCHIVE_READY:
runtime_options.archive_ready = true;
break;
case OPT_DOWNSTREAM:
runtime_options.downstream = true;
break;
case OPT_REPLICATION_LAG:
runtime_options.replication_lag = true;
break;
case OPT_ROLE:
runtime_options.role = true;
break;
case OPT_SLOTS:
runtime_options.slots = true;
break;
case OPT_MISSING_SLOTS:
runtime_options.missing_slots = true;
break;
case OPT_HAS_PASSFILE:
runtime_options.has_passfile = true;
break;
case OPT_REPL_CONN:
runtime_options.replication_connection = true;
break;
case OPT_DATA_DIRECTORY_CONFIG:
runtime_options.data_directory_config = true;
break;
/*--------------------
* "node rejoin" options
*--------------------
*/
case OPT_CONFIG_FILES:
strncpy(runtime_options.config_files, optarg, MAXLEN);
break;
case OPT_CONFIG_ARCHIVE_DIR:
/* TODO: check this is an absolute path */
strncpy(runtime_options.config_archive_dir, optarg, MAXPGPATH);
break;
/*-----------------------
* "node service" options
*-----------------------
*/
/* --action (repmgr node service --action) */
case OPT_ACTION:
strncpy(runtime_options.action, optarg, MAXLEN);
break;
case OPT_LIST_ACTIONS:
runtime_options.list_actions = true;
break;
case OPT_CHECKPOINT:
runtime_options.checkpoint = true;
break;
/*------------------------
* "cluster event" options
*------------------------
*/
case OPT_EVENT:
strncpy(runtime_options.event, optarg, MAXLEN);
break;
case OPT_LIMIT:
runtime_options.limit = repmgr_atoi(optarg, "--limit", &cli_errors, 1);
runtime_options.limit_provided = true;
break;
case OPT_ALL:
runtime_options.all = true;
break;
/*------------------------
* "cluster cleanup" options
*------------------------
*/
/* -k/--keep-history */
case 'k':
runtime_options.keep_history = repmgr_atoi(optarg, "-k/--keep-history", &cli_errors, 0);
break;
/*----------------
* logging options
*----------------
*/
/* -L/--log-level */
case 'L':
{
int detected_log_level = detect_log_level(optarg);
if (detected_log_level != -1)
{
strncpy(runtime_options.log_level, optarg, MAXLEN);
}
else
{
PQExpBufferData invalid_log_level;
initPQExpBuffer(&invalid_log_level);
appendPQExpBuffer(&invalid_log_level, _("invalid log level \"%s\" provided"), optarg);
item_list_append(&cli_errors, invalid_log_level.data);
termPQExpBuffer(&invalid_log_level);
}
break;
}
/* --log-to-file */
case OPT_LOG_TO_FILE:
runtime_options.log_to_file = true;
logger_output_mode = OM_DAEMON;
break;
/* --quiet */
case 'q':
runtime_options.quiet = true;
break;
/* --terse */
case 't':
runtime_options.terse = true;
break;
/* --verbose */
case 'v':
runtime_options.verbose = true;
break;
/*---------------
* output options
*---------------
*/
case OPT_CSV:
runtime_options.csv = true;
break;
case OPT_NAGIOS:
runtime_options.nagios = true;
break;
case OPT_OPTFORMAT:
runtime_options.optformat = true;
break;
/*---------------------------------
* undocumented options for testing
*----------------------------------
*/
case OPT_DISABLE_WAL_RECEIVER:
runtime_options.disable_wal_receiver = true;
break;
case OPT_ENABLE_WAL_RECEIVER:
runtime_options.enable_wal_receiver = true;
break;
/*-----------------------------
* options deprecated since 3.3
*-----------------------------
*/
case OPT_CHECK_UPSTREAM_CONFIG:
item_list_append(&cli_warnings,
_("--check-upstream-config is deprecated; use --dry-run instead"));
break;
case OPT_DATA_DIR:
item_list_append(&cli_warnings,
_("--data-dir is deprecated; use -D/--pgdata instead"));
break;
case OPT_NO_CONNINFO_PASSWORD:
item_list_append(&cli_warnings,
_("--no-conninfo-password is deprecated; use --use-recovery-conninfo-password to explicitly set a password"));
break;
/* -C/--remote-config-file */
case 'C':
item_list_append(&cli_warnings,
_("--remote-config-file is no longer required"));
break;
/* --recovery-min-apply-delay */
case OPT_RECOVERY_MIN_APPLY_DELAY:
item_list_append(&cli_warnings,
_("--recovery-min-apply-delay is now a configuration file parameter, \"recovery_min_apply_delay\""));
break;
case ':': /* missing option argument */
option_error_found = true;
break;
case '?':
/* Actual help option given? */
if (strcmp(argv[optind - 1], "-?") == 0)
{
help_option = true;
break;
}
/* otherwise fall through to default */
default: /* invalid option */
option_error_found = true;
break;
}
}
/*
* If -d/--dbname appears to be a conninfo string, validate by attempting
* to parse it (and if successful, store the parsed parameters)
*/
if (runtime_options.dbname[0])
{
if (strncmp(runtime_options.dbname, "postgresql://", 13) == 0 ||
strncmp(runtime_options.dbname, "postgres://", 11) == 0 ||
strchr(runtime_options.dbname, '=') != NULL)
{
char *errmsg = NULL;
PQconninfoOption *opts;
runtime_options.conninfo_provided = true;
opts = PQconninfoParse(runtime_options.dbname, &errmsg);
if (opts == NULL)
{
PQExpBufferData conninfo_error;
initPQExpBuffer(&conninfo_error);
appendPQExpBuffer(&conninfo_error, _("error parsing conninfo:\n%s"), errmsg);
item_list_append(&cli_errors, conninfo_error.data);
termPQExpBuffer(&conninfo_error);
pfree(errmsg);
}
else
{
/*
* Store any parameters provided in the conninfo string in our
* internal array; also overwrite any options set in
* runtime_options.(host|port|username), as the conninfo
* settings take priority
*/
PQconninfoOption *opt;
for (opt = opts; opt->keyword != NULL; opt++)
{
if (opt->val != NULL && opt->val[0] != '\0')
{
param_set(&source_conninfo, opt->keyword, opt->val);
}
if (strcmp(opt->keyword, "host") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.host, opt->val, MAXLEN);
runtime_options.host_param_provided = true;
}
if (strcmp(opt->keyword, "hostaddr") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.host, opt->val, MAXLEN);
runtime_options.host_param_provided = true;
}
else if (strcmp(opt->keyword, "port") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.port, opt->val, MAXLEN);
}
else if (strcmp(opt->keyword, "user") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.username, opt->val, MAXLEN);
}
}
PQconninfoFree(opts);
}
}
else
{
param_set(&source_conninfo, "dbname", runtime_options.dbname);
}
}
/*
* Disallow further running as root to prevent directory ownership
* problems. We check this here to give the root user a chance to execute
* --help/--version options.
*/
/* # HG_repmgr: allow root start
if (geteuid() == 0 && help_option == false)
{
fprintf(stderr,
_("%s: cannot be run as root\n"
"Please log in (using, e.g., \"su\") as the "
"(unprivileged) user that owns "
"the data directory.\n"
),
progname());
free_conninfo_params(&source_conninfo);
exit(ERR_BAD_CONFIG);
}*/
/* Exit here already if errors in command line options found */
if (cli_errors.head != NULL)
{
free_conninfo_params(&source_conninfo);
exit_with_cli_errors(&cli_errors, NULL);
}
/*----------
* Determine the node type and action; following are valid:
*
* { PRIMARY | MASTER } REGISTER |
* STANDBY { REGISTER | UNREGISTER | CLONE [node] | PROMOTE | FOLLOW [node] | SWITCHOVER } |
* WITNESS { CREATE | REGISTER | UNREGISTER }
* BDR { REGISTER | UNREGISTER } |
* NODE { STATUS | CHECK | REJOIN | SERVICE } |
* CLUSTER { CROSSCHECK | MATRIX | SHOW | EVENT | CLEANUP }
* DAEMON { STATUS | PAUSE | UNPAUSE | START | STOP }
*
* [node] is an optional hostname, provided instead of the -h/--host
* option
* ---------
*/
if (optind < argc)
{
repmgr_command = argv[optind++];
}
if (optind < argc)
{
repmgr_action = argv[optind++];
}
else
{
repmgr_action = dummy_action;
}
if (repmgr_command != NULL)
{
if (strcasecmp(repmgr_command, "PRIMARY") == 0 || strcasecmp(repmgr_command, "MASTER") == 0)
{
if (help_option == true)
{
do_primary_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "REGISTER") == 0)
action = PRIMARY_REGISTER;
else if (strcasecmp(repmgr_action, "UNREGISTER") == 0)
action = PRIMARY_UNREGISTER;
/* allow "primary check"/"primary status" as aliases for "node check"/"node status" */
else if (strcasecmp(repmgr_action, "CHECK") == 0)
action = NODE_CHECK;
else if (strcasecmp(repmgr_action, "STATUS") == 0)
action = NODE_STATUS;
}
else if (strcasecmp(repmgr_command, "STANDBY") == 0)
{
if (help_option == true)
{
do_standby_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "CLONE") == 0)
action = STANDBY_CLONE;
else if (strcasecmp(repmgr_action, "REGISTER") == 0)
action = STANDBY_REGISTER;
else if (strcasecmp(repmgr_action, "UNREGISTER") == 0)
action = STANDBY_UNREGISTER;
else if (strcasecmp(repmgr_action, "PROMOTE") == 0)
action = STANDBY_PROMOTE;
else if (strcasecmp(repmgr_action, "FOLLOW") == 0)
action = STANDBY_FOLLOW;
else if (strcasecmp(repmgr_action, "SWITCHOVER") == 0)
action = STANDBY_SWITCHOVER;
/* allow "standby check"/"standby status" as aliases for "node check"/"node status" */
else if (strcasecmp(repmgr_action, "CHECK") == 0)
action = NODE_CHECK;
else if (strcasecmp(repmgr_action, "STATUS") == 0)
action = NODE_STATUS;
}
else if (strcasecmp(repmgr_command, "WITNESS") == 0)
{
if (help_option == true)
{
do_witness_help();
exit(SUCCESS);
}
else if (strcasecmp(repmgr_action, "REGISTER") == 0)
action = WITNESS_REGISTER;
else if (strcasecmp(repmgr_action, "UNREGISTER") == 0)
action = WITNESS_UNREGISTER;
}
else if (strcasecmp(repmgr_command, "BDR") == 0)
{
if (help_option == true)
{
do_bdr_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "REGISTER") == 0)
action = BDR_REGISTER;
else if (strcasecmp(repmgr_action, "UNREGISTER") == 0)
action = BDR_UNREGISTER;
else if (strcasecmp(repmgr_action, "CHECK") == 0)
action = NODE_CHECK;
else if (strcasecmp(repmgr_action, "STATUS") == 0)
action = NODE_STATUS;
}
else if (strcasecmp(repmgr_command, "NODE") == 0)
{
if (help_option == true)
{
do_node_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "CHECK") == 0)
action = NODE_CHECK;
else if (strcasecmp(repmgr_action, "STATUS") == 0)
action = NODE_STATUS;
else if (strcasecmp(repmgr_action, "REJOIN") == 0)
action = NODE_REJOIN;
else if (strcasecmp(repmgr_action, "SERVICE") == 0)
action = NODE_SERVICE;
else if (strcasecmp(repmgr_action, "CONTROL") == 0)
action = NODE_CONTROL;
else if (strcasecmp(repmgr_action, "STARTUP") == 0)
action = NODE_STARTUP;
}
else if (strcasecmp(repmgr_command, "CLUSTER") == 0)
{
if (help_option == true)
{
do_cluster_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "SHOW") == 0)
action = CLUSTER_SHOW;
else if (strcasecmp(repmgr_action, "EVENT") == 0)
action = CLUSTER_EVENT;
/* allow "CLUSTER EVENTS" as synonym for "CLUSTER EVENT" */
else if (strcasecmp(repmgr_action, "EVENTS") == 0)
action = CLUSTER_EVENT;
else if (strcasecmp(repmgr_action, "CROSSCHECK") == 0)
action = CLUSTER_CROSSCHECK;
else if (strcasecmp(repmgr_action, "MATRIX") == 0)
action = CLUSTER_MATRIX;
else if (strcasecmp(repmgr_action, "CLEANUP") == 0)
action = CLUSTER_CLEANUP;
}
else if (strcasecmp(repmgr_command, "DAEMON") == 0)
{
if (help_option == true)
{
do_daemon_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "STATUS") == 0)
action = DAEMON_STATUS;
else if (strcasecmp(repmgr_action, "PAUSE") == 0)
action = DAEMON_PAUSE;
else if (strcasecmp(repmgr_action, "UNPAUSE") == 0)
action = DAEMON_UNPAUSE;
else if (strcasecmp(repmgr_action, "START") == 0)
action = DAEMON_START;
else if (strcasecmp(repmgr_action, "STOP") == 0)
action = DAEMON_STOP;
}
else
{
valid_repmgr_command_found = false;
}
}
if (help_option == true)
{
do_help();
exit(SUCCESS);
}
if (action == NO_ACTION)
{
PQExpBufferData command_error;
initPQExpBuffer(&command_error);
if (repmgr_command == NULL)
{
appendPQExpBuffer(&command_error,
_("no repmgr command provided"));
}
else if (valid_repmgr_command_found == false && repmgr_action[0] == '\0')
{
appendPQExpBuffer(&command_error,
_("unknown repmgr command '%s'"),
repmgr_command);
}
else if (repmgr_action[0] == '\0')
{
appendPQExpBuffer(&command_error,
_("no action provided for command '%s'"),
repmgr_command);
}
else
{
appendPQExpBuffer(&command_error,
_("unknown repmgr action '%s %s'"),
repmgr_command,
repmgr_action);
}
item_list_append(&cli_errors, command_error.data);
}
/*
* STANDBY CLONE historically accepts the upstream hostname as an
* additional argument
*/
if (action == STANDBY_CLONE)
{
if (optind < argc)
{
if (runtime_options.host_param_provided == true)
{
PQExpBufferData additional_host_arg;
initPQExpBuffer(&additional_host_arg);
appendPQExpBuffer(&additional_host_arg,
_("host name provided both with %s and as an extra parameter"),
runtime_options.conninfo_provided == true ? "host=" : "-h/--host");
item_list_append(&cli_errors, additional_host_arg.data);
}
else
{
strncpy(runtime_options.host, argv[optind++], MAXLEN);
param_set(&source_conninfo, "host", runtime_options.host);
runtime_options.host_param_provided = true;
}
}
}
if (optind < argc)
{
PQExpBufferData too_many_args;
initPQExpBuffer(&too_many_args);
appendPQExpBuffer(&too_many_args, _("too many command-line arguments (first extra is \"%s\")"), argv[optind]);
item_list_append(&cli_errors, too_many_args.data);
}
/*
* The configuration file is not required for some actions (e.g. 'standby
* clone'), however if available we'll parse it anyway for options like
* 'log_level', 'use_replication_slots' etc.
*/
load_config(runtime_options.config_file,
runtime_options.verbose,
runtime_options.terse,
&config_file_options,
argv[0]);
check_cli_parameters(action);
/*
* Sanity checks for command line parameters completed by now; any further
* errors will be runtime ones
*/
if (cli_errors.head != NULL)
{
free_conninfo_params(&source_conninfo);
exit_with_cli_errors(&cli_errors, valid_repmgr_command_found == true ? repmgr_command : NULL);
}
/* no errors detected by repmgr, but getopt might have */
if (option_error_found == true)
{
if (valid_repmgr_command_found == true)
{
printf(_("Try \"%s --help\" or \"%s %s --help\" for more information.\n"),
progname(),
progname(),
repmgr_command);
}
else
{
printf(_("Try \"repmgr --help\" for more information.\n"));
}
free_conninfo_params(&source_conninfo);
exit(ERR_BAD_CONFIG);
}
/*
* Print any warnings about inappropriate command line options, unless
* -t/--terse set
*/
if (cli_warnings.head != NULL && runtime_options.terse == false)
{
log_warning(_("following problems with command line parameters detected:"));
print_item_list(&cli_warnings);
}
/*
* post-processing following command line parameter checks
* =======================================================
*/
if (runtime_options.csv == true)
{
runtime_options.output_mode = OM_CSV;
}
else if (runtime_options.nagios == true)
{
runtime_options.output_mode = OM_NAGIOS;
}
else if (runtime_options.optformat == true)
{
runtime_options.output_mode = OM_OPTFORMAT;
}
/*
* Check for configuration file items which can be overriden by runtime
* options
* =====================================================================
*/
/*
* Command-line parameter -L/--log-level overrides any setting in config
* file
*/
if (*runtime_options.log_level != '\0')
{
strncpy(config_file_options.log_level, runtime_options.log_level, MAXLEN);
}
/*
* Initialise pg_bindir - command line parameter will override any setting
* in the configuration file
*/
if (!strlen(runtime_options.pg_bindir))
{
strncpy(runtime_options.pg_bindir, config_file_options.pg_bindir, MAXLEN);
}
/* Add trailing slash */
if (strlen(runtime_options.pg_bindir))
{
int len = strlen(runtime_options.pg_bindir);
if (runtime_options.pg_bindir[len - 1] != '/')
{
maxlen_snprintf(pg_bindir, "%s/", runtime_options.pg_bindir);
}
else
{
strncpy(pg_bindir, runtime_options.pg_bindir, MAXLEN);
}
}
/*
* Initialize the logger. We've previously requested STDERR logging only
* to ensure the repmgr command doesn't have its output diverted to a
* logging facility (which usually doesn't make sense for a command line
* program).
*
* If required (e.g. when calling repmgr from repmgrd), this behaviour can
* be overridden with "--log-to-file".
*/
logger_init(&config_file_options, progname());
if (runtime_options.verbose)
logger_set_verbose();
if (runtime_options.terse)
logger_set_terse();
/*
* If --dry-run specified, ensure log_level is at least LOG_INFO, regardless
* of what's in the configuration file or -L/--log-level paremeter, otherwise
* some or output might not be displayed.
*/
if (runtime_options.dry_run == true)
{
logger_set_min_level(LOG_INFO);
}
/*
* If -q/--quiet supplied, suppress any non-ERROR log output.
* This overrides everything else; we'll leave it up to the user to deal with the
* consequences of e.g. running --dry-run together with -q/--quiet.
*/
if (runtime_options.quiet == true)
{
logger_set_level(LOG_ERROR);
}
/*
* Node configuration information is not needed for all actions, with
* STANDBY CLONE being the main exception.
*/
if (config_file_required)
{
/*
* if a configuration file was provided, the configuration file parser
* will already have errored out if no valid node_id found
*/
if (config_file_options.node_id == NODE_NOT_FOUND)
{
free_conninfo_params(&source_conninfo);
log_error(_("no node information was found - please supply a configuration file"));
exit(ERR_BAD_CONFIG);
}
}
/*
* If a node was specified (by --node-id or --node-name), check it exists
* (and pre-populate a record for later use).
*
* At this point check_cli_parameters() will already have determined if
* provision of these is valid for the action, otherwise it unsets them.
*
* We need to check this much later than other command line parameters as
* we need to wait until the configuration file is parsed and we can
* obtain the conninfo string.
*/
if (runtime_options.node_id != UNKNOWN_NODE_ID || runtime_options.node_name[0] != '\0')
{
PGconn *conn = NULL;
RecordStatus record_status = RECORD_NOT_FOUND;
log_verbose(LOG_DEBUG, "connecting to local node to retrieve record for node specified with --node-id or --node-name");
if (strlen(config_file_options.conninfo))
conn = establish_db_connection(config_file_options.conninfo, true);
else
conn = establish_db_connection_by_params(&source_conninfo, true);
if (runtime_options.node_id != UNKNOWN_NODE_ID)
{
record_status = get_node_record(conn, runtime_options.node_id, &target_node_info);
if (record_status != RECORD_FOUND)
{
log_error(_("node %i (specified with --node-id) not found"),
runtime_options.node_id);
PQfinish(conn);
free_conninfo_params(&source_conninfo);
exit(ERR_BAD_CONFIG);
}
}
else if (runtime_options.node_name[0] != '\0')
{
char *escaped = escape_string(conn, runtime_options.node_name);
if (escaped == NULL)
{
log_error(_("unable to escape value provided for --node-name"));
PQfinish(conn);
free_conninfo_params(&source_conninfo);
exit(ERR_BAD_CONFIG);
}
record_status = get_node_record_by_name(conn, escaped, &target_node_info);
pfree(escaped);
if (record_status != RECORD_FOUND)
{
log_error(_("node %s (specified with --node-name) not found"),
runtime_options.node_name);
PQfinish(conn);
free_conninfo_params(&source_conninfo);
exit(ERR_BAD_CONFIG);
}
}
PQfinish(conn);
}
switch (action)
{
/* PRIMARY */
case PRIMARY_REGISTER:
do_primary_register();
break;
case PRIMARY_UNREGISTER:
do_primary_unregister();
break;
/* STANDBY */
case STANDBY_CLONE:
do_standby_clone();
break;
case STANDBY_REGISTER:
do_standby_register();
break;
case STANDBY_UNREGISTER:
do_standby_unregister();
break;
case STANDBY_PROMOTE:
do_standby_promote();
break;
case STANDBY_FOLLOW:
do_standby_follow();
break;
case STANDBY_SWITCHOVER:
do_standby_switchover();
break;
/* WITNESS */
case WITNESS_REGISTER:
do_witness_register();
break;
case WITNESS_UNREGISTER:
do_witness_unregister();
break;
/* BDR */
case BDR_REGISTER:
do_bdr_register();
break;
case BDR_UNREGISTER:
do_bdr_unregister();
break;
/* NODE */
case NODE_STATUS:
do_node_status();
break;
case NODE_CHECK:
do_node_check();
break;
case NODE_REJOIN:
do_node_rejoin();
break;
case NODE_SERVICE:
do_node_service();
break;
case NODE_CONTROL:
do_node_control();
break;
case NODE_STARTUP:
do_node_startup();
break;
/* CLUSTER */
case CLUSTER_SHOW:
do_cluster_show();
break;
case CLUSTER_EVENT:
do_cluster_event();
break;
case CLUSTER_CROSSCHECK:
do_cluster_crosscheck();
break;
case CLUSTER_MATRIX:
do_cluster_matrix();
break;
case CLUSTER_CLEANUP:
do_cluster_cleanup();
break;
/* DAEMON */
case DAEMON_STATUS:
do_daemon_status();
break;
case DAEMON_PAUSE:
do_daemon_pause();
break;
case DAEMON_UNPAUSE:
do_daemon_unpause();
break;
case DAEMON_START:
do_daemon_start();
break;
case DAEMON_STOP:
do_daemon_stop();
break;
default:
/* An action will have been determined by this point */
break;
}
free_conninfo_params(&source_conninfo);
return SUCCESS;
}
/*
* Check for useless or conflicting parameters, and also whether a
* configuration file is required.
*
* Messages will be added to the command line warning and error lists
* as appropriate.
*
* XXX for each individual actions, check only required actions
* for non-required actions check warn if provided
*/
static void
check_cli_parameters(const int action)
{
/*
* ========================================================================
* check all parameters required for an action are provided, and warn
* about ineffective actions
* ========================================================================
*/
switch (action)
{
case PRIMARY_REGISTER:
/* no required parameters */
break;
case STANDBY_CLONE:
{
standy_clone_mode mode = get_standby_clone_mode();
config_file_required = false;
if (mode == barman)
{
if (runtime_options.copy_external_config_files)
{
item_list_append(&cli_warnings,
_("--copy-external-config-files ineffective in Barman mode"));
}
if (runtime_options.fast_checkpoint)
{
item_list_append(&cli_warnings,
_("-c/--fast-checkpoint has no effect in Barman mode"));
}
}
else
{
if (!runtime_options.host_param_provided)
{
item_list_append_format(&cli_errors,
_("host name for the source node must be provided with -h/--host when executing %s"),
action_name(action));
}
if (!runtime_options.connection_param_provided)
{
item_list_append_format(&cli_errors,
_("database connection parameters for the source node must be provided when executing %s"),
action_name(action));
}
/*
* XXX if -D/--pgdata provided, and also
* config_file_options.pgdata, warn -D/--pgdata will be
* ignored
*/
if (*runtime_options.upstream_conninfo)
{
if (*runtime_options.replication_user)
{
item_list_append(&cli_warnings,
_("--replication-user ineffective when specifying --upstream-conninfo"));
}
}
if (runtime_options.no_upstream_connection == true)
{
item_list_append(&cli_warnings,
_("--no-upstream-connection only effective in Barman mode"));
}
}
if (strlen(config_file_options.config_directory))
{
if (runtime_options.copy_external_config_files == false)
{
item_list_append(&cli_warnings,
_("\"config_directory\" set in repmgr.conf, but --copy-external-config-files not provided"));
}
}
}
break;
case STANDBY_FOLLOW:
{
/*
* if `repmgr standby follow` executed with host params,
* ensure data directory was provided
*/
}
break;
case WITNESS_REGISTER:
{
if (!runtime_options.host_param_provided)
{
item_list_append_format(&cli_errors,
_("host name for the source node must be provided with -h/--host when executing %s"),
action_name(action));
}
}
break;
case NODE_CHECK:
if (runtime_options.has_passfile == true)
{
config_file_required = false;
}
break;
case NODE_STATUS:
if (runtime_options.node_id != UNKNOWN_NODE_ID)
{
item_list_append(
&cli_warnings,
"--node-id will be ignored; \"repmgr node status\" can only be executed on the local node");
}
if (runtime_options.node_name[0] != '\0')
{
item_list_append(
&cli_warnings,
"--node-name will be ignored; \"repmgr node status\" can only be executed on the local node");
}
break;
case NODE_REJOIN:
if (runtime_options.connection_param_provided == false)
{
item_list_append(
&cli_errors,
"database connection parameters for an available node must be provided when executing NODE REJOIN");
}
break;
case CLUSTER_SHOW:
case CLUSTER_MATRIX:
case CLUSTER_CROSSCHECK:
if (runtime_options.connection_param_provided)
config_file_required = false;
break;
case CLUSTER_EVENT:
/* no required parameters */
break;
}
/*
* ========================================================================
* warn if parameters provided for an action where they're not relevant
* ========================================================================
*/
/* --host etc. */
if (runtime_options.connection_param_provided)
{
switch (action)
{
case STANDBY_CLONE:
case STANDBY_FOLLOW:
case STANDBY_REGISTER:
case WITNESS_REGISTER:
case WITNESS_UNREGISTER:
case CLUSTER_SHOW:
case CLUSTER_MATRIX:
case CLUSTER_CROSSCHECK:
case NODE_REJOIN:
break;
default:
item_list_append_format(&cli_warnings,
_("database connection parameters not required when executing %s"),
action_name(action));
}
}
/* -D/--pgdata */
if (runtime_options.data_dir[0])
{
switch (action)
{
case STANDBY_CLONE:
case STANDBY_FOLLOW:
case NODE_SERVICE:
break;
default:
item_list_append_format(&cli_warnings,
_("-D/--pgdata not required when executing %s"),
action_name(action));
}
}
/*
* --node-id
*
* NOTE: overrides --node-name, if present
*/
if (runtime_options.node_id != UNKNOWN_NODE_ID)
{
switch (action)
{
case PRIMARY_UNREGISTER:
case STANDBY_UNREGISTER:
case WITNESS_UNREGISTER:
case CLUSTER_CLEANUP:
case CLUSTER_EVENT:
case CLUSTER_MATRIX:
case CLUSTER_CROSSCHECK:
break;
default:
item_list_append_format(&cli_warnings,
_("--node-id not required when executing %s"),
action_name(action));
runtime_options.node_id = UNKNOWN_NODE_ID;
}
}
if (runtime_options.node_name[0])
{
switch (action)
{
case STANDBY_UNREGISTER:
case CLUSTER_EVENT:
if (runtime_options.node_id != UNKNOWN_NODE_ID)
{
item_list_append(&cli_warnings,
_("--node-id provided, ignoring --node-name"));
memset(runtime_options.node_name, 0, sizeof(runtime_options.node_name));
}
break;
default:
item_list_append_format(&cli_warnings,
_("--node-name not required when executing %s"),
action_name(action));
memset(runtime_options.node_name, 0, sizeof(runtime_options.node_name));
}
}
if (runtime_options.upstream_node_id != UNKNOWN_NODE_ID)
{
switch (action)
{
case STANDBY_CLONE:
case STANDBY_REGISTER:
case STANDBY_FOLLOW:
case BDR_REGISTER:
break;
default:
item_list_append_format(&cli_warnings,
_("--upstream-node-id will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.replication_user[0])
{
switch (action)
{
case PRIMARY_REGISTER:
case STANDBY_REGISTER:
case STANDBY_CLONE:
break;
case STANDBY_FOLLOW:
item_list_append_format(&cli_warnings,
_("--replication-user ignored when executing %s"),
action_name(action));
break;
default:
item_list_append_format(&cli_warnings,
_("--replication-user not required when executing %s"),
action_name(action));
}
}
if (runtime_options.recovery_conf_only == true)
{
switch (action)
{
case STANDBY_CLONE:
break;
default:
item_list_append_format(&cli_warnings,
_("--create-recovery-conf will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.event[0])
{
switch (action)
{
case CLUSTER_EVENT:
break;
default:
item_list_append_format(&cli_warnings,
_("--event not required when executing %s"),
action_name(action));
}
}
if (runtime_options.limit_provided)
{
switch (action)
{
case CLUSTER_EVENT:
break;
default:
item_list_append_format(&cli_warnings,
_("--limit not required when executing %s"),
action_name(action));
}
}
if (runtime_options.all)
{
switch (action)
{
case CLUSTER_EVENT:
if (runtime_options.limit_provided == true)
{
runtime_options.all = false;
item_list_append(&cli_warnings,
_("--limit provided, ignoring --all"));
}
break;
default:
item_list_append_format(&cli_warnings,
_("--all not required when executing %s"),
action_name(action));
}
}
/* --wait/--no-wait */
if (runtime_options.wait_provided == true && runtime_options.no_wait == true)
{
item_list_append_format(&cli_errors,
_("both --wait and --no-wait options provided"));
}
else
{
if (runtime_options.wait_provided)
{
switch (action)
{
case DAEMON_START:
case DAEMON_STOP:
case STANDBY_FOLLOW:
break;
default:
item_list_append_format(&cli_warnings,
_("--wait will be ignored when executing %s"),
action_name(action));
}
}
else if (runtime_options.no_wait)
{
switch (action)
{
case DAEMON_START:
case DAEMON_STOP:
case NODE_REJOIN:
break;
default:
item_list_append_format(&cli_warnings,
_("--no-wait will be ignored when executing %s"),
action_name(action));
}
}
}
/* repmgr node service --action */
if (runtime_options.action[0] != '\0')
{
switch (action)
{
case NODE_SERVICE:
break;
default:
item_list_append_format(&cli_warnings,
_("--action will be ignored when executing %s"),
action_name(action));
}
}
/* repmgr node status --is-shutdown-cleanly */
if (runtime_options.is_shutdown_cleanly == true)
{
switch (action)
{
case NODE_STATUS:
break;
default:
item_list_append_format(&cli_warnings,
_("--is-shutdown-cleanly will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.always_promote == true)
{
switch (action)
{
case STANDBY_SWITCHOVER:
break;
default:
item_list_append_format(&cli_warnings,
_("--always-promote will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.force_rewind_used == true)
{
switch (action)
{
case STANDBY_SWITCHOVER:
case NODE_REJOIN:
break;
default:
item_list_append_format(&cli_warnings,
_("--force-rewind will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.repmgrd_no_pause == true)
{
switch (action)
{
case STANDBY_SWITCHOVER:
break;
default:
item_list_append_format(&cli_warnings,
_("--repmgrd-no-pause will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.config_files[0] != '\0')
{
switch (action)
{
case NODE_REJOIN:
break;
default:
item_list_append_format(&cli_warnings,
_("--config-files will be ignored when executing %s"),
action_name(action));
}
}
if (runtime_options.dry_run == true)
{
switch (action)
{
case PRIMARY_REGISTER:
case PRIMARY_UNREGISTER:
case STANDBY_CLONE:
case STANDBY_REGISTER:
case STANDBY_FOLLOW:
case STANDBY_SWITCHOVER:
case STANDBY_PROMOTE:
case WITNESS_REGISTER:
case WITNESS_UNREGISTER:
case NODE_REJOIN:
case NODE_SERVICE:
case DAEMON_PAUSE:
case DAEMON_UNPAUSE:
case DAEMON_START:
case DAEMON_STOP:
break;
default:
item_list_append_format(&cli_warnings,
_("--dry-run is not effective when executing %s"),
action_name(action));
}
}
/* check only one of --csv, --nagios and --optformat used */
{
int used_options = 0;
if (runtime_options.csv == true)
used_options++;
if (runtime_options.nagios == true)
used_options++;
if (runtime_options.optformat == true)
used_options++;
if (used_options > 1)
{
/* TODO: list which options were used */
item_list_append(&cli_errors,
"only one of --csv, --nagios and --optformat can be used");
}
}
/* --compact */
if (runtime_options.compact == true)
{
switch (action)
{
case CLUSTER_SHOW:
case DAEMON_STATUS:
break;
default:
item_list_append_format(&cli_warnings,
_("--compact is not effective when executing %s"),
action_name(action));
}
}
/* --disable-wal-receiver / --enable-wal-receiver */
if (runtime_options.disable_wal_receiver == true || runtime_options.enable_wal_receiver == true)
{
switch (action)
{
case NODE_CONTROL:
{
if (runtime_options.disable_wal_receiver == true && runtime_options.enable_wal_receiver == true)
{
item_list_append(&cli_errors,
_("provide either --disable-wal-receiver or --enable-wal-receiver"));
}
}
break;
default:
item_list_append_format(&cli_warnings,
_("--disable-wal-receiver / --enable-wal-receiver not effective when executing %s"),
action_name(action));
}
}
}
static const char *
action_name(const int action)
{
switch (action)
{
case PRIMARY_REGISTER:
return "PRIMARY REGISTER";
case PRIMARY_UNREGISTER:
return "PRIMARY UNREGISTER";
case STANDBY_CLONE:
return "STANDBY CLONE";
case STANDBY_REGISTER:
return "STANDBY REGISTER";
case STANDBY_UNREGISTER:
return "STANDBY UNREGISTER";
case STANDBY_PROMOTE:
return "STANDBY PROMOTE";
case STANDBY_FOLLOW:
return "STANDBY FOLLOW";
case WITNESS_REGISTER:
return "WITNESS REGISTER";
case WITNESS_UNREGISTER:
return "WITNESS UNREGISTER";
case BDR_REGISTER:
return "BDR REGISTER";
case BDR_UNREGISTER:
return "BDR UNREGISTER";
case NODE_STATUS:
return "NODE STATUS";
case NODE_CHECK:
return "NODE CHECK";
case NODE_REJOIN:
return "NODE REJOIN";
case NODE_SERVICE:
return "NODE SERVICE";
case CLUSTER_SHOW:
return "CLUSTER SHOW";
case CLUSTER_EVENT:
return "CLUSTER EVENT";
case CLUSTER_MATRIX:
return "CLUSTER MATRIX";
case CLUSTER_CROSSCHECK:
return "CLUSTER CROSSCHECK";
case DAEMON_STATUS:
return "DAEMON STATUS";
case DAEMON_PAUSE:
return "DAEMON PAUSE";
case DAEMON_UNPAUSE:
return "DAEMON UNPAUSE";
case DAEMON_START:
return "DAEMON START";
case DAEMON_STOP:
return "DAEMON STOP";
}
return "UNKNOWN ACTION";
}
void
print_error_list(ItemList *error_list, int log_level)
{
ItemListCell *cell = NULL;
for (cell = error_list->head; cell; cell = cell->next)
{
switch (log_level)
{
/* Currently we only need errors and warnings */
case LOG_ERROR:
log_error("%s", cell->string);
break;
case LOG_WARNING:
log_warning("%s", cell->string);
break;
}
}
}
void
print_status_header(int cols, ColHeader *headers)
{
int i;
int max_cols = 0;
/* count how many columns we actually need to display */
for (i = 0; i < cols; i++)
{
if (headers[i].display == true)
max_cols ++;
}
for (i = 0; i < cols; i++)
{
if (headers[i].display == false)
continue;
if (i == 0)
printf(" ");
else
printf(" | ");
printf("%-*s",
headers[i].max_length,
headers[i].title);
}
printf("\n");
printf("-");
for (i = 0; i < max_cols; i++)
{
int j;
if (headers[i].display == false)
continue;
for (j = 0; j < headers[i].max_length; j++)
printf("-");
if (i < (max_cols - 1))
printf("-+-");
else
printf("-");
}
printf("\n");
}
void
print_help_header(void)
{
printf(_("%s: replication management tool for PostgreSQL\n"), progname());
puts("");
/* add a big friendly warning if root is executing "repmgr --help" */
if (geteuid() == 0)
{
printf(_(" **************************************************\n"));
printf(_(" *** repmgr must be executed by a non-superuser ***\n"));
printf(_(" **************************************************\n"));
puts("");
}
}
static void
do_help(void)
{
print_help_header();
printf(_("Usage:\n"));
printf(_(" %s [OPTIONS] primary {register|unregister}\n"), progname());
printf(_(" %s [OPTIONS] standby {register|unregister|clone|promote|follow|switchover}\n"), progname());
printf(_(" %s [OPTIONS] bdr {register|unregister}\n"), progname());
printf(_(" %s [OPTIONS] node {status|check|rejoin|service}\n"), progname());
printf(_(" %s [OPTIONS] cluster {show|event|matrix|crosscheck|cleanup}\n"), progname());
printf(_(" %s [OPTIONS] witness {register|unregister}\n"), progname());
printf(_(" %s [OPTIONS] daemon {status|pause|unpause|start|stop}\n"), progname());
puts("");
printf(_(" Execute \"%s {primary|standby|bdr|node|cluster|witness|daemon} --help\" to see command-specific options\n"), progname());
puts("");
printf(_("General options:\n"));
printf(_(" -?, --help show this help, then exit\n"));
printf(_(" -V, --version output version information, then exit\n"));
printf(_(" --version-number output version number, then exit\n"));
puts("");
printf(_("General configuration options:\n"));
printf(_(" -b, --pg_bindir=PATH path to PostgreSQL binaries (optional)\n"));
printf(_(" -f, --config-file=PATH path to the repmgr configuration file\n"));
printf(_(" -F, --force force potentially dangerous operations to happen\n"));
puts("");
printf(_("Database connection options:\n"));
printf(_(" -d, --dbname=DBNAME database to connect to (default: "));
if (runtime_options.dbname[0] != '\0')
printf(_("\"%s\")\n"), runtime_options.dbname);
else
printf(_("\"%s\")\n"), runtime_options.username);
printf(_(" -h, --host=HOSTNAME database server host"));
if (runtime_options.host[0] != '\0')
printf(_(" (default: \"%s\")"), runtime_options.host);
printf(_("\n"));
printf(_(" -p, --port=PORT database server port (default: \"%s\")\n"), runtime_options.port);
printf(_(" -U, --username=USERNAME database user name to connect as (default: \"%s\")\n"), runtime_options.username);
printf(_(" -S, --superuser=USERNAME superuser to use, if repmgr user is not superuser\n"));
puts("");
printf(_("Node-specific options:\n"));
printf(_(" -D, --pgdata=DIR location of the node's data directory \n"));
printf(_(" --node-id specify a node by id (only available for some operations)\n"));
printf(_(" --node-name specify a node by name (only available for some operations)\n"));
puts("");
printf(_("Logging options:\n"));
printf(_(" --dry-run show what would happen for action, but don't execute it\n"));
printf(_(" -L, --log-level set log level (overrides configuration file; default: NOTICE)\n"));
printf(_(" --log-to-file log to file (or logging facility) defined in repmgr.conf\n"));
printf(_(" -q, --quiet suppress all log output apart from errors\n"));
printf(_(" -t, --terse don't display detail, hints and other non-critical output\n"));
printf(_(" -v, --verbose display additional log output (useful for debugging)\n"));
puts("");
}
/*
* Create the repmgr extension, and grant access for the repmgr
* user if not a superuser.
*
* Note:
* This is one of two places where superuser rights are required.
* We should also consider possible scenarious where a non-superuser
* has sufficient privileges to install the extension.
*/
bool
create_repmgr_extension(PGconn *conn)
{
PQExpBufferData query;
PGresult *res;
ExtensionStatus extension_status = REPMGR_UNKNOWN;
t_connection_user userinfo = T_CONNECTION_USER_INITIALIZER;
bool is_superuser = false;
PGconn *superuser_conn = NULL;
PGconn *schema_create_conn = NULL;
t_extension_versions extversions = T_EXTENSION_VERSIONS_INITIALIZER;
extension_status = get_repmgr_extension_status(conn, &extversions);
switch (extension_status)
{
case REPMGR_UNKNOWN:
log_error(_("unable to determine status of \"repmgr\" extension"));
return false;
case REPMGR_UNAVAILABLE:
log_error(_("\"repmgr\" extension is not available"));
return false;
case REPMGR_OLD_VERSION_INSTALLED:
log_error(_("an older version of the \"repmgr\" extension is installed"));
log_detail(_("version %s is installed but newer version %s is available"),
extversions.installed_version,
extversions.default_version);
log_hint(_("execute \"ALTER EXTENSION repmgr UPGRADE\""));
return false;
case REPMGR_INSTALLED:
log_info(_("\"repmgr\" extension is already installed"));
return true;
case REPMGR_AVAILABLE:
if (runtime_options.dry_run == true)
{
log_notice(_("would now attempt to install extension \"repmgr\""));
}
else
{
log_notice(_("attempting to install extension \"repmgr\""));
}
break;
}
/* 3. Attempt to get a superuser connection */
is_superuser = is_superuser_connection(conn, &userinfo);
get_superuser_connection(&conn, &superuser_conn, &schema_create_conn);
if (runtime_options.dry_run == true)
return true;
/* 4. Create extension */
initPQExpBuffer(&query);
wrap_ddl_query(&query, config_file_options.replication_type,
"CREATE EXTENSION repmgr");
res = PQexec(schema_create_conn, query.data);
termPQExpBuffer(&query);
if ((PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK))
{
log_error(_("unable to create \"repmgr\" extension:\n %s"),
PQerrorMessage(schema_create_conn));
log_hint(_("check that the provided user has sufficient privileges for CREATE EXTENSION"));
PQclear(res);
if (superuser_conn != NULL)
PQfinish(superuser_conn);
return false;
}
PQclear(res);
/* 5. If not superuser, grant usage */
if (is_superuser == false)
{
initPQExpBuffer(&query);
wrap_ddl_query(&query, config_file_options.replication_type,
"GRANT USAGE ON SCHEMA repmgr TO %s",
userinfo.username);
res = PQexec(schema_create_conn, query.data);
termPQExpBuffer(&query);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
log_error(_("unable to grant usage on \"repmgr\" extension to %s:\n %s"),
userinfo.username,
PQerrorMessage(schema_create_conn));
PQclear(res);
if (superuser_conn != 0)
PQfinish(superuser_conn);
return false;
}
initPQExpBuffer(&query);
wrap_ddl_query(&query, config_file_options.replication_type,
"GRANT ALL ON ALL TABLES IN SCHEMA repmgr TO %s",
userinfo.username);
res = PQexec(schema_create_conn, query.data);
termPQExpBuffer(&query);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
log_error(_("unable to grant permission on tables on \"repmgr\" extension to %s:\n %s"),
userinfo.username,
PQerrorMessage(schema_create_conn));
PQclear(res);
if (superuser_conn != NULL)
PQfinish(superuser_conn);
return false;
}
}
if (superuser_conn != NULL)
PQfinish(superuser_conn);
log_notice(_("\"repmgr\" extension successfully installed"));
create_event_notification(conn,
&config_file_options,
config_file_options.node_id,
"cluster_created",
true,
NULL);
return true;
}
/**
* check_server_version()
*
* Verify that the server is MIN_SUPPORTED_VERSION_NUM or later
*
* PGconn *conn:
* the connection to check
*
* char *server_type:
* either "primary" or "standby"; used to format error message
*
* bool exit_on_error:
* exit if reported server version is too low; optional to enable some callers
* to perform additional cleanup
*
* char *server_version_string
* passed to get_server_version(), which will place the human-readable
* server version string there (e.g. "9.4.0")
*/
int
check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string)
{
int conn_server_version_num = get_server_version(conn, server_version_string);
if (conn_server_version_num < MIN_SUPPORTED_VERSION_NUM)
{
if (conn_server_version_num > 0)
log_error(_("%s requires %s to be PostgreSQL %s or later"),
progname(),
server_type,
MIN_SUPPORTED_VERSION);
if (exit_on_error == true)
{
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
return UNKNOWN_SERVER_VERSION_NUM;
}
return conn_server_version_num;
}
/*
* check_93_config()
*
* Disable options not compatible with PostgreSQL 9.3
*/
void
check_93_config(void)
{
if (config_file_options.recovery_min_apply_delay_provided == true)
{
config_file_options.recovery_min_apply_delay_provided = false;
log_warning(_("configuration file option \"recovery_min_apply_delay\" not compatible with PostgreSQL 9.3, ignoring"));
}
if (config_file_options.use_replication_slots == true)
{
config_file_options.use_replication_slots = false;
log_warning(_("configuration file option \"use_replication_slots\" not compatible with PostgreSQL 9.3, ignoring"));
log_hint(_("replication slots are available from PostgreSQL 9.4"));
}
}
int
test_ssh_connection(char *host, char *remote_user)
{
char script[MAXLEN] = "";
int r = 1,
i;
/*
* On some OS, true is located in a different place than in Linux we have
* to try them all until all alternatives are gone or we found `true'
* because the target OS may differ from the source OS
*/
const char *bin_true_paths[] = {
"/bin/true",
"/usr/bin/true",
NULL
};
for (i = 0; bin_true_paths[i] && r != 0; ++i)
{
if (!remote_user[0])
maxlen_snprintf(script, "ssh -o Batchmode=yes %s %s %s 2>/dev/null",
config_file_options.ssh_options, host, bin_true_paths[i]);
else
maxlen_snprintf(script, "ssh -o Batchmode=yes %s %s -l %s %s 2>/dev/null",
config_file_options.ssh_options, host, remote_user,
bin_true_paths[i]);
log_verbose(LOG_DEBUG, _("test_ssh_connection(): executing %s"), script);
r = system(script);
}
if (r != 0)
log_warning(_("unable to connect to remote host \"%s\" via SSH"), host);
return r;
}
/*
* get_superuser_connection()
*
* Check if provided connection "conn" is a superuser connection, if not attempt to
* make a superuser connection "superuser_conn" with the provided --superuser parameter.
*
* "privileged_conn" is set to whichever connection is the superuser connection.
*/
void
get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGconn **privileged_conn)
{
t_connection_user userinfo = T_CONNECTION_USER_INITIALIZER;
t_conninfo_param_list conninfo_params = T_CONNINFO_PARAM_LIST_INITIALIZER;
bool is_superuser = false;
/* this should never happen */
if (PQstatus(*conn) != CONNECTION_OK)
{
log_error(_("no database connection available"));
log_detail("\n%s", PQerrorMessage(*conn));
exit(ERR_INTERNAL);
}
is_superuser = is_superuser_connection(*conn, &userinfo);
if (is_superuser == true)
{
*privileged_conn = *conn;
return;
}
if (runtime_options.superuser[0] == '\0')
{
log_error(_("\"%s\" is not a superuser and no superuser name supplied"), userinfo.username);
log_hint(_("supply a valid superuser name with -S/--superuser"));
PQfinish(*conn);
exit(ERR_BAD_CONFIG);
}
initialize_conninfo_params(&conninfo_params, false);
conn_to_param_list(*conn, &conninfo_params);
param_set(&conninfo_params, "user", runtime_options.superuser);
*superuser_conn = establish_db_connection_by_params(&conninfo_params, false);
if (PQstatus(*superuser_conn) != CONNECTION_OK)
{
log_error(_("unable to establish superuser connection as \"%s\""),
runtime_options.superuser);
PQfinish(*conn);
exit(ERR_BAD_CONFIG);
}
/* check provided superuser really is superuser */
if (!is_superuser_connection(*superuser_conn, NULL))
{
log_error(_("\"%s\" is not a superuser"), runtime_options.superuser);
PQfinish(*superuser_conn);
PQfinish(*conn);
exit(ERR_BAD_CONFIG);
}
log_debug("established superuser connection as \"%s\"", runtime_options.superuser);
*privileged_conn = *superuser_conn;
return;
}
standy_clone_mode
get_standby_clone_mode(void)
{
standy_clone_mode mode;
if (*config_file_options.barman_host != '\0' && runtime_options.without_barman == false)
mode = barman;
else
mode = pg_basebackup;
return mode;
}
char *
make_pg_path(const char *file)
{
maxlen_snprintf(path_buf, "%s%s", pg_bindir, file);
return path_buf;
}
int
copy_remote_files(char *host, char *remote_user, char *remote_path,
char *local_path, bool is_directory, int server_version_num)
{
PQExpBufferData rsync_flags;
char script[MAXLEN] = "";
char host_string[MAXLEN] = "";
int r = 0;
initPQExpBuffer(&rsync_flags);
if (*config_file_options.rsync_options == '\0')
{
appendPQExpBuffer(&rsync_flags, "%s",
"--archive --checksum --compress --progress --rsh=ssh");
}
else
{
appendPQExpBuffer(&rsync_flags, "%s",
config_file_options.rsync_options);
}
if (runtime_options.force)
{
appendPQExpBuffer(&rsync_flags, "%s",
" --delete --checksum");
}
if (!remote_user[0])
{
maxlen_snprintf(host_string, "%s", host);
}
else
{
maxlen_snprintf(host_string, "%s@%s", remote_user, host);
}
/*
* When copying the main PGDATA directory, certain files and contents of
* certain directories need to be excluded.
*
* See function 'sendDir()' in 'src/backend/replication/basebackup.c' -
* we're basically simulating what pg_basebackup does, but with rsync
* rather than the BASEBACKUP replication protocol command.
*
* *However* currently we'll always copy the contents of the 'pg_replslot'
* directory and delete later if appropriate.
*/
if (is_directory)
{
/* Files which we don't want */
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=postmaster.pid --exclude=postmaster.opts --exclude=global/pg_control");
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=recovery.conf --exclude=recovery.done");
if (server_version_num >= 90400)
{
/*
* Ideally we'd use PG_AUTOCONF_FILENAME from utils/guc.h, but
* that has too many dependencies for a mere client program.
*/
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=postgresql.auto.conf.tmp");
}
/* Temporary files which we don't want, if they exist */
appendPQExpBuffer(&rsync_flags, " --exclude=%s*",
PG_TEMP_FILE_PREFIX);
/* Directories which we don't want */
if (server_version_num >= 100000)
{
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=pg_wal/*");
}
else
{
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=pg_xlog/*");
}
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=pg_log/* --exclude=pg_stat_tmp/*");
maxlen_snprintf(script, "rsync %s %s:%s/* %s",
rsync_flags.data, host_string, remote_path, local_path);
}
else
{
maxlen_snprintf(script, "rsync %s %s:%s %s",
rsync_flags.data, host_string, remote_path, local_path);
}
termPQExpBuffer(&rsync_flags);
log_info(_("rsync command line:\n %s"), script);
r = system(script);
log_debug("copy_remote_files(): r = %i; WIFEXITED: %i; WEXITSTATUS: %i", r, WIFEXITED(r), WEXITSTATUS(r));
/* exit code 24 indicates vanished files, which isn't a problem for us */
if (WIFEXITED(r) && WEXITSTATUS(r) && WEXITSTATUS(r) != 24)
log_verbose(LOG_WARNING, "copy_remote_files(): rsync returned unexpected exit status %i", WEXITSTATUS(r));
return r;
}
void
make_remote_repmgr_path(PQExpBufferData *output_buf, t_node_info *remote_node_record)
{
if (config_file_options.repmgr_bindir[0] != '\0')
{
int len = strlen(config_file_options.repmgr_bindir);
appendPQExpBufferStr(output_buf,
config_file_options.repmgr_bindir);
/* Add trailing slash */
if (config_file_options.repmgr_bindir[len - 1] != '/')
{
appendPQExpBufferChar(output_buf, '/');
}
}
else if (pg_bindir[0] != '\0')
{
appendPQExpBufferStr(output_buf,
pg_bindir);
}
appendPQExpBuffer(output_buf,
"%s -f %s ",
progname(),
remote_node_record->config_file);
}
void
make_repmgrd_path(PQExpBufferData *output_buf)
{
if (config_file_options.repmgr_bindir[0] != '\0')
{
int len = strlen(config_file_options.repmgr_bindir);
appendPQExpBufferStr(output_buf,
config_file_options.repmgr_bindir);
/* Add trailing slash */
if (config_file_options.repmgr_bindir[len - 1] != '/')
{
appendPQExpBufferChar(output_buf, '/');
}
}
else if (pg_bindir[0] != '\0')
{
appendPQExpBufferStr(output_buf,
pg_bindir);
}
appendPQExpBuffer(output_buf,
"repmgrd -f %s ",
config_file_path);
}
/* ======================== */
/* server control functions */
/* ======================== */
void
get_server_action(t_server_action action, char *script, char *data_dir)
{
PQExpBufferData command;
if (data_dir == NULL || data_dir[0] == '\0')
data_dir = "(none provided)";
switch (action)
{
case ACTION_NONE:
script[0] = '\0';
return;
case ACTION_START:
{
if (config_file_options.service_start_command[0] != '\0')
{
maxlen_snprintf(script, "%s",
config_file_options.service_start_command);
}
else
{
initPQExpBuffer(&command);
appendPQExpBuffer(&command,
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
data_dir);
appendPQExpBuffer(&command,
" start");
strncpy(script, command.data, MAXLEN);
termPQExpBuffer(&command);
}
return;
}
case ACTION_STOP:
case ACTION_STOP_WAIT:
{
if (config_file_options.service_stop_command[0] != '\0')
{
maxlen_snprintf(script, "%s",
config_file_options.service_stop_command);
}
else
{
initPQExpBuffer(&command);
appendPQExpBuffer(&command,
"%s %s -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
data_dir);
if (action == ACTION_STOP_WAIT)
appendPQExpBuffer(&command,
" -w");
else
appendPQExpBuffer(&command,
" -W");
appendPQExpBuffer(&command,
" -m fast stop");
strncpy(script, command.data, MAXLEN);
termPQExpBuffer(&command);
}
return;
}
case ACTION_RESTART:
{
if (config_file_options.service_restart_command[0] != '\0')
{
maxlen_snprintf(script, "%s",
config_file_options.service_restart_command);
}
else
{
initPQExpBuffer(&command);
appendPQExpBuffer(&command,
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
data_dir);
appendPQExpBuffer(&command,
" restart");
strncpy(script, command.data, MAXLEN);
termPQExpBuffer(&command);
}
return;
}
case ACTION_RELOAD:
{
if (config_file_options.service_reload_command[0] != '\0')
{
maxlen_snprintf(script, "%s",
config_file_options.service_reload_command);
}
else
{
initPQExpBuffer(&command);
appendPQExpBuffer(&command,
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
data_dir);
appendPQExpBuffer(&command,
" reload");
strncpy(script, command.data, MAXLEN);
termPQExpBuffer(&command);
}
return;
}
case ACTION_PROMOTE:
{
if (config_file_options.service_promote_command[0] != '\0')
{
maxlen_snprintf(script, "%s",
config_file_options.service_promote_command);
}
else
{
initPQExpBuffer(&command);
appendPQExpBuffer(&command,
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
data_dir);
appendPQExpBuffer(&command,
" promote");
strncpy(script, command.data, MAXLEN);
termPQExpBuffer(&command);
}
return;
}
default:
return;
}
return;
}
bool
data_dir_required_for_action(t_server_action action)
{
switch (action)
{
case ACTION_NONE:
return false;
case ACTION_START:
if (config_file_options.service_start_command[0] != '\0')
{
return false;
}
return true;
case ACTION_STOP:
case ACTION_STOP_WAIT:
if (config_file_options.service_stop_command[0] != '\0')
{
return false;
}
return true;
case ACTION_RESTART:
if (config_file_options.service_restart_command[0] != '\0')
{
return false;
}
return true;
case ACTION_RELOAD:
if (config_file_options.service_reload_command[0] != '\0')
{
return false;
}
return true;
case ACTION_PROMOTE:
if (config_file_options.service_promote_command[0] != '\0')
{
return false;
}
return true;
default:
return false;
}
return false;
}
/*
* Copy the location of the configuration file directory into the
* provided buffer; if "config_directory" provided, use that, otherwise
* default to the data directory.
*
* This is primarily intended for use with "pg_ctl" (which itself shouldn't
* be used outside of development environments).
*/
void
get_node_config_directory(char *config_dir_buf)
{
if (config_file_options.config_directory[0] != '\0')
{
strncpy(config_dir_buf, config_file_options.config_directory, MAXPGPATH);
return;
}
if (config_file_options.data_directory[0] != '\0')
{
strncpy(config_dir_buf, config_file_options.data_directory, MAXPGPATH);
return;
}
return;
}
void
get_node_data_directory(char *data_dir_buf)
{
/*
* the configuration file setting has priority, and will always be set
* when a configuration file was provided
*/
if (config_file_options.data_directory[0] != '\0')
{
strncpy(data_dir_buf, config_file_options.data_directory, MAXPGPATH);
return;
}
if (runtime_options.data_dir[0] != '\0')
{
strncpy(data_dir_buf, runtime_options.data_dir, MAXPGPATH);
return;
}
return;
}
/*
* initialise a node record from the provided configuration
* parameters
*/
void
init_node_record(t_node_info *node_record)
{
node_record->node_id = config_file_options.node_id;
node_record->upstream_node_id = runtime_options.upstream_node_id;
node_record->priority = config_file_options.priority;
node_record->active = true;
if (config_file_options.location[0] != '\0')
strncpy(node_record->location, config_file_options.location, MAXLEN);
else
strncpy(node_record->location, "default", MAXLEN);
strncpy(node_record->node_name, config_file_options.node_name, sizeof(node_record->node_name));
strncpy(node_record->conninfo, config_file_options.conninfo, MAXLEN);
strncpy(node_record->config_file, config_file_path, MAXPGPATH);
/* highgo: init the virtual ip */
strncpy(node_record->virtual_ip, config_file_options.virtual_ip, MAXPGPATH);
strncpy(node_record->network_card, config_file_options.network_card, MAXPGPATH);
if (config_file_options.replication_user[0] != '\0')
{
/* replication user explicitly provided in configuration file */
strncpy(node_record->repluser, config_file_options.replication_user, NAMEDATALEN);
}
else
{
/* use the "user" value from "conninfo" */
char repluser[MAXLEN] = "";
(void) get_conninfo_value(config_file_options.conninfo, "user", repluser);
strncpy(node_record->repluser, repluser, NAMEDATALEN);
}
if (config_file_options.use_replication_slots == true)
{
create_slot_name(node_record->slot_name, config_file_options.node_id);
}
}
bool
can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *reason)
{
bool can_use = true;
/* wal_log_hints not available in 9.3, so just determine if data checksums enabled */
if (PQserverVersion(conn) < 90400)
{
int data_checksum_version = get_data_checksum_version(data_directory);
if (data_checksum_version < 0)
{
appendPQExpBuffer(reason,
_("unable to determine data checksum version"));
can_use = false;
}
else if (data_checksum_version == 0)
{
appendPQExpBuffer(reason,
_("this cluster was initialised without data checksums"));
can_use = false;
}
return can_use;
}
/* "full_page_writes" must be on in any case */
if (guc_set(conn, "full_page_writes", "=", "off"))
{
appendPQExpBuffer(reason,
_("\"full_page_writes\" must be set to \"on\""));
can_use = false;
}
/*
* "wal_log_hints" off - are data checksums available? Note: we're
* checking the local pg_control file here as the value will be the same
* throughout the cluster and saves a round-trip to the demotion
* candidate.
*/
if (guc_set(conn, "wal_log_hints", "=", "on") == false)
{
int data_checksum_version = get_data_checksum_version(data_directory);
if (data_checksum_version < 0)
{
if (can_use == false)
appendPQExpBuffer(reason, "; ");
appendPQExpBuffer(reason,
_("\"wal_log_hints\" is set to \"off\" but unable to determine data checksum version"));
can_use = false;
}
else if (data_checksum_version == 0)
{
if (can_use == false)
appendPQExpBuffer(reason, "; ");
appendPQExpBuffer(reason,
_("\"wal_log_hints\" is set to \"off\" and data checksums are disabled"));
can_use = false;
}
}
return can_use;
}
void
drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name)
{
t_replication_slot slot_info = T_REPLICATION_SLOT_INITIALIZER;
RecordStatus record_status = get_slot_record(conn, slot_name, &slot_info);
log_verbose(LOG_DEBUG, "attempting to delete slot \"%s\" on node %i",
slot_name, node_id);
if (record_status != RECORD_FOUND)
{
/* this is a good thing */
log_verbose(LOG_INFO,
_("slot \"%s\" does not exist on node %i, nothing to remove"),
slot_name, node_id);
}
else
{
if (slot_info.active == false)
{
if (drop_replication_slot(conn, slot_name) == true)
{
log_notice(_("replication slot \"%s\" deleted on node %i"), slot_name, node_id);
}
else
{
log_error(_("unable to delete replication slot \"%s\" on node %i"), slot_name, node_id);
}
}
/*
* if active replication slot exists, call Houston as we have a
* problem
*/
else
{
log_warning(_("replication slot \"%s\" is still active on node %i"), slot_name, node_id);
}
}
}
/*
* Here we'll perform some timeline sanity checks to ensure the follow target
* can actually be followed.
*
* See also comment for check_node_can_follow() in repmgrd-physical.c .
*/
bool
check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *follow_target_conn, t_node_info *follow_target_node_record, bool is_rejoin)
{
uint64 local_system_identifier = UNKNOWN_SYSTEM_IDENTIFIER;
t_conninfo_param_list follow_target_repl_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
PGconn *follow_target_repl_conn = NULL;
t_system_identification follow_target_identification = T_SYSTEM_IDENTIFICATION_INITIALIZER;
TimeLineHistoryEntry *follow_target_history = NULL;
bool success = true;
const char *action = is_rejoin == true ? "rejoin" : "follow";
/* check replication connection */
initialize_conninfo_params(&follow_target_repl_conninfo, false);
conn_to_param_list(follow_target_conn, &follow_target_repl_conninfo);
if (strcmp(param_get(&follow_target_repl_conninfo, "user"), follow_target_node_record->repluser) != 0)
{
param_set(&follow_target_repl_conninfo, "user", follow_target_node_record->repluser);
param_set(&follow_target_repl_conninfo, "dbname", "replication");
}
param_set(&follow_target_repl_conninfo, "replication", "1");
follow_target_repl_conn = establish_db_connection_by_params(&follow_target_repl_conninfo, false);
free_conninfo_params(&follow_target_repl_conninfo);
if (PQstatus(follow_target_repl_conn) != CONNECTION_OK)
{
log_error(_("unable to establish a replication connection to the %s target node"), action);
return false;
}
else if (runtime_options.dry_run == true)
{
log_info(_("replication connection to the %s target node was successful"), action);
}
/* check system_identifiers match */
if (identify_system(follow_target_repl_conn, &follow_target_identification) == false)
{
log_error(_("unable to query the %s target node's system identification"), action);
PQfinish(follow_target_repl_conn);
return false;
}
local_system_identifier = get_system_identifier(config_file_options.data_directory);
/*
* Check for thing that should never happen, but expect the unexpected anyway.
*/
if (follow_target_identification.system_identifier != local_system_identifier)
{
log_error(_("this node is not part of the %s target node's replication cluster"), action);
log_detail(_("this node's system identifier is %lu, %s target node's system identifier is %lu"),
local_system_identifier,
action,
follow_target_identification.system_identifier);
PQfinish(follow_target_repl_conn);
return false;
}
if (runtime_options.dry_run == true)
{
log_info(_("local and %s target system identifiers match"), action);
log_detail(_("system identifier is %lu"), local_system_identifier);
}
/* check timelines */
log_verbose(LOG_DEBUG, "local timeline: %i; %s target timeline: %i",
local_tli,
action,
follow_target_identification.timeline);
/* upstream's timeline is lower than ours - impossible case */
if (follow_target_identification.timeline < local_tli)
{
log_error(_("this node's timeline is ahead of the %s target node's timeline"), action);
log_detail(_("this node's timeline is %i, %s target node's timeline is %i"),
local_tli,
action,
follow_target_identification.timeline);
PQfinish(follow_target_repl_conn);
return false;
}
/* timelines are the same - check relative positions */
if (follow_target_identification.timeline == local_tli)
{
XLogRecPtr follow_target_xlogpos = get_node_current_lsn(follow_target_conn);
if (local_xlogpos == InvalidXLogRecPtr || follow_target_xlogpos == InvalidXLogRecPtr)
{
log_error(_("unable to compare LSN positions"));
PQfinish(follow_target_repl_conn);
return false;
}
if (local_xlogpos <= follow_target_xlogpos)
{
log_info(_("timelines are same, this server is not ahead"));
log_detail(_("local node lsn is %X/%X, %s target lsn is %X/%X"),
format_lsn(local_xlogpos),
action,
format_lsn(follow_target_xlogpos));
}
else
{
log_error(_("this node is ahead of the %s target"), action);
log_detail(_("local node lsn is %X/%X, %s target lsn is %X/%X"),
format_lsn(local_xlogpos),
action,
format_lsn(follow_target_xlogpos));
success = false;
}
}
else
{
/*
* upstream has higher timeline - check where it forked off from this node's timeline
*/
follow_target_history = get_timeline_history(follow_target_repl_conn, local_tli + 1);
if (follow_target_history == NULL)
{
/* get_timeline_history() will emit relevant error messages */
PQfinish(follow_target_repl_conn);
return false;
}
log_debug("local tli: %i; local_xlogpos: %X/%X; follow_target_history->tli: %i; follow_target_history->end: %X/%X",
local_tli,
format_lsn(local_xlogpos),
follow_target_history->tli,
format_lsn(follow_target_history->end));
/*
* Local node has proceeded beyond the follow target's fork, so we
* definitely can't attach.
*
* This could be the case if the follow target was promoted, but does
* not contain all changes which are being replayed to this standby.
*/
if (local_xlogpos > follow_target_history->end)
{
if (is_rejoin == true && runtime_options.force_rewind_used == true)
{
log_notice(_("pg_rewind execution required for this node to attach to rejoin target node %i"),
follow_target_node_record->node_id);
}
else
{
log_error(_("this node cannot attach to %s target node %i"),
action,
follow_target_node_record->node_id);
success = false;
}
log_detail(_("%s target server's timeline %i forked off current database system timeline %i before current recovery point %X/%X"),
action,
local_tli + 1,
local_tli,
format_lsn(local_xlogpos));
if (is_rejoin == true && runtime_options.force_rewind_used == false)
{
log_hint(_("use --force-rewind to execute pg_rewind"));
}
}
if (success == true)
{
if (is_rejoin == false || (is_rejoin == true && runtime_options.force_rewind_used == false))
{
log_info(_("local node %i can attach to %s target node %i"),
config_file_options.node_id,
action,
follow_target_node_record->node_id);
log_detail(_("local node's recovery point: %X/%X; %s target node's fork point: %X/%X"),
format_lsn(local_xlogpos),
action,
format_lsn(follow_target_history->end));
}
}
}
PQfinish(follow_target_repl_conn);
if (follow_target_history)
pfree(follow_target_history);
return success;
}
/*
* Simple check to see if "shared_preload_libraries" includes "repmgr".
* Parsing "shared_preload_libraries" is non-trivial, as it's potentially
* a comma-separated list, and worse may not be readable by the repmgr
* user.
*
* Instead, we check if a function which should return a value returns
* NULL; this indicates the shared library is not installed.
*/
void
check_shared_library(PGconn *conn)
{
bool ok = repmgrd_check_local_node_id(conn);
if (ok == true)
return;
log_error(_("repmgrd not configured for this node"));
log_hint(_("ensure \"shared_preload_libraries\" includes \"repmgr\" and restart PostgreSQL"));
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
bool
is_repmgrd_running(PGconn *conn)
{
pid_t pid;
bool is_running = false;
pid = repmgrd_get_pid(conn);
if (pid != UNKNOWN_PID)
{
if (kill(pid, 0) != -1)
{
is_running = true;
}
}
return is_running;
}
C
1
https://gitee.com/highgosoftware/hg_repmgr.git
git@gitee.com:highgosoftware/hg_repmgr.git
highgosoftware
hg_repmgr
hg_repmgr
master

Search