Compare commits

...

73 Commits

Author SHA1 Message Date
Ian Barwick
e59b57376d Update code comments 2016-07-12 10:59:48 +09:00
Ian Barwick
3db87e6a31 Remove unused error code ERR_BAD_PASSWORD 2016-07-12 10:59:42 +09:00
Ian Barwick
94d05619c3 README: update error code list 2016-07-12 10:59:37 +09:00
Ian Barwick
807c7c926c Update README with details about conninfo parameter handling
From 3.1.4 `repmgr` will behave like other PostgreSQL utilities
when handling database connection parameters, in particular
accepting a conninfo string and honouring libpq connection defaults.
2016-07-12 10:59:30 +09:00
Ian Barwick
df68f1f3f6 Make more consistent use of conninfo parameters
Removed the existing keyword array which has a fixed, limited number
of parameters and replace it with a dynamic array which can be
used to store as many parameters as reported by libpq.
2016-07-12 10:59:26 +09:00
Ian Barwick
d4c75bb6c7 Add missing space when setting "application_name" 2016-07-12 10:59:20 +09:00
Ian Barwick
94d4e1128d Improve default host/dbname handling
repmgr disallows socket connections anyway (the whole point of providing
the host is to connect to a remote machine) so don't show that as
a fallback default in the -?/--help output.
2016-07-12 10:59:13 +09:00
Ian Barwick
dbd82ba687 Enable a conninfo string to be passed to repmgr in the -d/--dbname parameter
This matches the behaviour of other PostgreSQL utilities such as pg_basebackup,
psql et al.

Note that unlike psql, but like pg_basebackup, repmgr does not accept a
"left-over" parameter as a conninfo string; this could be added later.

Parameters specified in the conninfo string will override any parameters
supplied correcly (e.g. `-d "host=foo"` will override `-h bar`).
2016-07-12 10:59:09 +09:00
Ian Barwick
0888fbc538 Generate "primary_conninfo" using the primary connection's parameters
Having successfully connected to the primary, we can use the actual parameters
reported by libpq to create "primary_conninfo", rather than the limited
subset previously defined by repmgr. Assuming that the user can
pass a conninfo string to repmgr (see following commit), this makes it
possible to provide other connection parameters, e.g. related to
SSL usage.
2016-07-12 10:59:05 +09:00
Ian Barwick
92a84bd950 Remove now-superfluous wildcard in rmtree() call 2016-07-07 09:54:50 +09:00
Ian Barwick
a3318d65d2 Bump version
3.1.4
2016-07-07 08:49:42 +09:00
Ian Barwick
374e9811c9 Merge branch 'master' of github.com:2ndQuadrant/repmgr into REL3_1_STABLE 2016-07-06 16:43:39 +09:00
Ian Barwick
16896510dc Fix log formatting 2016-05-17 17:24:30 +09:00
Ian Barwick
1c155a1088 Update HISTORY 2016-05-17 11:12:18 +09:00
Ian Barwick
31d57f4122 Update Makefile
Add include file dependencies (see caveat in file).

Also update comments.
2016-05-16 19:15:58 +09:00
Ian Barwick
7b313b9d71 README.md: improve documentation of repl_status view 2016-05-16 13:51:08 +09:00
Ian Barwick
cf126642bd repmgrd: handle situations where streaming replication is inactive 2016-05-16 12:31:31 +09:00
Ian Barwick
52281fcde8 repmgrd: rename some variables to better match the system functions they're populated from 2016-05-16 12:31:06 +09:00
Ian Barwick
de573edaaa Remove extraneous PQfinish() 2016-05-16 12:23:39 +09:00
Ian Barwick
4cb7f301ad Correct check for wal_level in 9.3 2016-05-16 12:23:33 +09:00
Ian Barwick
87d8de4441 Remove unneeded column 2016-05-16 12:23:25 +09:00
Ian Barwick
6db742f81e repmgrd: better handling of missing upstream_node_id
Ensure we default to master node.
2016-05-16 12:23:20 +09:00
Ian Barwick
c79933685c Add missing newlines in log messages 2016-05-16 12:23:15 +09:00
Ian Barwick
04ba672b9f repmgrd: avoid additional connection to local instance in do_master_failover() 2016-05-16 12:23:09 +09:00
Ian Barwick
4f4111063a Suppress gnu_printf format warning 2016-05-16 12:23:03 +09:00
Ian Barwick
3a3a536e6d repmgrd: rename variable for clarity 2016-05-16 12:22:58 +09:00
Ian Barwick
6f7206a5a1 Don't follow the promotion candidate standby if the primary reappears 2016-05-16 12:22:49 +09:00
Ian Barwick
f9fd1dd227 Don't terminate a standby's repmgrd if self-promotion fails due to master reappearing
Per GitHub #173
2016-05-16 12:22:40 +09:00
Martin
8140ba9c27 The commit fixes problems not taking in account while working on the
issue with rsync returning non-zero status on vanishing files on commit
83e5f98171.

Alvaro Herrera gave me some tips which pointed me in the correct
direction.

This was reported by sungjae lee <sj860908@gmail.com>
2016-05-16 12:22:27 +09:00
Ian Barwick
32dba444e1 Enable long option --pgdata as alias for -D/--data-dir
pg_ctl provides -D/--pgdata; we want to be as close to the core utilities
as possible.
2016-05-16 12:22:17 +09:00
Ian Barwick
8212ff8d8a Bump version
3.1.3
2016-05-12 07:54:42 +09:00
Martin
1ccd0edad2 We were not checking the return code after rsyncing the tablespaces.
This fixes #168
2016-04-17 17:59:50 -03:00
Martin
59b31dd1ca Ignore rsync error code for vanished files.
It's very common to come over vanish files during a backup or rsync
o the data directory (dropped index, temp tables, etc.)

This fixes #149
2016-04-17 17:59:50 -03:00
Ian Barwick
300b9f0cc2 Fix pre-9.6 wal_level check 2016-04-12 16:18:29 +09:00
Ian Barwick
0efee4cf65 Fix hint message formatting 2016-04-12 16:07:38 +09:00
Ian Barwick
0cb2584886 Bump version
3.1.2
2016-04-12 15:56:39 +09:00
Ian Barwick
b88d27248c Use "immediately_reserve" parameter in pg_create_physical_replication_slot (9.6) 2016-04-12 15:56:06 +09:00
Ian Barwick
683c54325e Enable repmgr to be compiled with PostgreSQL 9.6 2016-04-12 15:55:51 +09:00
Ian Barwick
70d398cd47 Update HISTORY 2016-04-12 15:53:40 +09:00
Ian Barwick
7b7d80e5f2 Update HISTORY 2016-04-12 15:53:33 +09:00
Ian Barwick
96b0e26084 Remove duplicate inclusion from header file 2016-04-06 14:16:00 +09:00
Ian Barwick
91c498f6f1 Update HISTORY 2016-04-06 11:57:46 +09:00
Ian Barwick
d48093e732 Preserver failover slots when cloning a standby, if enabled 2016-04-06 11:20:27 +09:00
Ian Barwick
3f0d1754a4 MAXFILENAME -> MAXPGPATH 2016-04-06 11:20:27 +09:00
Craig Ringer
f27979bbe1 WIP support for preserving failover slots 2016-04-06 11:20:27 +09:00
Ian Barwick
e9445a5d5e Make self-referencing foreign key on repl_nodes table deferrable 2016-04-01 15:20:36 +09:00
Ian Barwick
9a2717b5e3 Improve debugging output for node resyncing
We'll need this for testing.
2016-04-01 15:20:32 +09:00
Ian Barwick
dd6ea1cd77 Rename copy_configuration () to witness_copy_node_records()
As it's witness-specific. Per suggestion from Martín.
2016-04-01 11:30:08 +09:00
Ian Barwick
de5908c122 Make witness server node update an atomic operation
If the connection to the primary is lost, roll back to the previously
known state.

TRUNCATE is of course not MVCC-friendly, but that shouldn't matter here
as only one process should ever be looking at this table.
2016-04-01 11:15:27 +09:00
Ian Barwick
4b5c84921c Replace MAXFILENAME with MAXPGPATH 2016-03-31 20:11:10 +09:00
Ian Barwick
aaa8d70cef Comment out configuration items in sample config file
The configured values are either the defaults, or examples which
may not work in a real environment. If this file is being used as
a template, the onus is on the user to uncomment and check all
desired parameters.
2016-03-31 15:14:30 +09:00
Gianni Ciolli
ca31b846e7 Rewording comment for clarity. 2016-03-31 15:01:29 +09:00
Ian Barwick
a27cecb559 Update HISTORY 2016-03-31 14:59:03 +09:00
Ian Barwick
cf0cdfa6a1 Bump version
3.1.2rc1
2016-03-31 14:56:49 +09:00
Ian Barwick
31489d92c0 Check directory entity filetype in a more portable way 2016-03-30 20:21:41 +09:00
Ian Barwick
b7fd13aed2 Fix pg_ctl path generation in do_standby_switchover() 2016-03-30 16:46:57 +09:00
Ian Barwick
3c4bf27aa7 Add headers as dependencies in Makefile 2016-03-30 15:06:15 +09:00
Ian Barwick
0ebd9c15d9 Regularly sync witness server repl_nodes table.
Although the witness server will resync the repl_nodes table following
a failover, other operations (e.g. removing or cloning a standby)
were previously not reflected in the witness server's copy of this
table.

As a short-term workaround, automatically resync the table at regular
intervals (defined by the configuration file parameter
"witness_repl_nodes_sync_interval_secs", default 30 seconds).
2016-03-30 15:06:12 +09:00
Nikolay Shaplov
f9dba283d4 Better use /24 network mask in this example 2016-03-30 15:05:29 +09:00
Ian Barwick
205f1cebbb It's unlikely this situation will occur on a witness server
Which is why the error message is for master/standby only.
2016-03-30 15:05:26 +09:00
Ian Barwick
4d97c1ebf7 Add hint about registering the server after cloning it.
This step is easy to forget.
2016-03-30 15:05:20 +09:00
Ian Barwick
12c395e91f README: Add note about 'repmgr_funcs' 2016-03-30 15:05:17 +09:00
Ian Barwick
bd1e4f71d6 repmgrd: fix error message 2016-03-30 15:05:10 +09:00
Ian Barwick
cb49071ea4 Fix code comment 2016-03-30 15:05:06 +09:00
Ian Barwick
5ad674edff Bump version
3.1.1
2016-02-23 15:56:24 +09:00
Ian Barwick
ac09bad89c Minor fixes to README.md 2016-02-23 14:37:59 +09:00
Ian Barwick
009d92fec8 Ensure witness node is registered before the repl_nodes table is copied
This fixes a bug introduced into the previous commit, where the
witness node was registered last to prevent a spurious node record
being created even if witness server creation failed.
2016-02-23 14:37:54 +09:00
Martin
b3d8a68a1d Fix a few paragraphs from the README.md. 2016-02-23 14:37:48 +09:00
Ian Barwick
05b47cb2a8 Prevent repmgr/repmgrd running as root 2016-02-23 14:37:44 +09:00
Ian Barwick
dc542a1b7d Better handling of errors during witness creation
Ensure witness is only registered after all steps for creation
have been successfully completed.

Also write an event record if connection could not be made to
the witness server after initial creation.

This addresses GitHub issue #146.
2016-02-23 14:37:39 +09:00
Ian Barwick
6ce8058749 witness creation: extract database and user names from the local conninfo string
99.9% of the time they'll be the same as the primary connection, but
it's more consistent to use the provided local conninfo string
(from which the port is already extracted).
2016-02-23 14:37:31 +09:00
Ian Barwick
2edcac77f0 README.md: update witness server section 2016-02-23 14:37:27 +09:00
Ian Barwick
f740374392 Add '-P/--pwprompt' option for "repmgr create witness"
Optionally prompt for superuser and repmgr user when creating a witness.
This ensures a password can be provided if the primary's pg_hba.conf
mandates it.

This deprecates '--initdb-no-pwprompt'; and changes the default behaviour of
"repmgr create witness", which previously required a superuser password
unless '--initdb-no-pwprompt' was supplied.

This behaviour is more consistent with other PostgreSQL utilities such
as createuser.

Partial fix for GitHub issue #145.
2016-02-23 14:37:23 +09:00
5 changed files with 325 additions and 134 deletions

View File

@@ -215,6 +215,34 @@ command line options:
- `-b/--pg_bindir` - `-b/--pg_bindir`
### Command line options and environment variables
For some commands, e.g. `repmgr standby clone`, database connection parameters
need to be provided. Like other PostgreSQL utilities, following standard
parameters can be used:
- `-d/--dbname=DBNAME`
- `-h/--host=HOSTNAME`
- `-p/--port=PORT`
- `-U/--username=USERNAME`
If `-d/--dbname` contains an `=` sign or starts with a valid URI prefix (`postgresql://`
or `postgres://`), it is treated as a conninfo string. See the PostgreSQL
documentation for further details:
https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
Note that if a `conninfo` string is provided, values set in this will override any
provided as individual parameters. For example, with `-d 'host=foo' --host bar`, `foo`
will be chosen over `bar`.
Like other PostgreSQL utilities, `repmgr` will default to any values set in environment
variables if explicit command line parameters are not provided. See the PostgreSQL
documentation for further details:
https://www.postgresql.org/docs/current/static/libpq-envars.html
Setting up a simple replication cluster with repmgr Setting up a simple replication cluster with repmgr
--------------------------------------------------- ---------------------------------------------------
@@ -470,7 +498,11 @@ so should be used with care.
Further options can be passed to the `pg_basebackup` utility via Further options can be passed to the `pg_basebackup` utility via
the setting `pg_basebackup_options` in `repmgr.conf`. See the PostgreSQL the setting `pg_basebackup_options` in `repmgr.conf`. See the PostgreSQL
documentation for more details of available options: documentation for more details of available options:
<<<<<<< HEAD
http://www.postgresql.org/docs/current/static/app-pgbasebackup.html
=======
https://www.postgresql.org/docs/current/static/app-pgbasebackup.html https://www.postgresql.org/docs/current/static/app-pgbasebackup.html
>>>>>>> 72f9b0145afab1060dd1202c8f8937653c8b2e39
### Using rsync to clone a standby ### Using rsync to clone a standby
@@ -488,7 +520,6 @@ and destination server as the contents of files existing on both servers need
to be compared, meaning this method is not necessarily faster than making a to be compared, meaning this method is not necessarily faster than making a
fresh clone with `pg_basebackup`. fresh clone with `pg_basebackup`.
### Dealing with PostgreSQL configuration files ### Dealing with PostgreSQL configuration files
By default, `repmgr` will attempt to copy the standard configuration files By default, `repmgr` will attempt to copy the standard configuration files
@@ -503,6 +534,21 @@ which enables any valid `rsync` options to be passed to that command, e.g.:
rsync_options='--exclude=postgresql.local.conf' rsync_options='--exclude=postgresql.local.conf'
### Controlling `primary_conninfo` in `recovery.conf`
`repmgr` will create the `primary_conninfo` setting in `recovery.conf` based
on the connection parameters provided to `repmgr standby clone` and PostgreSQL's
standard connection defaults, including any environment variables set on the
local node.
To include specific connection parameters other than the standard host, port,
username and database values (e.g. `sslmode`), include these in a `conninfo`-style
tring passed to `repmgr` with `-d/--dbname` (see above for details), and/or set
appropriate environment variables.
Note that PostgreSQL will always set explicit defaults for `sslmode` and
`sslcompression`.
Setting up cascading replication with repmgr Setting up cascading replication with repmgr
-------------------------------------------- --------------------------------------------
@@ -1359,20 +1405,22 @@ which contains connection details for the local database.
`repmgr` or `repmgrd` will return one of the following error codes on program `repmgr` or `repmgrd` will return one of the following error codes on program
exit: exit:
* SUCCESS (0) Program ran successfully. * SUCCESS (0) Program ran successfully.
* ERR_BAD_CONFIG (1) Configuration file could not be parsed or was invalid * ERR_BAD_CONFIG (1) Configuration file could not be parsed or was invalid
* ERR_BAD_RSYNC (2) An rsync call made by the program returned an error * ERR_BAD_RSYNC (2) An rsync call made by the program returned an error (repmgr only)
* ERR_NO_RESTART (4) An attempt to restart a PostgreSQL instance failed * ERR_NO_RESTART (4) An attempt to restart a PostgreSQL instance failed
* ERR_DB_CON (6) Error when trying to connect to a database * ERR_DB_CON (6) Error when trying to connect to a database
* ERR_DB_QUERY (7) Error while executing a database query * ERR_DB_QUERY (7) Error while executing a database query
* ERR_PROMOTED (8) Exiting program because the node has been promoted to master * ERR_PROMOTED (8) Exiting program because the node has been promoted to master
* ERR_BAD_PASSWORD (9) Password used to connect to a database was rejected * ERR_STR_OVERFLOW (10) String overflow error
* ERR_STR_OVERFLOW (10) String overflow error * ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only)
* ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only) * ERR_BAD_SSH (12) Error when connecting to remote host via SSH (repmgr only)
* ERR_BAD_SSH (12) Error when connecting to remote host via SSH * ERR_SYS_FAILURE (13) Error when forking (repmgrd only)
* ERR_SYS_FAILURE (13) Error when forking (repmgrd only) * ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup (repmgr only)
* ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup * ERR_MONITORING_FAIL (16) Unrecoverable error encountered during monitoring (repmgrd only)
* ERR_MONITORING_FAIL (16) Unrecoverable error encountered during monitoring (repmgrd only) * ERR_BAD_BACKUP_LABEL (17) Corrupt or unreadable backup label encountered (repmgr only)
* ERR_SWITCHOVER_FAIL (18) Error encountered during switchover (repmgr only)
Support and Assistance Support and Assistance
---------------------- ----------------------

View File

@@ -29,7 +29,6 @@
#define ERR_DB_CON 6 #define ERR_DB_CON 6
#define ERR_DB_QUERY 7 #define ERR_DB_QUERY 7
#define ERR_PROMOTED 8 #define ERR_PROMOTED 8
#define ERR_BAD_PASSWORD 9
#define ERR_STR_OVERFLOW 10 #define ERR_STR_OVERFLOW 10
#define ERR_FAILOVER_FAIL 11 #define ERR_FAILOVER_FAIL 11
#define ERR_BAD_SSH 12 #define ERR_BAD_SSH 12

377
repmgr.c
View File

@@ -83,14 +83,14 @@
#define CLUSTER_CLEANUP 12 #define CLUSTER_CLEANUP 12
static bool create_recovery_file(const char *data_dir);
static int test_ssh_connection(char *host, char *remote_user); static int test_ssh_connection(char *host, char *remote_user);
static int copy_remote_files(char *host, char *remote_user, char *remote_path, static int copy_remote_files(char *host, char *remote_user, char *remote_path,
char *local_path, bool is_directory, int server_version_num); char *local_path, bool is_directory, int server_version_num);
static int run_basebackup(const char *data_dir, int server_version); static int run_basebackup(const char *data_dir, int server_version);
static void check_parameters_for_action(const int action); static void check_parameters_for_action(const int action);
static bool create_schema(PGconn *conn); static bool create_schema(PGconn *conn);
static void write_primary_conninfo(char *line); static bool create_recovery_file(const char *data_dir, PGconn *primary_conn);
static void write_primary_conninfo(char *line, PGconn *primary_conn);
static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line); static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line);
static void check_master_standby_version_match(PGconn *conn, PGconn *master_conn); static void check_master_standby_version_match(PGconn *conn, PGconn *master_conn);
static int check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string); static int check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string);
@@ -123,9 +123,15 @@ static bool copy_file(const char *old_filename, const char *new_filename);
static bool read_backup_label(const char *local_data_directory, struct BackupLabel *out_backup_label); static bool read_backup_label(const char *local_data_directory, struct BackupLabel *out_backup_label);
static void param_set(const char *param, const char *value);
/* Global variables */ /* Global variables */
static const char *keywords[6]; static PQconninfoOption *opts = NULL;
static const char *values[6];
static int param_count = 0;
static char **param_keywords;
static char **param_values;
static bool config_file_required = true; static bool config_file_required = true;
/* Initialization of runtime options */ /* Initialization of runtime options */
@@ -134,6 +140,7 @@ t_configuration_options options = T_CONFIGURATION_OPTIONS_INITIALIZER;
bool wal_keep_segments_used = false; bool wal_keep_segments_used = false;
bool connection_param_provided = false; bool connection_param_provided = false;
bool host_param_provided = false;
bool pg_rewind_supplied = false; bool pg_rewind_supplied = false;
static char *server_mode = NULL; static char *server_mode = NULL;
@@ -199,7 +206,9 @@ main(int argc, char **argv)
bool check_upstream_config = false; bool check_upstream_config = false;
bool config_file_parsed = false; bool config_file_parsed = false;
char *ptr = NULL; char *ptr = NULL;
const char *env;
PQconninfoOption *defs = NULL;
PQconninfoOption *def;
set_progname(argv[0]); set_progname(argv[0]);
@@ -216,43 +225,74 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
/* Initialise some defaults */
/* set default user */ param_count = 0;
env = getenv("PGUSER"); defs = PQconndefaults();
if (!env)
/* Count maximum number of parameters */
for (def = defs; def->keyword; def++)
param_count ++;
/* Initialize our internal parameter list */
param_keywords = pg_malloc0(sizeof(char *) * (param_count + 1));
param_values = pg_malloc0(sizeof(char *) * (param_count + 1));
for (c = 0; c <= param_count; c++)
{ {
struct passwd *pw = NULL; param_keywords[c] = NULL;
pw = getpwuid(geteuid()); param_values[c] = NULL;
if (pw) }
/*
* Pre-set any defaults, which can be overwritten if matching
* command line parameters are provided
*/
for (def = defs; def->keyword; def++)
{
if (def->val != NULL && def->val[0] != '\0')
{ {
env = pw->pw_name; param_set(def->keyword, def->val);
} }
else
if (strcmp(def->keyword, "host") == 0 &&
(def->val != NULL && def->val[0] != '\0'))
{ {
fprintf(stderr, _("could not get current user name: %s\n"), strerror(errno)); strncpy(runtime_options.host, def->val, MAXLEN);
exit(ERR_BAD_CONFIG); }
else if (strcmp(def->keyword, "hostaddr") == 0 &&
(def->val != NULL && def->val[0] != '\0'))
{
strncpy(runtime_options.host, def->val, MAXLEN);
}
else if (strcmp(def->keyword, "port") == 0 &&
(def->val != NULL && def->val[0] != '\0'))
{
strncpy(runtime_options.masterport, def->val, MAXLEN);
}
else if (strcmp(def->keyword, "dbname") == 0 &&
(def->val != NULL && def->val[0] != '\0'))
{
strncpy(runtime_options.dbname, def->val, MAXLEN);
}
else if (strcmp(def->keyword, "user") == 0 &&
(def->val != NULL && def->val[0] != '\0'))
{
strncpy(runtime_options.username, def->val, MAXLEN);
} }
} }
strncpy(runtime_options.username, env, MAXLEN);
/* set default database */ PQconninfoFree(defs);
env = getenv("PGDATABASE");
if (!env)
/*
* Though libpq will default to the username as dbname, PQconndefaults()
* doesn't return this
*/
if (runtime_options.dbname[0] == '\0')
{ {
env = runtime_options.username; strncpy(runtime_options.dbname, runtime_options.username, MAXLEN);
} }
strncpy(runtime_options.dbname, env, MAXLEN);
/* set default port */
env = getenv("PGPORT");
if (!env)
{
env = DEF_PGPORT_STR;
}
strncpy(runtime_options.masterport, env, MAXLEN);
/* Prevent getopt_long() from printing an error message */ /* Prevent getopt_long() from printing an error message */
opterr = 0; opterr = 0;
@@ -276,14 +316,18 @@ main(int argc, char **argv)
exit(SUCCESS); exit(SUCCESS);
case 'd': case 'd':
strncpy(runtime_options.dbname, optarg, MAXLEN); strncpy(runtime_options.dbname, optarg, MAXLEN);
/* we'll set the dbname parameter below if we detect it's not a conninfo string */
connection_param_provided = true; connection_param_provided = true;
break; break;
case 'h': case 'h':
strncpy(runtime_options.host, optarg, MAXLEN); strncpy(runtime_options.host, optarg, MAXLEN);
param_set("host", optarg);
connection_param_provided = true; connection_param_provided = true;
host_param_provided = true;
break; break;
case 'p': case 'p':
repmgr_atoi(optarg, "-p/--port", &cli_errors, false); repmgr_atoi(optarg, "-p/--port", &cli_errors, false);
param_set("port", optarg);
strncpy(runtime_options.masterport, strncpy(runtime_options.masterport,
optarg, optarg,
MAXLEN); MAXLEN);
@@ -291,6 +335,7 @@ main(int argc, char **argv)
break; break;
case 'U': case 'U':
strncpy(runtime_options.username, optarg, MAXLEN); strncpy(runtime_options.username, optarg, MAXLEN);
param_set("user", optarg);
connection_param_provided = true; connection_param_provided = true;
break; break;
case 'S': case 'S':
@@ -443,6 +488,77 @@ main(int argc, char **argv)
} }
} }
/*
* 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)
{
if(strncmp(runtime_options.dbname, "postgresql://", 13) == 0 ||
strncmp(runtime_options.dbname, "postgres://", 11) == 0 ||
strchr(runtime_options.dbname, '=') != NULL)
{
char *errmsg = NULL;
opts = PQconninfoParse(runtime_options.dbname, &errmsg);
if (opts == NULL)
{
PQExpBufferData conninfo_error;
initPQExpBuffer(&conninfo_error);
appendPQExpBuffer(&conninfo_error, _("error parsing conninfo:\n%s"), errmsg);
error_list_append(&cli_errors, conninfo_error.data);
termPQExpBuffer(&conninfo_error);
free(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(opt->keyword, opt->val);
}
if (strcmp(opt->keyword, "host") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.host, opt->val, MAXLEN);
host_param_provided = true;
}
if (strcmp(opt->keyword, "hostaddr") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.host, opt->val, MAXLEN);
host_param_provided = true;
}
else if (strcmp(opt->keyword, "port") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.masterport, opt->val, MAXLEN);
}
else if (strcmp(opt->keyword, "user") == 0 &&
(opt->val != NULL && opt->val[0] != '\0'))
{
strncpy(runtime_options.username, opt->val, MAXLEN);
}
}
}
}
else
{
param_set("dbname", runtime_options.dbname);
}
}
/* Exit here already if errors in command line options found */ /* Exit here already if errors in command line options found */
if (cli_errors.head != NULL) if (cli_errors.head != NULL)
@@ -450,7 +566,6 @@ main(int argc, char **argv)
exit_with_errors(); exit_with_errors();
} }
if (check_upstream_config == true) if (check_upstream_config == true)
{ {
do_check_upstream_config(); do_check_upstream_config();
@@ -620,14 +735,14 @@ main(int argc, char **argv)
} }
} }
keywords[2] = "user"; /* keywords[2] = "user";
values[2] = (runtime_options.username[0]) ? runtime_options.username : NULL; values[2] = (runtime_options.username[0]) ? runtime_options.username : NULL;
keywords[3] = "dbname"; keywords[3] = "dbname";
values[3] = runtime_options.dbname; values[3] = runtime_options.dbname;
keywords[4] = "application_name"; keywords[4] = "application_name";
values[4] = (char *) progname(); values[4] = (char *) progname();
keywords[5] = NULL; keywords[5] = NULL;
values[5] = NULL; values[5] = NULL;*/
/* /*
* Initialize the logger. If verbose command line parameter was input, * Initialize the logger. If verbose command line parameter was input,
@@ -1386,15 +1501,11 @@ do_standby_clone(void)
runtime_options.dest_dir); runtime_options.dest_dir);
} }
/* Connection parameters for master only */ param_set("application_name", options.node_name);
keywords[0] = "host";
values[0] = runtime_options.host;
keywords[1] = "port";
values[1] = runtime_options.masterport;
/* Connect to check configuration */ /* Connect to check configuration */
log_info(_("connecting to upstream node\n")); log_info(_("connecting to upstream node\n"));
upstream_conn = establish_db_connection_by_params(keywords, values, true); upstream_conn = establish_db_connection_by_params((const char**)param_keywords, (const char**)param_values, true);
/* Verify that upstream node is a supported server version */ /* Verify that upstream node is a supported server version */
log_verbose(LOG_INFO, _("connected to upstream node, checking its state\n")); log_verbose(LOG_INFO, _("connected to upstream node, checking its state\n"));
@@ -2079,7 +2190,7 @@ stop_backup:
if (server_version_num >= 90400 && if (server_version_num >= 90400 &&
backup_label.min_failover_slot_lsn == InvalidXLogRecPtr) backup_label.min_failover_slot_lsn == InvalidXLogRecPtr)
{ {
maxlen_snprintf(dirpath, "%s/pg_replslot/*", maxlen_snprintf(dirpath, "%s/pg_replslot/",
local_data_directory); local_data_directory);
log_debug("deleting pg_replslot directory contents\n"); log_debug("deleting pg_replslot directory contents\n");
@@ -2101,7 +2212,7 @@ stop_backup:
} }
/* Finally, write the recovery.conf file */ /* Finally, write the recovery.conf file */
create_recovery_file(local_data_directory); create_recovery_file(local_data_directory, upstream_conn);
if (runtime_options.rsync_only) if (runtime_options.rsync_only)
{ {
@@ -2533,7 +2644,7 @@ do_standby_follow(void)
* to determine primary, and carry out some other checks while we're * to determine primary, and carry out some other checks while we're
* at it. * at it.
*/ */
if ( *runtime_options.host == '\0') if (host_param_provided == false)
{ {
/* We need to connect to check configuration */ /* We need to connect to check configuration */
log_info(_("connecting to standby database\n")); log_info(_("connecting to standby database\n"));
@@ -2596,12 +2707,7 @@ do_standby_follow(void)
/* primary server info explictly provided - attempt to connect to that */ /* primary server info explictly provided - attempt to connect to that */
else else
{ {
keywords[0] = "host"; master_conn = establish_db_connection_by_params((const char**)param_keywords, (const char**)param_values, true);
values[0] = runtime_options.host;
keywords[1] = "port";
values[1] = runtime_options.masterport;
master_conn = establish_db_connection_by_params(keywords, values, true);
master_id = get_master_node_id(master_conn, options.cluster_name); master_id = get_master_node_id(master_conn, options.cluster_name);
@@ -2620,16 +2726,6 @@ do_standby_follow(void)
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
/*
* set the host and masterport variables with the master ones before
* closing the connection because we will need them to recreate the
* recovery.conf file
*/
strncpy(runtime_options.host, PQhost(master_conn), MAXLEN);
strncpy(runtime_options.masterport, PQport(master_conn), MAXLEN);
strncpy(runtime_options.username, PQuser(master_conn), MAXLEN);
/* /*
* If 9.4 or later, and replication slots in use, we'll need to create a * If 9.4 or later, and replication slots in use, we'll need to create a
* slot on the new master * slot on the new master
@@ -2667,7 +2763,7 @@ do_standby_follow(void)
log_info(_("changing standby's master\n")); log_info(_("changing standby's master\n"));
/* write the recovery.conf file */ /* write the recovery.conf file */
if (!create_recovery_file(data_dir)) if (!create_recovery_file(data_dir, master_conn))
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
/* Finally, restart the service */ /* Finally, restart the service */
@@ -3707,12 +3803,6 @@ do_witness_create(void)
char repmgr_user[MAXLEN]; char repmgr_user[MAXLEN];
char repmgr_db[MAXLEN]; char repmgr_db[MAXLEN];
/* Connection parameters for master only */
keywords[0] = "host";
values[0] = runtime_options.host;
keywords[1] = "port";
values[1] = runtime_options.masterport;
/* /*
* Extract the repmgr user and database names from the conninfo string * Extract the repmgr user and database names from the conninfo string
* provided in repmgr.conf * provided in repmgr.conf
@@ -3721,7 +3811,8 @@ do_witness_create(void)
get_conninfo_value(options.conninfo, "dbname", repmgr_db); get_conninfo_value(options.conninfo, "dbname", repmgr_db);
/* We need to connect to check configuration and copy it */ /* We need to connect to check configuration and copy it */
masterconn = establish_db_connection_by_params(keywords, values, true); masterconn = establish_db_connection_by_params((const char**)param_keywords, (const char**)param_values, true);
if (!masterconn) if (!masterconn)
{ {
/* No event logging possible here as we can't connect to the master */ /* No event logging possible here as we can't connect to the master */
@@ -4171,8 +4262,6 @@ do_witness_create(void)
static void static void
do_help(void) do_help(void)
{ {
const char *host;
printf(_("%s: replication management tool for PostgreSQL\n"), progname()); printf(_("%s: replication management tool for PostgreSQL\n"), progname());
printf(_("\n")); printf(_("\n"));
printf(_("Usage:\n")); printf(_("Usage:\n"));
@@ -4192,8 +4281,10 @@ do_help(void)
printf(_("\n")); printf(_("\n"));
printf(_("Connection options:\n")); printf(_("Connection options:\n"));
printf(_(" -d, --dbname=DBNAME database to connect to (default: \"%s\")\n"), runtime_options.dbname); printf(_(" -d, --dbname=DBNAME database to connect to (default: \"%s\")\n"), runtime_options.dbname);
host = getenv("PGHOST"); printf(_(" -h, --host=HOSTNAME database server host"));
printf(_(" -h, --host=HOSTNAME database server host or socket directory (default: \"%s\")\n"), host ? host : _("local socket")); 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.masterport); printf(_(" -p, --port=PORT database server port (default: \"%s\")\n"), runtime_options.masterport);
printf(_(" -U, --username=USERNAME database user name to connect as (default: \"%s\")\n"), runtime_options.username); printf(_(" -U, --username=USERNAME database user name to connect as (default: \"%s\")\n"), runtime_options.username);
printf(_("\n")); printf(_("\n"));
@@ -4250,7 +4341,7 @@ do_help(void)
* Creates a recovery file for a standby. * Creates a recovery file for a standby.
*/ */
static bool static bool
create_recovery_file(const char *data_dir) create_recovery_file(const char *data_dir, PGconn *primary_conn)
{ {
FILE *recovery_file; FILE *recovery_file;
char recovery_file_path[MAXLEN]; char recovery_file_path[MAXLEN];
@@ -4276,7 +4367,7 @@ create_recovery_file(const char *data_dir)
log_debug(_("recovery.conf: %s"), line); log_debug(_("recovery.conf: %s"), line);
/* primary_conninfo = '...' */ /* primary_conninfo = '...' */
write_primary_conninfo(line); write_primary_conninfo(line, primary_conn);
if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false)
return false; return false;
@@ -4490,11 +4581,25 @@ run_basebackup(const char *data_dir, int server_version)
appendPQExpBuffer(&params, " -D %s", data_dir); appendPQExpBuffer(&params, " -D %s", data_dir);
if (opts != NULL && strlen(runtime_options.dbname))
{
appendPQExpBuffer(&params, " -d '%s'", runtime_options.dbname);
}
if (strlen(runtime_options.host)) if (strlen(runtime_options.host))
{ {
appendPQExpBuffer(&params, " -h %s", runtime_options.host); appendPQExpBuffer(&params, " -h %s", runtime_options.host);
} }
/*
* XXX -p and -U will be duplicated if provided in conninfo string
* but not as explict repmgr command line parameters, e.g.:
*
* pg_basebackup -l "repmgr base backup" -d 'host=localhost port=5501 dbname=repmgr user=repmgr' -p 5501 -U repmgr -X stream
*
* this is ugly but harmless; we should fix it some time
*
*/
if (strlen(runtime_options.masterport)) if (strlen(runtime_options.masterport))
{ {
appendPQExpBuffer(&params, " -p %s", runtime_options.masterport); appendPQExpBuffer(&params, " -p %s", runtime_options.masterport);
@@ -4650,7 +4755,7 @@ check_parameters_for_action(const int action)
error_list_append(&cli_errors, _("master hostname (-h/--host) required when executing STANDBY FOLLOW with -D/--data-dir option")); error_list_append(&cli_errors, _("master hostname (-h/--host) required when executing STANDBY FOLLOW with -D/--data-dir option"));
} }
if (!runtime_options.dest_dir[0]) if (host_param_provided && !runtime_options.dest_dir[0])
{ {
error_list_append(&cli_errors, _("local data directory (-D/--data-dir) required when executing STANDBY FOLLOW with -h/--host option")); error_list_append(&cli_errors, _("local data directory (-D/--data-dir) required when executing STANDBY FOLLOW with -h/--host option"));
} }
@@ -4679,12 +4784,14 @@ check_parameters_for_action(const int action)
case STANDBY_SWITCHOVER: case STANDBY_SWITCHOVER:
/* allow all parameters to be supplied */ /* allow all parameters to be supplied */
break; break;
case STANDBY_ARCHIVE_CONFIG: case STANDBY_ARCHIVE_CONFIG:
if (strcmp(runtime_options.config_archive_dir, "") == 0) if (strcmp(runtime_options.config_archive_dir, "") == 0)
{ {
error_list_append(&cli_errors, _("--config-archive-dir required when executing STANDBY ARCHIVE_CONFIG")); error_list_append(&cli_errors, _("--config-archive-dir required when executing STANDBY ARCHIVE_CONFIG"));
} }
break; break;
case STANDBY_RESTORE_CONFIG: case STANDBY_RESTORE_CONFIG:
if (strcmp(runtime_options.config_archive_dir, "") == 0) if (strcmp(runtime_options.config_archive_dir, "") == 0)
{ {
@@ -4698,6 +4805,7 @@ check_parameters_for_action(const int action)
config_file_required = false; config_file_required = false;
break; break;
case WITNESS_CREATE: case WITNESS_CREATE:
/* Require data directory */ /* Require data directory */
if (strcmp(runtime_options.dest_dir, "") == 0) if (strcmp(runtime_options.dest_dir, "") == 0)
@@ -4706,9 +4814,11 @@ check_parameters_for_action(const int action)
} }
/* allow all parameters to be supplied */ /* allow all parameters to be supplied */
break; break;
case CLUSTER_SHOW: case CLUSTER_SHOW:
/* allow all parameters to be supplied */ /* allow all parameters to be supplied */
break; break;
case CLUSTER_CLEANUP: case CLUSTER_CLEANUP:
/* allow all parameters to be supplied */ /* allow all parameters to be supplied */
break; break;
@@ -5056,46 +5166,45 @@ create_schema(PGconn *conn)
} }
/* This function uses global variables to determine connection settings. Special
* usage of the PGPASSWORD variable is handled, but strongly discouraged */
static void static void
write_primary_conninfo(char *line) write_primary_conninfo(char *line, PGconn *primary_conn)
{ {
char host_buf[MAXLEN] = ""; PQconninfoOption *connOptions;
char conn_buf[MAXLEN] = ""; PQconninfoOption *option;
char user_buf[MAXLEN] = ""; PQExpBufferData conninfo_buf;
char appname_buf[MAXLEN] = ""; bool application_name_provided = false;
char password_buf[MAXLEN] = "";
/* Environment variable for password (UGLY, please use .pgpass!) */ connOptions = PQconninfo(primary_conn);
const char *password = getenv("PGPASSWORD");
if (password != NULL) initPQExpBuffer(&conninfo_buf);
for (option = connOptions; option && option->keyword; option++)
{ {
maxlen_snprintf(password_buf, " password=%s", password); /*
* Skip empty settings and ones which don't make any sense in
* recovery.conf
*/
if (strcmp(option->keyword, "dbname") == 0 ||
strcmp(option->keyword, "replication") == 0 ||
(option->val == NULL) ||
(option->val != NULL && option->val[0] == '\0'))
continue;
if (conninfo_buf.len != 0)
appendPQExpBufferChar(&conninfo_buf, ' ');
if (strcmp(option->keyword, "application_name") == 0)
application_name_provided = true;
/* XXX escape option->val */
appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, option->val);
} }
if (runtime_options.host[0]) /* `application_name` not provided - default to repmgr node name */
{ if (application_name_provided == false)
maxlen_snprintf(host_buf, " host=%s", runtime_options.host); appendPQExpBuffer(&conninfo_buf, " application_name=%s", options.node_name);
}
if (runtime_options.username[0]) maxlen_snprintf(line, "primary_conninfo = '%s'\n", conninfo_buf.data);
{
maxlen_snprintf(user_buf, " user=%s", runtime_options.username);
}
if (options.node_name[0])
{
maxlen_snprintf(appname_buf, " application_name=%s", options.node_name);
}
maxlen_snprintf(conn_buf, "port=%s%s%s%s%s",
(runtime_options.masterport[0]) ? runtime_options.masterport : DEF_PGPORT_STR,
host_buf, user_buf, password_buf,
appname_buf);
maxlen_snprintf(line, "primary_conninfo = '%s'\n", conn_buf);
} }
@@ -5484,17 +5593,11 @@ do_check_upstream_config(void)
parse_config(&options); parse_config(&options);
/* Connection parameters for upstream server only */
keywords[0] = "host";
values[0] = runtime_options.host;
keywords[1] = "port";
values[1] = runtime_options.masterport;
keywords[2] = "dbname";
values[2] = runtime_options.dbname;
/* We need to connect to check configuration and start a backup */ /* We need to connect to check configuration and start a backup */
log_info(_("connecting to upstream server\n")); log_info(_("connecting to upstream server\n"));
conn = establish_db_connection_by_params(keywords, values, true);
conn = establish_db_connection_by_params((const char**)param_keywords, (const char**)param_values, true);
/* Verify that upstream server is a supported server version */ /* Verify that upstream server is a supported server version */
log_verbose(LOG_INFO, _("connected to upstream server, checking its state\n")); log_verbose(LOG_INFO, _("connected to upstream server, checking its state\n"));
@@ -5695,3 +5798,45 @@ copy_file(const char *old_filename, const char *new_filename)
return true; return true;
} }
static void
param_set(const char *param, const char *value)
{
int c;
int value_len = strlen(value) + 1;
/*
* Scan array to see if the parameter is already set - if so replace it
*/
for (c = 0; c <= param_count && param_keywords[c] != NULL; c++)
{
if (strcmp(param_keywords[c], param) == 0)
{
if (param_values[c] != NULL)
pfree(param_values[c]);
param_values[c] = pg_malloc0(value_len);
strncpy(param_values[c], value, value_len);
return;
}
}
/*
* Parameter not in array - add it and its associated value
*/
if (c < param_count)
{
int param_len = strlen(param) + 1;
param_keywords[c] = pg_malloc0(param_len);
param_values[c] = pg_malloc0(value_len);
strncpy(param_keywords[c], param, param_len);
strncpy(param_values[c], value, value_len);
}
/*
* It's theoretically possible a parameter couldn't be added as
* the array is full, but it's highly improbable so we won't
* handle it at the moment.
*/
}

View File

@@ -1121,7 +1121,6 @@ standby_monitor(void)
last_xlog_receive_location, last_xlog_receive_location,
replication_lag, replication_lag,
apply_lag); apply_lag);
/* /*
* Execute the query asynchronously, but don't check for a result. We will * Execute the query asynchronously, but don't check for a result. We will
* check the result next time we pause for a monitor step. * check the result next time we pause for a monitor step.

View File

@@ -1,6 +1,6 @@
#ifndef _VERSION_H_ #ifndef _VERSION_H_
#define _VERSION_H_ #define _VERSION_H_
#define REPMGR_VERSION "3.2dev" #define REPMGR_VERSION "3.1.4"
#endif #endif