Compare commits

..

165 Commits

Author SHA1 Message Date
Jaime Casanova
efd50f11ac Fix HISTORY to show from newest to oldest 2012-07-27 11:30:35 -05:00
Jaime Casanova
45a39084ed Prepare release notes for release 2012-07-21 11:56:00 -05:00
Jaime Casanova
94c73a016f Add a release note that was missing 2012-07-05 09:36:42 -05:00
Jaime Casanova
be5cbe4ddd Improve the version message to actually show the repmgr version not
only postgresql's one
2012-06-25 22:53:16 -05:00
Jaime Casanova
30d35d5b4c Names on history file are without surnames when they are well-known
so, keep it that way
2012-06-13 00:48:34 -05:00
Jaime Casanova
fa889a11ac Remove now finished TODO item about having a sanity check for ssh 2012-06-13 00:43:00 -05:00
Jaime Casanova
f4087d0a32 Add a \n in a message 2012-06-12 23:41:31 -05:00
Jaime Casanova
a55d7a4bd3 getMasterConnection() cannot avoid checking the same node that asks
to find the master.
This was a micro optimization based on the fact that all commands
that needed to detect the master were executed from the standby
but now that we have CLUSTER level commands that is not true anymore
2012-06-12 23:23:49 -05:00
Jaime Casanova
5d8cf6abe0 Allow repmgr to obtain tablespace's locations from pg 9.2 and later
in which we no longer have a spclocation column in pg_tablespaces
2012-06-12 10:49:23 -05:00
Jaime Casanova
9caa243354 Moving the 'Starting backup' message to a better place 2012-06-12 09:44:06 -05:00
Jaime Casanova
6880483947 STANDBY CLONE should be run by a SUPERUSER, otherwise we won't be able
to retrieve data_directory and the other parameters we need by
querying the database.
2012-06-12 09:37:03 -05:00
Jaime Casanova
3d89fdadab Fix a typo in a message 2012-06-12 09:28:27 -05:00
Cédric Villemain
6e9e4e05ae Add test_ssh_connection
The feature was written by Jaime and reworked by me to fix
https://github.com/greg2ndQuadrant/repmgr/issues/5
2012-05-09 15:24:38 -05:00
Jaime Casanova
17a160e970 Correct credits 2012-05-09 14:59:00 -05:00
Jaime Casanova
e0e01aa9db Add Carlo to CREDITS 2012-04-27 02:09:48 -05:00
Jaime Casanova
b09eff9f76 Avoid the possibility of a double free. Fix by Carlo Ascani 2012-04-27 02:08:40 -05:00
Jaime Casanova
3c5d82b9ef Complete CREDITS and HISTORY for release 2012-04-27 02:07:42 -05:00
Jaime Casanova
257dbc4f42 Cleanup of patch that introduces write_primary_conninfo() 2012-04-27 01:23:37 -05:00
Jaime Casanova
2a64099163 Added function "write_primary_conninfo" which now adds the username to the primary_conninfo parameter in recovery.conf 2012-04-27 01:10:24 -05:00
Jaime Casanova
41c05bea7b Fix CLUSTER SHOW, that i have broken 2012-04-26 13:58:39 -05:00
Jaime Casanova
7d76d86e19 Add debug information to CLUSTER SHOW y CLUSTER CLEANUP 2012-04-26 13:31:54 -05:00
Jaime Casanova
36d5b5bc24 I need a local connection to get the master of the cluster 2012-04-26 12:29:04 -05:00
Jaime Casanova
c543402d65 A typo that escaped to my previous review 2012-04-26 12:23:24 -05:00
Jaime Casanova
d0959b953e Cleanup patch about CLUSTER CLEANUP 2012-04-26 12:21:41 -05:00
Jaime Casanova
0660bded0b Fix a switch in which a "break" was missing that makes always that --force option was used end up in the default section and error. 2012-04-19 12:21:49 -05:00
Jaime Casanova
209a0c64d2 Add documentation about both CLUSTER SHOW and CLUSTER CLEANUP commands 2012-04-14 21:56:58 -05:00
Jaime Casanova
fd76ec6283 Adds a CLUSTER CLEANUP command to clean monitor's history,
also include a --keep-history (-k) option to indicate how many
days of history to keep
2012-04-14 21:34:06 -05:00
Jaime Casanova
7d579cf71f Add CLUSTER SHOW command to show the current nodes configured 2012-04-14 21:11:51 -05:00
Jaime Casanova
d790ef740b Add a paragraph in the docs describing how to clean history 2012-04-11 10:54:22 -05:00
Jaime Casanova
aa6633b027 Complete the lists of error codes that repmgr can return in the README.rst 2012-04-11 10:38:22 -05:00
Jaime Casanova
c3bffce379 Run astyle to format code before tagging the release 2012-04-11 10:35:37 -05:00
Jaime Casanova
78aea00a6d Avoid to show what segments are needed for this backup if the rsync failed 2012-04-11 10:34:38 -05:00
Jaime Casanova
91601204b5 Remove last argument from log_err, left in commit 9b8fb7e960.
Also rephrase the sentence

Reported by Jeroen Dekkers
2011-11-28 17:26:19 -05:00
Jaime Casanova
c91ddc2f5e Fix a wrong message.
It was saying the problem is the version of the PostgreSQL server while
it actually is because the MASTER REGISTER command was running on a
standby node
2011-11-10 09:30:42 -05:00
Jaime Casanova
72f74dd7a7 Fix a typo introduced in commit 94c9c3a5c6 2011-11-03 12:54:55 -05:00
Jaime Casanova
901d07fa92 Improve performance of the repl_status view 2011-10-20 23:20:03 -05:00
Greg Smith
f0e609bcd4 Add strnlen on platforms that don't have it, such as OS X 2011-10-20 17:04:29 -05:00
Jaime Casanova
94c9c3a5c6 Let the clone happen in a session with synchronous_commit off. This
is because in pg 9.1 the default configuration can easily allow sync
rep to be activated even if no standby is present and will block
pg_start_backup() and pg_stop_backup() in that case.

Also remove a second connection we were opening to execute
pg_stop_backup(), i'm not sure why that was there but now it was
a problem because it was another session and not the one we set here.
2011-10-03 13:56:31 -05:00
Cédric Villemain
3af5243bcc Fix rsync return code test 2011-08-24 09:14:22 -05:00
Cédric Villemain
85bbae462a Add --ignore-rsync-warning to README 2011-08-22 00:34:01 -05:00
Cédric Villemain
14e49d41c2 Add --ignore-rsync-warning command line option
This fix the rsync return code in case there are vanished files.

Common situation are DROPed tables and TEMPorary object deletion and
are handled by PostgreSQL.
But as it may exist situation where an external process delete files in
the PGDATA the flag is off by default.

XXX 2 items :

 * is -I a good choice ? maybe we need to prevent future --ignore-foo and
   add something like : --ignore=rsync_warning -I rsync_warning
 * the warning message is not enough explicit with the risk involved by
   --force usage
2011-08-22 00:32:40 -05:00
Cédric Villemain
1bd8a703c8 Fix getopt for ignore-rsync-warning
The change was loosed during merge and not checked in master/
2011-06-06 20:56:45 -04:00
Greg Smith
3ca0f2db06 Fix unapplied astyle. Closes gh-26. 2011-04-05 17:21:48 -04:00
Greg Smith
4c792c8013 Add TODO, update history for 1.1.0 release 2011-03-09 15:27:29 -05:00
Greg Smith
e7903a7971 Merge branch 'master' of git+ssh://git@git.2ndquadrant.it/git/repmgr 2011-03-03 09:27:18 -05:00
Greg Smith
f372793114 Merge correction commit 2011-03-03 09:26:22 -05:00
trbs
295a183d0b removing old code which is made invalid after merging the different branches by upstream 2011-03-03 09:25:27 -05:00
trbs
c6693340c1 cleanup help and usage 2011-03-03 09:21:46 -05:00
trbs
cc0c516810 added new lines to log messages 2011-03-03 09:21:38 -05:00
Jaime Casanova
58ec43106e Options -U, -R and -p shouldn't be mandatory. Fix by checking if the string
is empty before use.
2011-03-02 02:44:01 -05:00
Greg Smith
3e960286c7 Update history for 1.1.0-beta1 2011-02-24 19:25:40 -05:00
Greg Smith
bf0bbd747b Fix typos in some logging messages 2011-02-24 19:05:03 -05:00
Greg Smith
459f14b594 Document repmgrd's role on each type of node. Add
logging (when run in verbose mode) showing what
decisions repmgrd is making after it determines
what type of node it is.
2011-02-24 18:58:03 -05:00
Greg Smith
f928edf897 Improve parameter validation, logging, and error
handling in repmgrd
2011-02-24 18:13:16 -05:00
Greg Smith
c7b62003b2 Reorganize README to be easier to follow 2011-02-23 09:50:07 -05:00
Greg Smith
59b7b90169 Update README with better two-node example output 2011-02-23 09:08:08 -05:00
Greg Smith
3cdd6a57fd Add more info level logging around database activity 2011-02-23 08:42:49 -05:00
Greg Smith
5dcec5818f Update README with standard two-host examples 2011-02-23 08:42:06 -05:00
Greg Smith
9c6288993b Fix double free in standby clone 2011-02-23 07:26:39 -05:00
Greg Smith
2675101330 Make PGPASSWORD substitution in recovery.conf optional 2011-02-23 07:19:36 -05:00
Greg Smith
02daccd6e7 Wrap use of PQconnectdbParams, add proper error
checking and logging when the connection fails.
2011-02-23 06:44:10 -05:00
Greg Smith
18ef5b3cf3 Convert more uses of fprintf to use logger 2011-02-23 05:59:09 -05:00
Greg Smith
3b2203c38c Make verbose command line option useful on top of
the new logging infrastructure.
2011-02-23 05:29:29 -05:00
Greg Smith
fee7da1ba7 Fix cast on WAL lag computation to (long long) 2011-02-22 07:43:02 -05:00
Greg Smith
a98aec12e1 And one more round of astyle work 2011-02-15 00:57:12 -05:00
Greg Smith
757457ad85 Fix merge error that removed setting keywords 2011-02-15 00:55:50 -05:00
Greg Smith
45022debc6 Break out error codes, cleanup header files and HISTORY 2011-02-15 00:43:36 -05:00
Greg Smith
f6618a01c7 Post-merge astyle run 2011-02-15 00:14:13 -05:00
Greg Smith
20af4ffc2c Merge commit '3ef1fa126d9c9b9ba3b29deab7f67218cdf7ce10'
Conflicts:
	.gitignore
	Makefile
	README.rst
	check_dir.c
	config.c
	config.h
	dbutils.h
	repmgr.c
	repmgr.conf
	repmgr.h
	repmgrd.c
2011-02-15 00:06:01 -05:00
Greg Smith
3ef1fa126d astyle run against Heroku code 2011-02-14 21:51:14 -05:00
Greg Smith
ce06e6c8e0 Fix some mistakes in the history file 2011-02-10 19:34:50 -05:00
Greg Smith
0ad3d91e9c Cleanup history and credits 2011-02-10 18:57:50 -05:00
Greg Smith
0130420947 astyle cleanup run after recent merges 2011-02-10 18:55:51 -05:00
Greg Smith
c3af4274ae Document astyle command line used to standardize
code formatting
2011-02-10 18:55:17 -05:00
Greg Smith
5460517a43 Protect against duplicate header inclusion 2011-02-10 18:35:56 -05:00
Greg Smith
1d32133d49 Update all the copyrights to add 2011 2011-02-10 18:33:02 -05:00
Greg Smith
1be62fa6b2 Fix use of "options" broken by recent merging 2011-02-10 18:26:44 -05:00
Charles Duffy
b62ad056f1 repmgrd: log restored connection only after a break 2011-02-10 17:57:13 -05:00
Greg Smith
ec30caef0f Merge commit '78b925ea54bb7a131e97c2f5d0a63f54ca58e26f' 2011-02-10 17:52:50 -05:00
Greg Smith
78b925ea54 Unify use of error codes across all exit statements 2011-02-10 17:49:34 -05:00
Charles Duffy
7e7c45c61d exit with status 2 if manual work is needed during standby_clone 2011-02-10 17:08:43 -05:00
Charles Duffy
a02cd43115 repmgr: treat directory access errors during standby clone as failure 2011-02-10 17:03:00 -05:00
Charles Duffy
a67688f198 minor format string and comment fixes (add missing newlines, fix english usage, format string argument order) 2011-02-10 17:01:59 -05:00
Charles Duffy
f74b806e4f repmgr: exit with nonzero status on obvious errors 2011-02-10 17:00:33 -05:00
Charles Duffy
657aa3013b avoid double free on repmgrd exit as master 2011-02-10 16:40:00 -05:00
Greg Smith
8d2aff558d Update history with recent merges 2011-02-10 16:19:35 -05:00
Gabriele Bartolini
6e1306cfb2 Uses a switch to detect the log level 2011-02-10 16:09:46 -05:00
Gabriele Bartolini
308efba95e added explanations about logging 2011-02-10 16:09:46 -05:00
Gabriele Bartolini
556f256bb6 Now standby clone command accepts configuration file - for logging purposes only 2011-02-10 16:08:07 -05:00
Gabriele Bartolini
83eee2ed96 added some messages to the clone command 2011-02-10 16:08:07 -05:00
Gabriele Bartolini
cb36aaddff - Added \n 2011-02-10 16:08:07 -05:00
Gabriele Bartolini
3146d2c676 fixed some bugs with standby clone 2011-02-10 16:08:07 -05:00
Gabriele Bartolini
3f1c6a5852 Removed any sprintf/strcpy call and use snprintf/strncpy - Fixed bug with tblspc_dir - added debug messages after every query 2011-02-10 16:08:07 -05:00
Gabriele Bartolini
716a0ae9d3 removed any malloc operation, added t_runtime_options struct 2011-02-10 16:05:03 -05:00
Gabriele Bartolini
7a3be3a2f9 Cleaned log messages (\n at the end of every line) 2011-02-10 15:58:55 -05:00
Gabriele Bartolini
f6a6632169 Added new log system to both repmgr and repmgrd. Needs cleaning, but it is a good starting point 2011-02-10 15:58:55 -05:00
Gabriele Bartolini
2c1eafd7a9 first alpha version for syslog support 2011-02-10 15:56:10 -05:00
Greg Smith
1787cf1c21 Update credits and history to reflect recent merges 2011-02-10 15:19:00 -05:00
Greg Smith
287e2c1a67 Tweak .gitignore to ignore more doc build artifacts 2011-02-10 15:09:18 -05:00
trbs
b487772b84 added note about postgresql-server-dev-9.0 and use libxslt-dev instead of version specific package name 2011-02-10 15:06:09 -05:00
trbs
52313a2d6e made rsync_options configurable 2011-02-10 15:06:09 -05:00
trbs
814863edf2 use struct for config file information 2011-02-10 15:06:09 -05:00
trbs
e4f431c355 fix line 2011-02-10 15:06:09 -05:00
trbs
c3816d8586 added readme.html to gitignore 2011-02-10 15:05:07 -05:00
trbs
4ea0fc4214 Updated README with Debian/Ubuntu install information 2011-02-10 15:05:07 -05:00
trbs
03ed0e692a fix strformat order reversal in error message 2011-02-10 15:00:44 -05:00
Daniel Farina
ce21291418 Issue a message when a configuration file is passed unnecessarily
Simply intends to set expectations around the fact that a config file
is not used *and* doesn't affect anything material about the standby
clone command.

This uses a notion of "logging level" (like NOTICE) that is not yet
seen this program, but I'll probably introduce that soon.

Signed-off-by: Daniel Farina <daniel@heroku.com>
2011-02-10 03:19:36 -08:00
Daniel Farina
4b007bf4bc Use a better check for when to issue an error message
This patch changes raising the error "You need to use connection
parameters to the master when issuing a STANDBY CLONE command." to
only occur when no host (a global variable, that is abused for at
least two purposes, but is okay in this case) is passed, rather than
when no config file is passed, which is fine for standby clone (which
itself is a wrapper for rsync)

Signed-off-by: Daniel Farina <daniel@heroku.com>
2011-02-10 03:19:30 -08:00
Daniel Farina
da82829659 Avoid checking config file when performing standby clone action
Signed-off-by: Daniel Farina <daniel@heroku.com>
2011-02-10 02:10:58 -08:00
Dan Farina
ebbb7c3a47 Fix more quoting: used an overly-quoted copy
Signed-off-by: Dan Farina <daniel@heroku.com>
2011-02-07 03:59:18 -08:00
Dan Farina
630d716819 Use the proper freeing method
As according to
http://www.postgresql.org/docs/9.0/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING

Signed-off-by: Dan Farina <daniel@heroku.com>
2011-02-07 03:59:18 -08:00
Dan Farina
c1b84fe9fc Make some fast hacks to quote schema names
Signed-off-by: Dan Farina <drfarina@acm.org>
2011-02-07 03:59:18 -08:00
Dan Farina
bfd3aed33b Fix a myriad of problems introduced by merging
Signed-off-by: Dan Farina <drfarina@acm.org>
2011-02-06 18:32:49 -08:00
Dan Farina
c356f9e97f Merge remote-tracking branch 'tbrs/master' into heroku
Grab the configuration struct changes. It was expeditious to un-do
some of my by-hand line-wrapping that avoids 80 character limit,
though.

Conflicts:
	config.c
	config.h
	repmgr.c
	repmgr.h
	repmgrd.c
2011-02-04 18:35:56 -08:00
Dan Farina
a52ebc412c Merge remote-tracking branch 'origin/master' into heroku
The Great Whitespace Reconciliation

Conflicts:
	check_dir.c
	config.c
	dbutils.c
	repmgr.c
	repmgr.h
	repmgrd.c

Signed-off-by: Dan Farina <drfarina@acm.org>
2011-02-04 18:27:28 -08:00
Cédric Villemain
fbf97807b8 Remove spurious file 2011-01-27 21:33:09 +01:00
Hannu Krosing
3cce9efac0 Merge branch 'master' of git+ssh://git.2ndquadrant.it/git/repmgr 2011-01-27 10:04:25 +02:00
Hannu Krosing
ec8678379f Makefile changes for building debian packages, left out of previous commit 2011-01-27 10:02:59 +02:00
Cédric Villemain
d017edbe47 Adding information for debian and --version test 2011-01-27 02:11:05 +01:00
Hannu Krosing
f41ba482eb dded section on building on Debian and Ubuntu 2011-01-27 01:09:18 +02:00
Hannu Krosing
4c5fad2070 simple support for creating deb file 2011-01-27 00:32:52 +02:00
Greg Smith
0dd31b09dd Additional notes on 32 vs. 64 build issues 2011-01-22 09:59:43 -05:00
Greg Smith
943dda0ad0 Document potential build issues on RPM 2011-01-22 09:09:37 -05:00
Greg Smith
d4de19bc78 Doc warning: need server restart after config change 2011-01-17 15:38:44 -05:00
trbs
1999b534fd added note about postgresql-server-dev-9.0 and use libxslt-dev instead of version specific package name 2011-01-11 18:24:17 +01:00
trbs
57169f51cf made rsync_options configurable 2011-01-08 01:46:39 +01:00
trbs
fc2405f17d use struct for config file information 2011-01-07 01:36:46 +01:00
trbs
4a0e827464 fix line 2011-01-07 01:32:31 +01:00
trbs
745392e3a2 added readme.html to gitignore 2011-01-06 23:46:21 +01:00
Jaime Casanova
170f519d84 Fixing a message, shouldn't show progname in the middle of a line.
Is one message not two.
2010-12-29 00:45:33 -05:00
trbs
cf5717ad59 Updated README with Debian/Ubuntu install information 2010-12-23 15:02:23 +01:00
trbs
cb1192b912 fix strformat order reversal in error message 2010-12-23 15:02:02 +01:00
Dan Farina
156714f3f1 Fix quoting misbehavior
Move the single quote in the formatting string to the end of the line.

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-23 01:04:05 -08:00
Dan Farina
dd5ac660bf Comments and cleanup
Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-23 00:55:39 -08:00
Dan Farina
3a430397dc Avoid overwriting the new password-in-recovery.conf formatting
Previously: did that work, then threw it away/overwrote it.

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-23 00:55:39 -08:00
Dan Farina
1f098c60ac Remove dead-ish code
This is a leftover after having fixed various problems in the verbose
output here.

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-23 00:55:39 -08:00
Dan Farina
0bae682a0d Fix off-by-one in passing buffer size to xsnprintf
This would cause a consistent panic (exit() call) without reason.
This should not enable any overruns (but check my work).

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-23 00:55:32 -08:00
Dan Farina
f969dca821 Hack to get passwords in recovery.conf
Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-22 20:10:32 -08:00
Dan Farina
29c39c21f6 Avoid a use-after-free in verbose logging (again)
Previously, this print would use memory freed by PQClear previously.

Also allocate/free memory to prevent this tiny memory leak.

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-22 20:10:15 -08:00
Dan Farina
620974ba04 Fix unsafe string handling
It looks like the old code would overflow in some cases.

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-22 19:00:21 -08:00
Greg Smith
9843205a4f Reformat all source code using astyle
astyle --style=ansi --indent=tab
2010-12-22 19:07:01 -05:00
Greg Smith
1ddb78ddd8 Merge branch 'license' of https://github.com/gbartolini/repmgr into gbartolini-license 2010-12-22 19:02:50 -05:00
Greg Smith
b0e1428caa Add contribution information 2010-12-22 19:01:58 -05:00
Greg Smith
47349b01e0 Merge branch 'gabriele-2010-12' of https://github.com/gbartolini/repmgr into gbartolini-gabriele-2010-12 2010-12-22 18:43:14 -05:00
Greg Smith
e5d886d651 Add CREDITS file 2010-12-22 18:42:24 -05:00
Greg Smith
91a304e3ee Add HISTORY file. Ignore build artifacts in git. 2010-12-22 18:34:29 -05:00
Dan Farina
faddaed316 Avoid a use-after-free in verbose logging
Previously, this print would use memory freed by PQClear previously.

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-22 15:03:05 -08:00
Dan Farina
7b0a142075 Merge remote branch 'gbartolini/gabriele-2010-12' into heroku
Conflicts:
	config.c
	dbutils.c
	dbutils.h
	repmgrd.c

Signed-off-by: Dan Farina <drfarina@acm.org>
2010-12-21 16:08:07 -08:00
Dan Farina
ec73a07e2f Make various buffers larger
MAXLEN definitely needed to be bigger to properly format fairly common
connection strings.  In addition, the reliance on xsnprintf helps
detect cases where even this buffer is not long enough.

the buffer in parse_config has been made bigger in a bit of sloppy
programming, but what really needs to happen is reporting when a line
cannot be properly parsed/is too big for the buffer.  That is just a
kludge.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
fc13d50e37 Canonicalize whitespace
Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
309bb92d95 Prevent a double-free
This can occur because prior to this, there is a code path that looks
like this:

    primaryConn = myLocalConn

CloseConnections will subsequently try to free both with PQFinish.
I'm not sure if this is the right fix -- it's more of hack -- without
more information about design intention.

One reasonable alternative would be to have CloseConnections perform
this check itself. As-is I am pretty sure that this fix leaves a
signal-race (when CloseConnections is called, without the check, in
the interrupt handler) unfixed.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
6cea339697 Fix a use-after-free
A result is being cleared while there are still pointers that
refer to datums in it.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
3f2094a242 Initialize connection pointers to NULL
This makes some bugs easier to find.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
84b69b3bd4 Whitespace to adjust for longer snprintf identifier
This is done in a separate patch to try and reduce the sound and fury
of the patch that actually did the conversion from sprintf to
snprintf-alikes.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
916c0492fb sprintf to snprintf conversion
Move out string operations to another file, and introduce a frontend
to snprintf for various situations.  This change is important for
catching and eliminating sprintf overflows, which are as of now many
times silently corrupting memory.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
846c0b92e8 Install install/uninstall SQL also.
Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
778303bb6e Split up install/uninstall actions more like a standard contrib
Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Dan Farina
af2edf10a0 Attack of whitespace pedantry
pgsql conventions (tabs, four-spaces-wide, etc) applied all around.

Also tried to fix some very tiny capitalization errors, auto-fill
problems, and some inter-block vertical whitespacing issues.

Long strings in repmgr.c were left intact, though. They are rather
numerous and are less of a problem than tiny bits of function calls
and comments wrapping over a line; the latter kind of problem has been
mostly fixed.

Signed-off-by: Dan Farina <drfarina@acm.org>
Signed-off-by: Peter van Hardenberg <pvh@heroku.com>
2010-12-21 15:19:28 -08:00
Gabriele Bartolini
d88783a4d9 Changed pg_version() prototype in order to remove the small memory leak 2010-12-16 22:48:19 +01:00
Gabriele Bartolini
f2bec9a08f Some cosmetic changes 2010-12-16 22:31:26 +01:00
Gabriele Bartolini
05e88a2cc8 removed strncmp improper usage, initialise values asap 2010-12-16 22:21:27 +01:00
Gabriele Bartolini
763a1e8b3d Reviewed the code, fixed a small bug with option name detection 2010-12-16 22:14:18 +01:00
Gabriele Bartolini
c687d0d670 Reviewed the code, fixed a small bug with option name detection 2010-12-16 22:13:30 +01:00
Gabriele Bartolini
9b7a078e0e Added license information in every source file 2010-12-16 21:50:15 +01:00
Greg Smith
56c65acd99 Add force option to help and docs on help 2010-12-06 00:33:02 -05:00
26 changed files with 3446 additions and 1559 deletions

6
.gitignore vendored
View File

@@ -0,0 +1,6 @@
*~
*.o
repmgr
repmgrd
README.htm*
README.pdf

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2010, 2ndQuadrant Limited
Copyright (c) 2010-2011, 2ndQuadrant Limited
All rights reserved.
This program is free software: you can redistribute it and/or modify

14
CREDITS Normal file
View File

@@ -0,0 +1,14 @@
Code and documentation contributors to repmgr include:
Jaime Casanova <jaime@2ndQuadrant.com>
Simon Riggs <simon@2ndQuadrant.com>
Greg Smith <greg@2ndQuadrant.com>
Robert J. Noles <rj@2ndQuadrant.com>
Gabriele Bartolini <gabriele@2ndQuadrant.com>
Bas van Oostveen <v.oostveen@gmail.com>
Hannu Krosing <hannu@2ndQuadrant.com>
Cédric Villemain <cedric@2ndquadrant.com>
Charles Duffy <charles@dyfis.net>
Daniel Farina <daniel@heroku.com>
Marco Nenciarini <marco.nenciarini@2ndquadrant.it>
Carlo Ascani <carlo.ascani@2ndquadrant.it>

48
HISTORY Normal file
View File

@@ -0,0 +1,48 @@
1.2.0 2012-07-27
Test ssh connection before trying to rsync (Cédric)
Add CLUSTER SHOW command (Carlo)
Add CLUSTER CLEANUP command (Jaime)
Add function write_primary_conninfo (Marco)
Teach repmgr how to get tablespace's location in different pg version (Jaime)
Improve version message (Carlo)
1.1.1 2012-04-18
Add --ignore-rsync-warning (Cédric)
Add strnlen for compatibility with OS X (Greg)
Improve performance of repl_status view (Jaime)
Remove last argument from log_err (Jaime, Reported by Jeroen Dekkers)
Complete documentation about possible error conditions (Jaime)
Document how to clean history (Jaime)
1.1.0 2011-03-09
Make options -U, -R and -p not mandatory (Jaime)
1.1.0b1 2011-02-24
Fix missing "--force" option in help (Greg Smith)
Correct warning message for wal_keep_segments (Bas van Oostveen)
Add Debian build/usage docs (Bas, Hannu Krosing, Cedric Villemain)
Add Debian .deb packaging (Hannu)
Move configuration data into a structure (Bas, Gabriele Bartolini)
Make rsync options configurable (Bas)
Add syslog as alternate logging destination (Gabriele)
Change from using malloc to static memory allocations (Gabriele)
Add debugging messages after every query (Gabriele)
Parameterize schema name used for repmgr (Gabriele)
Avoid buffer overruns by using snprintf etc. (Gabriele)
Fix use of database query after close (Gabriele)
Add information about progress during "standby clone" (Gabriele)
Fix double free errors in repmgrd (Charles Duffy, Greg)
Make repmgr exit with an error code when encountering an error (Charles)
Standardize on error return codes, use in repmgrd too (Greg)
Add [un]install actions/SQL like most contrib modules (Daniel Farina)
Wrap all string construction and produce error on overflow (Daniel)
Correct freeing of memory from first_wal_segment (Daniel)
Allow creating recovery.conf file with a password (Daniel)
Inform when STANDBY CLONE sees an unused config file (Daniel)
Use 64-bit computation for WAL apply_lag (Greg)
Add info messages for database and general work done (Greg)
Map old verbose flag into a useful setting for the new logger (Greg)
Document repmgrd startup restrictions and log info about them (Greg)
1.0.0 2010-12-05
First public release

View File

@@ -1,9 +1,11 @@
#
# Makefile
# Copyright (c) 2ndQuadrant, 2010
# Copyright (c) 2ndQuadrant, 2010-2011
repmgrd_OBJS = dbutils.o config.o repmgrd.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o
repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o
DATA = repmgr.sql uninstall_repmgr.sql
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport)
@@ -26,11 +28,27 @@ include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
# XXX: Try to use PROGRAM construct (see pgxs.mk) someday. Right now
# is overriding pgxs install.
install:
$(INSTALL_PROGRAM) repmgrd$(X) '$(DESTDIR)$(bindir)'
$(INSTALL_PROGRAM) repmgr$(X) '$(DESTDIR)$(bindir)'
ifneq (,$(DATA)$(DATA_built))
@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
endif
clean:
rm -f *.o
rm -f repmgrd
rm -f repmgr
deb: repmgrd repmgr
mkdir -p ./debian/usr/bin
cp repmgrd repmgr ./debian/usr/bin/
dpkg-deb --build debian
mv debian.deb ../postgresql-repmgr-9.0_1.0.0.deb

File diff suppressed because it is too large Load Diff

14
TODO Normal file
View File

@@ -0,0 +1,14 @@
Known issues in repmgr
======================
* The check for whether ``wal_keep_segments`` is considered large enough
does a string comparison rather than an integer one. It can give both
false positive (setting is large enough but flagged as too small) and
false negative (setting is too small but not noted as such) errors.
* When running repmgr against a remote machine, operations that start
the database server using the ``pg_ctl`` command may accidentally
terminate after their associated ssh session ends.
* After running repmgrd as a regular foreground application, hitting
control-C causes the program to crash.

View File

@@ -1,8 +1,20 @@
/*
* check_dir.c
* Copyright (c) 2ndQuadrant, 2010
* check_dir.c - Directories management functions
* Copyright (C) 2ndQuadrant, 2010-2011
*
* 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/>.
*
* Directories management functions
*/
#include <sys/stat.h>
@@ -12,9 +24,12 @@
#include <stdio.h>
#include <string.h>
/* NB: postgres_fe must be included BEFORE check_dir */
#include "postgres_fe.h"
#include "check_dir.h"
#include "strutil.h"
#include "log.h"
static int mkdir_p(char *path, mode_t omode);
@@ -31,63 +46,63 @@ static int mkdir_p(char *path, mode_t omode);
int
check_dir(char *dir)
{
DIR *chkdir;
struct dirent *file;
int result = 1;
DIR *chkdir;
struct dirent *file;
int result = 1;
errno = 0;
errno = 0;
chkdir = opendir(dir);
chkdir = opendir(dir);
if (!chkdir)
return (errno == ENOENT) ? 0 : -1;
if (!chkdir)
return (errno == ENOENT) ? 0 : -1;
while ((file = readdir(chkdir)) != NULL)
{
if (strcmp(".", file->d_name) == 0 ||
strcmp("..", file->d_name) == 0)
{
/* skip this and parent directory */
continue;
}
else
{
result = 2; /* not empty */
break;
}
}
while ((file = readdir(chkdir)) != NULL)
{
if (strcmp(".", file->d_name) == 0 ||
strcmp("..", file->d_name) == 0)
{
/* skip this and parent directory */
continue;
}
else
{
result = 2; /* not empty */
break;
}
}
#ifdef WIN32
/*
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
* released version
*/
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
/*
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
* released version
*/
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
#endif
closedir(chkdir);
closedir(chkdir);
if (errno != 0)
return -1; /* some kind of I/O error? */
if (errno != 0)
return -1; /* some kind of I/O error? */
return result;
}
/*
* Create directory
* Create directory
*/
bool
create_directory(char *dir)
{
if (mkdir_p(dir, 0700) == 0)
return true;
if (mkdir_p(dir, 0700) == 0)
return true;
fprintf(stderr, _("Could not create directory \"%s\": %s\n"),
dir, strerror(errno));
log_err(_("Could not create directory \"%s\": %s\n"),
dir, strerror(errno));
return false;
return false;
}
bool
@@ -99,7 +114,7 @@ set_directory_permissions(char *dir)
/* function from initdb.c */
/* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */
/* source adapted from FreeBSD /src/bin/mkdir/mkdir.c */
/*
* this tries to build all the elements of a path to a directory a la mkdir -p
@@ -114,10 +129,10 @@ mkdir_p(char *path, mode_t omode)
{
struct stat sb;
mode_t numask,
oumask;
oumask;
int first,
last,
retval;
last,
retval;
char *p;
p = path;
@@ -136,8 +151,8 @@ mkdir_p(char *path, mode_t omode)
return 1;
}
else if (p[1] == ':' &&
((p[0] >= 'a' && p[0] <= 'z') ||
(p[0] >= 'A' && p[0] <= 'Z')))
((p[0] >= 'a' && p[0] <= 'z') ||
(p[0] >= 'A' && p[0] <= 'Z')))
{
/* local drive */
p += 2;
@@ -207,10 +222,11 @@ mkdir_p(char *path, mode_t omode)
bool
is_pg_dir(char *dir)
{
char path[8192];
struct stat sb;
const size_t buf_sz = 8192;
char path[buf_sz];
struct stat sb;
sprintf(path, "%s/PG_VERSION", dir);
xsnprintf(path, buf_sz, "%s/PG_VERSION", dir);
return (stat(path, &sb) == 0) ? true : false;
return (stat(path, &sb) == 0) ? true : false;
}

View File

@@ -1,10 +1,28 @@
/*
* check_dir.h
* Copyright (c) 2ndQuadrant, 2010
* Copyright (c) 2ndQuadrant, 2010-2011
*
* 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/>.
*
*/
#ifndef _REPMGR_CHECK_DIR_H_
#define _REPMGR_CHECK_DIR_H_
int check_dir(char *dir);
bool create_directory(char *dir);
bool set_directory_permissions(char *dir);
bool is_pg_dir(char *dir);
#endif

136
config.c
View File

@@ -1,80 +1,127 @@
/*
* config.c
* Copyright (c) 2ndQuadrant, 2010
* config.c - Functions to parse the config file
* Copyright (C) 2ndQuadrant, 2010-2011
*
* 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/>.
*
* Functions to parse the config file
*/
#include "config.h"
#include "repmgr.h"
#include "strutil.h"
void
parse_config(const char *config_file, char *cluster_name, int *node, char *conninfo)
parse_config(const char* config_file, t_configuration_options* options)
{
char *s, buff[256];
char *s, buff[MAXLINELENGTH];
char name[MAXLEN];
char value[MAXLEN];
FILE *fp = fopen (config_file, "r");
/* Initialize */
memset(options->cluster_name, 0, sizeof(options->cluster_name));
options->node = -1;
memset(options->conninfo, 0, sizeof(options->conninfo));
memset(options->rsync_options, 0, sizeof(options->rsync_options));
/*
* Since some commands don't require a config file at all, not
* having one isn't necessarily a problem.
*/
if (fp == NULL)
return;
{
fprintf(stderr, _("Did not find the configuration file '%s', continuing\n"), config_file);
return;
}
/* Read next line */
while ((s = fgets (buff, sizeof buff, fp)) != NULL)
{
char name[MAXLEN];
char value[MAXLEN];
/* Skip blank lines and comments */
if (buff[0] == '\n' || buff[0] == '#')
continue;
/* Skip blank lines and comments */
if (buff[0] == '\n' || buff[0] == '#')
continue;
/* Parse name/value pair from line */
/* Parse name/value pair from line */
parse_line(buff, name, value);
/* Copy into correct entry in parameters struct */
if (strcmp(name, "cluster") == 0)
strncpy (cluster_name, value, MAXLEN);
else if (strcmp(name, "node") == 0)
*node = atoi(value);
else if (strcmp(name, "conninfo") == 0)
strncpy (conninfo, value, MAXLEN);
else
printf ("WARNING: %s/%s: Unknown name/value pair!\n", name, value);
}
/* Copy into correct entry in parameters struct */
if (strcmp(name, "cluster") == 0)
strncpy (options->cluster_name, value, MAXLEN);
else if (strcmp(name, "node") == 0)
options->node = atoi(value);
else if (strcmp(name, "conninfo") == 0)
strncpy (options->conninfo, value, MAXLEN);
else if (strcmp(name, "rsync_options") == 0)
strncpy (options->rsync_options, value, QUERY_STR_LEN);
else if (strcmp(name, "loglevel") == 0)
strncpy (options->loglevel, value, MAXLEN);
else if (strcmp(name, "logfacility") == 0)
strncpy (options->logfacility, value, MAXLEN);
else
printf ("WARNING: %s/%s: Unknown name/value pair!\n", name, value);
}
/* Close file */
fclose (fp);
/* Close file */
fclose (fp);
/* Check config settings */
if (strnlen(options->cluster_name, MAXLEN)==0)
{
fprintf(stderr, "Cluster name is missing. "
"Check the configuration file.\n");
exit(ERR_BAD_CONFIG);
}
if (options->node == -1)
{
fprintf(stderr, "Node information is missing. "
"Check the configuration file.\n");
exit(ERR_BAD_CONFIG);
}
}
char *
trim (char *s)
{
/* Initialize start, end pointers */
char *s1 = s, *s2 = &s[strlen (s) - 1];
/* Initialize start, end pointers */
char *s1 = s, *s2 = &s[strlen (s) - 1];
/* Trim and delimit right side */
while ( (isspace (*s2)) && (s2 >= s1) )
s2--;
*(s2+1) = '\0';
/* Trim and delimit right side */
while ( (isspace (*s2)) && (s2 >= s1) )
--s2;
*(s2+1) = '\0';
/* Trim left side */
while ( (isspace (*s1)) && (s1 < s2) )
s1++;
/* Trim left side */
while ( (isspace (*s1)) && (s1 < s2) )
++s1;
/* Copy finished string */
strcpy (s, s1);
return s;
/* Copy finished string */
strcpy (s, s1);
return s;
}
void
parse_line(char *buff, char *name, char *value)
{
int i;
int j;
int i = 0;
int j = 0;
/*
* first we find the name of the parameter
*/
j = 0;
for (i = 0; i < MAXLEN; i++)
for ( ; i < MAXLEN; ++i)
{
if (buff[i] != '=')
name[j++] = buff[i];
@@ -83,12 +130,11 @@ parse_line(char *buff, char *name, char *value)
}
name[j] = '\0';
i++;
/*
* Now the value
*/
*/
j = 0;
for ( ; i < MAXLEN; i++)
for ( ++i ; i < MAXLEN; ++i)
if (buff[i] == '\'')
continue;
else if (buff[i] != '\n')
@@ -96,5 +142,5 @@ parse_line(char *buff, char *name, char *value)
else
break;
value[j] = '\0';
trim(value);
trim(value);
}

View File

@@ -1,9 +1,40 @@
/*
* config.h
* Copyright (c) 2ndQuadrant, 2010
* Copyright (c) 2ndQuadrant, 2010-2011
*
* 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/>.
*
*/
void parse_config(const char *config_file, char *cluster_name, int *node, char *service);
#ifndef _REPMGR_CONFIG_H_
#define _REPMGR_CONFIG_H_
#include "repmgr.h"
#include "strutil.h"
typedef struct
{
char cluster_name[MAXLEN];
int node;
char conninfo[MAXLEN];
char loglevel[MAXLEN];
char logfacility[MAXLEN];
char rsync_options[QUERY_STR_LEN];
} t_configuration_options;
void parse_config(const char* config_file, t_configuration_options* options);
void parse_line(char *buff, char *name, char *value);
char *trim(char *s);
#endif

297
dbutils.c
View File

@@ -1,122 +1,165 @@
/*
* dbutils.c
* Copyright (c) 2ndQuadrant, 2010
* dbutils.c - Database connection/management functions
* Copyright (C) 2ndQuadrant, 2010-2011
*
* Database connection/management functions
* 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 "repmgr.h"
#include "strutil.h"
#include "log.h"
PGconn *
establishDBConnection(const char *conninfo, const bool exit_on_error)
{
PGconn *conn;
/* Make a connection to the database */
conn = PQconnectdb(conninfo);
/* Check to see that the backend connection was successfully made */
if ((PQstatus(conn) != CONNECTION_OK))
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
/* Make a connection to the database */
PGconn *conn = PQconnectdb(conninfo);
/* Check to see that the backend connection was successfully made */
if ((PQstatus(conn) != CONNECTION_OK))
{
log_err(_("Connection to database failed: %s\n"),
PQerrorMessage(conn));
if (exit_on_error)
{
PQfinish(conn);
exit(1);
PQfinish(conn);
exit(ERR_DB_CON);
}
}
}
return conn;
}
PGconn *
establishDBConnectionByParams(const char *keywords[], const char *values[],const bool exit_on_error)
{
/* Make a connection to the database */
PGconn *conn = PQconnectdbParams(keywords, values, true);
/* Check to see that the backend connection was successfully made */
if ((PQstatus(conn) != CONNECTION_OK))
{
log_err(_("Connection to database failed: %s\n"),
PQerrorMessage(conn));
if (exit_on_error)
{
PQfinish(conn);
exit(ERR_DB_CON);
}
}
return conn;
}
bool
is_standby(PGconn *conn)
{
PGresult *res;
PGresult *res;
bool result;
res = PQexec(conn, "SELECT pg_is_in_recovery()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "Can't query server mode: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(1);
}
res = PQexec(conn, "SELECT pg_is_in_recovery()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("Can't query server mode: %s"),
PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(ERR_DB_QUERY);
}
if (strcmp(PQgetvalue(res, 0, 0), "f") == 0)
result = false;
else
result = true;
result = true;
PQclear(res);
return result;
}
/*
/*
* If postgreSQL version is 9 or superior returns the major version
* if 8 or inferior returns an empty string
*/
char *
pg_version(PGconn *conn)
pg_version(PGconn *conn, char* major_version)
{
PGresult *res;
char *major_version;
int major_version1;
char *major_version2;
int major_version1;
char *major_version2;
res = PQexec(conn, "WITH pg_version(ver) AS (SELECT split_part(version(), ' ', 2)) "
"SELECT split_part(ver, '.', 1), split_part(ver, '.', 2) FROM pg_version");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
PQclear(res);
res = PQexec(conn,
"WITH pg_version(ver) AS "
"(SELECT split_part(version(), ' ', 2)) "
"SELECT split_part(ver, '.', 1), split_part(ver, '.', 2) "
"FROM pg_version");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("Version check PQexec failed: %s"),
PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(1);
}
major_version1 = atoi(PQgetvalue(res, 0, 0));
major_version2 = PQgetvalue(res, 0, 1);
PQclear(res);
exit(ERR_DB_QUERY);
}
major_version1 = atoi(PQgetvalue(res, 0, 0));
major_version2 = PQgetvalue(res, 0, 1);
major_version = malloc(10);
if (major_version1 >= 9)
{
/* form a major version string */
sprintf(major_version, "%d.%s", major_version1, major_version2);
xsnprintf(major_version, MAXVERSIONSTR, "%d.%s", major_version1,
major_version2);
}
else
strcpy(major_version, "");
PQclear(res);
return major_version;
}
bool
guc_setted(PGconn *conn, const char *parameter, const char *op, const char *value)
guc_setted(PGconn *conn, const char *parameter, const char *op,
const char *value)
{
PGresult *res;
char sqlquery[8192];
char sqlquery[QUERY_STR_LEN];
sprintf(sqlquery, "SELECT true FROM pg_settings "
" WHERE name = '%s' AND setting %s '%s'",
parameter, op, value);
sqlquery_snprintf(sqlquery, "SELECT true FROM pg_settings "
" WHERE name = '%s' AND setting %s '%s'",
parameter, op, value);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
PQclear(res);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("GUC setting check PQexec failed: %s"),
PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(1);
}
exit(ERR_DB_QUERY);
}
if (PQntuples(res) == 0)
{
PQclear(res);
return false;
return false;
}
PQclear(res);
@@ -128,75 +171,117 @@ const char *
get_cluster_size(PGconn *conn)
{
PGresult *res;
const char *size;
char sqlquery[8192];
const char *size;
char sqlquery[QUERY_STR_LEN];
sprintf(sqlquery, "SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
" FROM pg_database ");
sqlquery_snprintf(
sqlquery,
"SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
" FROM pg_database ");
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
PQclear(res);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("Get cluster size PQexec failed: %s"),
PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(1);
}
size = PQgetvalue(res, 0, 0);
exit(ERR_DB_QUERY);
}
size = PQgetvalue(res, 0, 0);
PQclear(res);
return size;
}
/*
* get a connection to master by reading repl_nodes, creating a connection
* get a connection to master by reading repl_nodes, creating a connection
* to each node (one at a time) and finding if it is a master or a standby
*
* NB: If master_conninfo_out may be NULL. If it is non-null, it is assumed to
* point to allocated memory of MAXCONNINFO in length, and the master server
* connection string is placed there.
*/
PGconn *
getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id)
getMasterConnection(PGconn *standby_conn, char *cluster,
int *master_id, char *master_conninfo_out)
{
PGconn *master_conn = NULL;
PGresult *res1;
PGresult *res2;
char sqlquery[8192];
char master_conninfo[8192];
PGconn *master_conn = NULL;
PGresult *res1;
PGresult *res2;
char sqlquery[QUERY_STR_LEN];
char master_conninfo_stack[MAXCONNINFO];
char *master_conninfo = &*master_conninfo_stack;
char schema_str[MAXLEN];
char schema_quoted[MAXLEN];
int i;
/* find all nodes belonging to this cluster */
sprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes "
" WHERE cluster = '%s' and id <> %d",
cluster, cluster, id);
/*
* If the caller wanted to get a copy of the connection info string, sub
* out the local stack pointer for the pointer passed by the caller.
*/
if (master_conninfo_out != NULL)
master_conninfo = master_conninfo_out;
res1 = PQexec(standby_conn, sqlquery);
if (PQresultStatus(res1) != PGRES_TUPLES_OK)
{
fprintf(stderr, "Can't get nodes info: %s\n", PQerrorMessage(standby_conn));
PQclear(res1);
/*
* XXX: This is copied in at least two other procedures
*
* Assemble the unquoted schema name
*/
maxlen_snprintf(schema_str, "repmgr_%s", cluster);
{
char *identifier = PQescapeIdentifier(standby_conn, schema_str,
strlen(schema_str));
maxlen_snprintf(schema_quoted, "%s", identifier);
PQfreemem(identifier);
}
/* find all nodes belonging to this cluster */
log_info(_("finding node list for cluster '%s'\n"),
cluster);
sqlquery_snprintf(sqlquery, "SELECT * FROM %s.repl_nodes "
" WHERE cluster = '%s'",
schema_quoted, cluster);
res1 = PQexec(standby_conn, sqlquery);
if (PQresultStatus(res1) != PGRES_TUPLES_OK)
{
log_err(_("Can't get nodes info: %s\n"),
PQerrorMessage(standby_conn));
PQclear(res1);
PQfinish(standby_conn);
exit(1);
}
exit(ERR_DB_QUERY);
}
for (i = 0; i < PQntuples(res1); i++)
{
{
/* initialize with the values of the current node being processed */
*master_id = atoi(PQgetvalue(res1, i, 0));
strcpy(master_conninfo, PQgetvalue(res1, i, 2));
strncpy(master_conninfo, PQgetvalue(res1, i, 2), MAXCONNINFO);
log_info(_("checking role of cluster node '%s'\n"),
master_conninfo);
master_conn = establishDBConnection(master_conninfo, false);
if (PQstatus(master_conn) != CONNECTION_OK)
continue;
/*
* I can't use the is_standby() function here because on error that
* function closes the connection i pass and exit, but i still need to close
* standby_conn
/*
* Can't use the is_standby() function here because on error that
* function closes the connection passed and exits. This still
* needs to close master_conn first.
*/
res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()");
if (PQresultStatus(res2) != PGRES_TUPLES_OK)
{
fprintf(stderr, "Can't get recovery state from this node: %s\n", PQerrorMessage(master_conn));
PQclear(res2);
res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()");
if (PQresultStatus(res2) != PGRES_TUPLES_OK)
{
log_err(_("Can't get recovery state from this node: %s\n"),
PQerrorMessage(master_conn));
PQclear(res2);
PQfinish(master_conn);
continue;
}
}
/* if false, this is the master */
if (strcmp(PQgetvalue(res2, 0, 0), "f") == 0)
@@ -212,17 +297,17 @@ getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id)
PQfinish(master_conn);
*master_id = -1;
}
}
}
/* If we finish this loop without finding a master then
* we doesn't have the info or the master has failed (or we
* reached max_connections or superuser_reserved_connections,
* anything else i'm missing?),
* Probably we will need to check the error to know if we need
* to start failover procedure or just fix some situation on the
* standby.
*/
* we doesn't have the info or the master has failed (or we
* reached max_connections or superuser_reserved_connections,
* anything else I'm missing?).
*
* Probably we will need to check the error to know if we need
* to start failover procedure or just fix some situation on the
* standby.
*/
PQclear(res1);
return NULL;
}

View File

@@ -1,12 +1,37 @@
/*
* dbutils.h
* Copyright (c) 2ndQuadrant, 2010
* Copyright (c) 2ndQuadrant, 2010-2011
*
* 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/>.
*
*/
#ifndef _REPMGR_DBUTILS_H_
#define _REPMGR_DBUTILS_H_
#include "strutil.h"
PGconn *establishDBConnection(const char *conninfo, const bool exit_on_error);
bool is_standby(PGconn *conn);
char *pg_version(PGconn *conn);
bool guc_setted(PGconn *conn, const char *parameter, const char *op, const char *value);
const char *get_cluster_size(PGconn *conn);
PGconn * getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id);
PGconn *establishDBConnectionByParams(const char *keywords[],
const char *values[],
const bool exit_on_error);
bool is_standby(PGconn *conn);
char *pg_version(PGconn *conn, char* major_version);
bool guc_setted(PGconn *conn, const char *parameter, const char *op,
const char *value);
const char *get_cluster_size(PGconn *conn);
PGconn *getMasterConnection(PGconn *standby_conn, char *cluster,
int *master_id, char *master_conninfo_out);
#endif

9
debian/DEBIAN/control vendored Normal file
View File

@@ -0,0 +1,9 @@
Package: repmgr
Version: 1.0-1
Section: database
Priority: optional
Architecture: all
Depends: rsync, postgresql-9.0
Maintainer: Greg Smith <greg@2ndQuadrant.com>
Description: PostgreSQL replication setup, magament and monitoring
has two main executables

37
errcode.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* errcode.h
* Copyright (C) 2ndQuadrant, 2011
*
* 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/>.
*
*/
#ifndef _ERRCODE_H_
#define _ERRCODE_H_
/* Exit return code */
#define SUCCESS 0
#define ERR_BAD_CONFIG 1
#define ERR_BAD_RSYNC 2
#define ERR_STOP_BACKUP 3
#define ERR_NO_RESTART 4
#define ERR_NEEDS_XLOG 5
#define ERR_DB_CON 6
#define ERR_DB_QUERY 7
#define ERR_PROMOTED 8
#define ERR_BAD_PASSWORD 9
#define ERR_STR_OVERFLOW 10
#endif /* _ERRCODE_H_ */

213
log.c Normal file
View File

@@ -0,0 +1,213 @@
/*
* log.c - Logging methods
* Copyright (C) 2ndQuadrant, 2010-2011
*
* This module is a set of methods for logging (currently only syslog)
*
* 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 "repmgr.h"
#include <stdlib.h>
#ifdef HAVE_SYSLOG
#include <syslog.h>
#include <stdarg.h>
#endif
#include "log.h"
#define DEFAULT_IDENT "repmgr"
#ifdef HAVE_SYSLOG
#define DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
#endif
/* #define REPMGR_DEBUG */
static int detect_log_level(const char* level);
static int detect_log_facility(const char* facility);
int log_type = REPMGR_STDERR;
int log_level = LOG_NOTICE;
bool logger_init(const char* ident, const char* level, const char* facility)
{
int l;
int f;
#ifdef HAVE_SYSLOG
int syslog_facility = DEFAULT_SYSLOG_FACILITY;
#endif
#ifdef REPMGR_DEBUG
printf("Logger initialisation (Level: %s, Facility: %s)\n", level, facility);
#endif
if (!ident)
{
ident = DEFAULT_IDENT;
}
if (level && *level)
{
l = detect_log_level(level);
#ifdef REPMGR_DEBUG
printf("Assigned level for logger: %d\n", l);
#endif
if (l > 0)
log_level = l;
else
stderr_log_warning(_("Cannot detect log level %s (use any of DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG)\n"), level);
}
if (facility && *facility)
{
f = detect_log_facility(facility);
#ifdef REPMGR_DEBUG
printf("Assigned facility for logger: %d\n", f);
#endif
if (f == 0)
{
/* No syslog requested, just stderr */
#ifdef REPMGR_DEBUG
printf(_("Use stderr for logging\n"));
#endif
}
else if (f == -1)
{
stderr_log_warning(_("Cannot detect log facility %s (use any of LOCAL0, LOCAL1, ..., LOCAL7, USER or STDERR)\n"), facility);
}
#ifdef HAVE_SYSLOG
else
{
syslog_facility = f;
log_type = REPMGR_SYSLOG;
}
#endif
}
#ifdef HAVE_SYSLOG
if (log_type == REPMGR_SYSLOG)
{
setlogmask (LOG_UPTO (log_level));
openlog (ident, LOG_CONS | LOG_PID | LOG_NDELAY, syslog_facility);
stderr_log_notice(_("Setup syslog (level: %s, facility: %s)\n"), level, facility);
}
#endif
return true;
}
bool logger_shutdown(void)
{
#ifdef HAVE_SYSLOG
if (log_type == REPMGR_SYSLOG)
closelog();
#endif
return true;
}
/*
* Set a minimum logging level. Intended for command line verbosity
* options, which might increase requested logging over what's specified
* in the regular configuration file.
*/
void logger_min_verbose(int minimum)
{
if (log_level < minimum)
log_level = minimum;
}
int detect_log_level(const char* level)
{
if (!strcmp(level, "DEBUG"))
return LOG_DEBUG;
if (!strcmp(level, "INFO"))
return LOG_INFO;
if (!strcmp(level, "NOTICE"))
return LOG_NOTICE;
if (!strcmp(level, "WARNING"))
return LOG_WARNING;
if (!strcmp(level, "ERR"))
return LOG_ERR;
if (!strcmp(level, "ALERT"))
return LOG_ALERT;
if (!strcmp(level, "CRIT"))
return LOG_CRIT;
if (!strcmp(level, "EMERG"))
return LOG_EMERG;
return 0;
}
int detect_log_facility(const char* facility)
{
int local = 0;
if (!strncmp(facility, "LOCAL", 5) && strlen(facility) == 6)
{
local = atoi (&facility[5]);
switch (local)
{
case 0:
return LOG_LOCAL0;
break;
case 1:
return LOG_LOCAL1;
break;
case 2:
return LOG_LOCAL2;
break;
case 3:
return LOG_LOCAL3;
break;
case 4:
return LOG_LOCAL4;
break;
case 5:
return LOG_LOCAL5;
break;
case 6:
return LOG_LOCAL6;
break;
case 7:
return LOG_LOCAL7;
break;
}
}
else if (!strcmp(facility, "USER"))
{
return LOG_USER;
}
else if (!strcmp(facility, "STDERR"))
{
return 0;
}
return -1;
}

121
log.h Normal file
View File

@@ -0,0 +1,121 @@
/*
* log.h
* Copyright (c) 2ndQuadrant, 2010-2011
*
* 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/>.
*
*/
#ifndef _REPMGR_LOG_H_
#define _REPMGR_LOG_H_
#include "repmgr.h"
#define REPMGR_SYSLOG 1
#define REPMGR_STDERR 2
/* Standard error logging */
#define stderr_log_debug(...) if (log_level >= LOG_DEBUG) fprintf(stderr, __VA_ARGS__)
#define stderr_log_info(...) if (log_level >= LOG_INFO) fprintf(stderr, __VA_ARGS__)
#define stderr_log_notice(...) if (log_level >= LOG_NOTICE) fprintf(stderr, __VA_ARGS__)
#define stderr_log_warning(...) if (log_level >= LOG_WARNING) fprintf(stderr, __VA_ARGS__)
#define stderr_log_err(...) if (log_level >= LOG_ERR) fprintf(stderr, __VA_ARGS__)
#define stderr_log_crit(...) if (log_level >= LOG_CRIT) fprintf(stderr, __VA_ARGS__)
#define stderr_log_alert(...) if (log_level >= LOG_ALERT) fprintf(stderr, __VA_ARGS__)
#define stderr_log_emerg(...) if (log_level >= LOG_EMERG) fprintf(stderr, __VA_ARGS__)
#ifdef HAVE_SYSLOG
#include <syslog.h>
#define log_debug(...) \
if (log_type == REPMGR_SYSLOG) \
syslog(LOG_DEBUG, __VA_ARGS__); \
else \
stderr_log_debug(__VA_ARGS__);
#define log_info(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_INFO, __VA_ARGS__); \
else stderr_log_info(__VA_ARGS__); \
}
#define log_notice(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_NOTICE, __VA_ARGS__); \
else stderr_log_notice(__VA_ARGS__); \
}
#define log_warning(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_WARNING, __VA_ARGS__); \
else stderr_log_warning(__VA_ARGS__); \
}
#define log_err(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_ERR, __VA_ARGS__); \
else stderr_log_err(__VA_ARGS__); \
}
#define log_crit(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_CRIT, __VA_ARGS__); \
else stderr_log_crit(__VA_ARGS__); \
}
#define log_alert(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_ALERT, __VA_ARGS__); \
else stderr_log_alert(__VA_ARGS__); \
}
#define log_emerg(...) \
{ \
if (log_type == REPMGR_SYSLOG) syslog(LOG_ALERT, __VA_ARGS__); \
else stderr_log_alert(__VA_ARGS__); \
}
#else
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
#define log_debug(...) stderr_log_debug(__VA_ARGS__)
#define log_info(...) stderr_log_info(__VA_ARGS__)
#define log_notice(...) stderr_log_notice(__VA_ARGS__)
#define log_warning(...) stderr_log_warning(__VA_ARGS__)
#define log_err(...) stderr_log_err(__VA_ARGS__)
#define log_crit(...) stderr_log_crit(__VA_ARGS__)
#define log_alert(...) stderr_log_alert(__VA_ARGS__)
#define log_emerg(...) stderr_log_emerg(__VA_ARGS__)
#endif
/* Logger initialisation and shutdown */
bool logger_shutdown(void);
bool logger_init(const char* ident, const char* level, const char* facility);
void logger_min_verbose(int minimum);
extern int log_type;
extern int log_level;
#endif

2214
repmgr.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,21 @@
###################################################
# Replication Manager configuration file
###################################################
# Cluster name
cluster=test
# Node ID
node=2
# Connection information
conninfo='host=192.168.204.104'
rsync_options=--archive --checksum --compress --progress --rsh=ssh
# Log level: possible values are DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG
# Default: NOTICE
loglevel=NOTICE
# Logging facility: possible values are STDERR or - for Syslog integration - one of LOCAL0, LOCAL1, ..., LOCAL7, USER
# Default: STDERR
logfacility=STDERR

View File

@@ -1,6 +1,19 @@
/*
* dbutils.h
* Copyright (c) 2ndQuadrant, 2010
* repmgr.h
* Copyright (c) 2ndQuadrant, 2010-2011
*
* 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/>.
*
*/
@@ -11,14 +24,43 @@
#include "getopt_long.h"
#include "libpq-fe.h"
#include "strutil.h"
#include "dbutils.h"
#include "config.h"
#include "errcode.h"
#define PRIMARY_MODE 0
#define STANDBY_MODE 1
#define MAXLEN 80
#define CONFIG_FILE "repmgr.conf"
#include "config.h"
#define MAXFILENAME 1024
#define ERRBUFF_SIZE 512
#define DEFAULT_CONFIG_FILE "./repmgr.conf"
#define DEFAULT_WAL_KEEP_SEGMENTS "5000"
#define DEFAULT_DEST_DIR "."
#define DEFAULT_MASTER_PORT "5432"
#define DEFAULT_DBNAME "postgres"
#define DEFAULT_REPMGR_SCHEMA_PREFIX "repmgr_"
/* Run time options type */
typedef struct
{
char dbname[MAXLEN];
char host[MAXLEN];
char username[MAXLEN];
char dest_dir[MAXFILENAME];
char config_file[MAXFILENAME];
char remote_user[MAXLEN];
char wal_keep_segments[MAXLEN];
bool verbose;
bool force;
bool ignore_rsync_warn;
char masterport[MAXLEN];
/* parameter used by CLUSTER CLEANUP */
int keep_history;
} t_runtime_options;
#endif

View File

@@ -1,3 +1,10 @@
/*
* repmgr.sql
*
* Copyright (C) 2ndQuadrant, 2011
*
*/
CREATE USER repmgr;
CREATE SCHEMA repmgr;
@@ -5,27 +12,25 @@ CREATE SCHEMA repmgr;
* The table repl_nodes keeps information about all machines in
* a cluster
*/
drop table if exists repl_nodes cascade;
CREATE TABLE repl_nodes (
id integer primary key,
cluster text not null, -- Name to identify the cluster
conninfo text not null
id integer primary key,
cluster text not null, -- Name to identify the cluster
conninfo text not null
);
ALTER TABLE repl_nodes OWNER TO repmgr;
/*
* Keeps monitor info about every node and their relative "position"
* Keeps monitor info about every node and their relative "position"
* to primary
*/
drop table if exists repl_monitor cascade;
CREATE TABLE repl_monitor (
primary_node INTEGER NOT NULL,
standby_node INTEGER NOT NULL,
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
last_wal_primary_location TEXT NOT NULL,
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
last_wal_primary_location TEXT NOT NULL,
last_wal_standby_location TEXT NOT NULL,
replication_lag BIGINT NOT NULL,
apply_lag BIGINT NOT NULL
replication_lag BIGINT NOT NULL,
apply_lag BIGINT NOT NULL
);
ALTER TABLE repl_monitor OWNER TO repmgr;
@@ -33,21 +38,20 @@ ALTER TABLE repl_monitor OWNER TO repmgr;
/*
* This view shows the latest monitor info about every node.
* Interesting thing to see:
* replication_lag: in bytes (this is how far the latest xlog record
* replication_lag: in bytes (this is how far the latest xlog record
* we have received is from master)
* apply_lag: in bytes (this is how far the latest xlog record
* we have applied is from the latest record we
* we have applied is from the latest record we
* have received)
* time_lag: how many seconds are we from being up-to-date with master
*/
drop view if exists repl_status;
CREATE VIEW repl_status AS
WITH monitor_info AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY primary_node, standby_node
ORDER BY last_monitor_time desc)
FROM repl_monitor)
SELECT primary_node, standby_node, last_monitor_time, last_wal_primary_location,
last_wal_standby_location, pg_size_pretty(replication_lag) replication_lag,
pg_size_pretty(apply_lag) apply_lag,
SELECT primary_node, standby_node, last_monitor_time, last_wal_primary_location,
last_wal_standby_location, pg_size_pretty(replication_lag) replication_lag,
pg_size_pretty(apply_lag) apply_lag,
age(now(), last_monitor_time) AS time_lag
FROM monitor_info a
WHERE row_number = 1;

464
repmgrd.c
View File

@@ -1,10 +1,23 @@
/*
* repmgrd.c
* Copyright (c) 2ndQuadrant, 2010
* repmgrd.c - Replication manager daemon
* Copyright (C) 2ndQuadrant, 2010-2011
*
* Replication manager daemon
* This module connects to the nodes of a replication cluster and monitors
* how far are they from master
* how far are they from master
*
* 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 <signal.h>
@@ -14,31 +27,41 @@
#include <unistd.h>
#include "repmgr.h"
#include "config.h"
#include "log.h"
#include "strutil.h"
#include "version.h"
#include "libpq/pqsignal.h"
char myClusterName[MAXLEN];
/* Local info */
t_configuration_options local_options;
int myLocalMode = STANDBY_MODE;
int myLocalId = -1;
PGconn *myLocalConn;
PGconn *myLocalConn = NULL;
/* Primary info */
int primaryId;
char primaryConninfo[MAXLEN];
PGconn *primaryConn;
t_configuration_options primary_options;
char sqlquery[8192];
PGconn *primaryConn = NULL;
char sqlquery[QUERY_STR_LEN];
const char *progname;
char *config_file = NULL;
char *config_file = DEFAULT_CONFIG_FILE;
bool verbose = false;
char repmgr_schema[MAXLEN];
/*
* should initialize with {0} to be ANSI complaint ? but this raises
* error with gcc -Wall
*/
t_configuration_options config = {};
static void help(const char *progname);
static void checkClusterConfiguration(void);
static void help(const char* progname);
static void usage(void);
static void checkClusterConfiguration(PGconn *conn,PGconn *primary);
static void checkNodeConfiguration(char *conninfo);
static void CancelQuery(void);
@@ -50,28 +73,29 @@ static void handle_sigint(SIGNAL_ARGS);
static void setup_cancel_handler(void);
#define CloseConnections() \
if (PQisBusy(primaryConn) == 1) \
CancelQuery(); \
if (myLocalConn != NULL) \
PQfinish(myLocalConn); \
if (primaryConn != NULL) \
PQfinish(primaryConn);
if (PQisBusy(primaryConn) == 1) \
CancelQuery(); \
if (myLocalConn != NULL) \
PQfinish(myLocalConn); \
if (primaryConn != NULL && primaryConn != myLocalConn) \
PQfinish(primaryConn);
/*
* Every 3 seconds, insert monitor info
*/
#define MonitorCheck() \
for (;;) \
{ \
MonitorExecute(); \
sleep(3); \
}
#define MonitorCheck() \
for (;;) \
{ \
MonitorExecute(); \
sleep(3); \
}
int
main(int argc, char **argv)
{
static struct option long_options[] = {
static struct option long_options[] =
{
{"config", required_argument, NULL, 'f'},
{"verbose", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}
@@ -80,8 +104,7 @@ main(int argc, char **argv)
int optindex;
int c;
char conninfo[MAXLEN];
const char *standby_version = NULL;
char standby_version[MAXVERSIONSTR];
progname = get_progname(argv[0]);
@@ -90,92 +113,112 @@ main(int argc, char **argv)
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
help(progname);
exit(0);
exit(SUCCESS);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
printf("%s (PostgreSQL) " PG_VERSION "\n", progname);
exit(0);
printf("%s %s (PostgreSQL %s)\n", progname, REPMGR_VERSION, PG_VERSION);
exit(SUCCESS);
}
}
while ((c = getopt_long(argc, argv, "f:v", long_options, &optindex)) != -1)
{
switch (c)
{
case 'f':
config_file = optarg;
break;
case 'v':
verbose = true;
break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
case 'f':
config_file = optarg;
break;
case 'v':
verbose = true;
break;
default:
usage();
exit(ERR_BAD_CONFIG);
}
}
setup_cancel_handler();
if (config_file == NULL)
{
config_file = malloc(5 + sizeof(CONFIG_FILE));
sprintf(config_file, "./%s", CONFIG_FILE);
}
/*
* Read the configuration file: repmgr.conf
*/
parse_config(config_file, myClusterName, &myLocalId, conninfo);
if (myLocalId == -1)
*/
parse_config(config_file, &local_options);
if (local_options.node == -1)
{
fprintf(stderr, "Node information is missing. "
"Check the configuration file.\n");
exit(1);
log_err("Node information is missing. "
"Check the configuration file, or provide one if you have not done so.\n");
exit(ERR_BAD_CONFIG);
}
myLocalConn = establishDBConnection(conninfo, true);
logger_init(progname, local_options.loglevel, local_options.logfacility);
if (verbose)
logger_min_verbose(LOG_INFO);
snprintf(repmgr_schema, MAXLEN, "%s%s", DEFAULT_REPMGR_SCHEMA_PREFIX, local_options.cluster_name);
log_info(_("%s Connecting to database '%s'\n"), progname, local_options.conninfo);
myLocalConn = establishDBConnection(local_options.conninfo, true);
/* should be v9 or better */
standby_version = pg_version(myLocalConn);
log_info(_("%s Connected to database, checking its state\n"), progname);
pg_version(myLocalConn, standby_version);
if (strcmp(standby_version, "") == 0)
{
PQfinish(myLocalConn);
fprintf(stderr, _("%s needs standby to be PostgreSQL 9.0 or better\n"), progname);
exit(1);
log_err(_("%s needs standby to be PostgreSQL 9.0 or better\n"), progname);
exit(ERR_BAD_CONFIG);
}
/*
* Set my server mode, establish a connection to primary
/*
* Set my server mode, establish a connection to primary
* and start monitor
*/
*/
myLocalMode = is_standby(myLocalConn) ? STANDBY_MODE : PRIMARY_MODE;
if (myLocalMode == PRIMARY_MODE)
{
primaryId = myLocalId;
strcpy(primaryConninfo, conninfo);
primary_options.node = local_options.node;
strncpy(primary_options.conninfo, local_options.conninfo, MAXLEN);
primaryConn = myLocalConn;
}
else
{
/* I need the id of the primary as well as a connection to it */
primaryConn = getMasterConnection(myLocalConn, myLocalId, myClusterName, &primaryId);
log_info(_("%s Connecting to primary for cluster '%s'\n"),
progname, local_options.cluster_name);
primaryConn = getMasterConnection(myLocalConn,
local_options.cluster_name,
&primary_options.node,NULL);
if (primaryConn == NULL)
exit(1);
{
CloseConnections();
exit(ERR_BAD_CONFIG);
}
}
checkClusterConfiguration();
checkNodeConfiguration(conninfo);
checkClusterConfiguration(myLocalConn,primaryConn);
checkNodeConfiguration(local_options.conninfo);
if (myLocalMode == STANDBY_MODE)
{
MonitorCheck();
log_info(_("%s Starting continuous standby node monitoring\n"), progname);
MonitorCheck();
}
else
{
log_info(_("%s This is a primary node, program not needed here; exiting'\n"), progname);
}
/* close the connection to the database and cleanup */
CloseConnections();
/* Prevent a double-free */
if (primaryConn == myLocalConn)
myLocalConn = NULL;
return 0;
/* close the connection to the database and cleanup */
CloseConnections();
/* Shuts down logging system */
logger_shutdown();
return 0;
}
@@ -187,7 +230,7 @@ main(int argc, char **argv)
static void
MonitorExecute(void)
{
PGresult *res;
PGresult *res;
char monitor_standby_timestamp[MAXLEN];
char last_wal_primary_location[MAXLEN];
char last_wal_standby_received[MAXLEN];
@@ -199,42 +242,45 @@ MonitorExecute(void)
int connection_retries;
/*
* Check if the master is still available, if after 5 minutes of retries
* we cannot reconnect, try to get a new master.
/*
* Check if the master is still available, if after 5 minutes of retries
* we cannot reconnect, try to get a new master.
*/
for (connection_retries = 0; connection_retries < 15; connection_retries++)
{
if (PQstatus(primaryConn) != CONNECTION_OK)
{
fprintf(stderr, "\n%s: Connection to master has been lost, trying to recover...\n", progname);
log_warning(_("Connection to master has been lost, trying to recover...\n"));
/* wait 20 seconds between retries */
sleep(20);
PQreset(primaryConn);
}
}
else
{
fprintf(stderr, "\n%s: Connection to master has been restored, continue monitoring.\n", progname);
if (connection_retries > 0)
{
log_notice(_("Connection to master has been restored, continue monitoring.\n"));
}
break;
}
}
if (PQstatus(primaryConn) != CONNECTION_OK)
{
fprintf(stderr, "\n%s: We couldn't reconnect to master, checking if ", progname);
fprintf(stderr, "%s: another node has been promoted.\n", progname);
log_err(_("We couldn't reconnect to master. Now checking if another node has been promoted.\n"));
for (connection_retries = 0; connection_retries < 6; connection_retries++)
{
primaryConn = getMasterConnection(myLocalConn, myLocalId, myClusterName, &primaryId);
primaryConn = getMasterConnection(myLocalConn,
local_options.cluster_name, &primary_options.node,NULL);
if (PQstatus(primaryConn) == CONNECTION_OK)
{
/* Connected, we can continue the process so break the loop */
fprintf(stderr, "\n%s: Connected to node %d, continue monitoring.\n", progname, primaryId);
log_err(_("Connected to node %d, continue monitoring.\n"), primary_options.node);
break;
}
else
{
fprintf(stderr, "\n%s: We haven't found a new master, waiting before retry...\n", progname);
log_err(_("We haven't found a new master, waiting before retry...\n"));
/* wait 5 minutes before retries, after 6 failures (30 minutes) we stop trying */
sleep(300);
}
@@ -242,59 +288,59 @@ MonitorExecute(void)
}
if (PQstatus(primaryConn) != CONNECTION_OK)
{
fprintf(stderr, "\n%s: We couldn't reconnect for long enough, exiting...\n", progname);
exit(1);
log_err(_("We couldn't reconnect for long enough, exiting...\n"));
exit(ERR_DB_CON);
}
/* Check if we still are a standby, we could have been promoted */
if (!is_standby(myLocalConn))
{
fprintf(stderr, "\n%s: seems like we have been promoted, so exit from monitoring...\n",
progname);
{
log_err(_("It seems like we have been promoted, so exit from monitoring...\n"));
CloseConnections();
exit(1);
exit(ERR_PROMOTED);
}
/*
/*
* first check if there is a command being executed,
* and if that is the case, cancel the query so i can
* insert the current record
*/
* insert the current record
*/
if (PQisBusy(primaryConn) == 1)
CancelQuery();
/* Get local xlog info */
sprintf(sqlquery,
"SELECT CURRENT_TIMESTAMP, pg_last_xlog_receive_location(), "
"pg_last_xlog_replay_location()");
sqlquery_snprintf(
sqlquery,
"SELECT CURRENT_TIMESTAMP, pg_last_xlog_receive_location(), "
"pg_last_xlog_replay_location()");
res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s\n", PQerrorMessage(myLocalConn));
PQclear(res);
res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err("PQexec failed: %s\n", PQerrorMessage(myLocalConn));
PQclear(res);
/* if there is any error just let it be and retry in next loop */
return;
}
return;
}
strcpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0));
strcpy(last_wal_standby_received , PQgetvalue(res, 0, 1));
strcpy(last_wal_standby_applied , PQgetvalue(res, 0, 2));
PQclear(res);
strncpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
strncpy(last_wal_standby_received , PQgetvalue(res, 0, 1), MAXLEN);
strncpy(last_wal_standby_applied , PQgetvalue(res, 0, 2), MAXLEN);
PQclear(res);
/* Get primary xlog info */
sprintf(sqlquery, "SELECT pg_current_xlog_location() ");
sqlquery_snprintf(sqlquery, "SELECT pg_current_xlog_location() ");
res = PQexec(primaryConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s\n", PQerrorMessage(primaryConn));
PQclear(res);
return;
}
res = PQexec(primaryConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err("PQexec failed: %s\n", PQerrorMessage(primaryConn));
PQclear(res);
return;
}
strcpy(last_wal_primary_location, PQgetvalue(res, 0, 0));
PQclear(res);
strncpy(last_wal_primary_location, PQgetvalue(res, 0, 0), MAXLEN);
PQclear(res);
/* Calculate the lag */
lsn_primary = walLocationToBytes(last_wal_primary_location);
@@ -304,57 +350,59 @@ MonitorExecute(void)
/*
* Build the SQL to execute on primary
*/
sprintf(sqlquery,
"INSERT INTO repmgr_%s.repl_monitor "
"VALUES(%d, %d, '%s'::timestamp with time zone, "
" '%s', '%s', "
" %lld, %lld)", myClusterName,
primaryId, myLocalId, monitor_standby_timestamp,
last_wal_primary_location,
last_wal_standby_received,
(lsn_primary - lsn_standby_received),
(lsn_standby_received - lsn_standby_applied));
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor "
"VALUES(%d, %d, '%s'::timestamp with time zone, "
" '%s', '%s', "
" %lld, %lld)", repmgr_schema,
primary_options.node, local_options.node, monitor_standby_timestamp,
last_wal_primary_location,
last_wal_standby_received,
(lsn_primary - lsn_standby_received),
(lsn_standby_received - lsn_standby_applied));
/*
* Execute the query asynchronously, but don't check for a result. We
* will check the result next time we pause for a monitor step.
*/
if (PQsendQuery(primaryConn, sqlquery) == 0)
fprintf(stderr, "Query could not be sent to primary. %s\n",
PQerrorMessage(primaryConn));
log_warning("Query could not be sent to primary. %s\n",
PQerrorMessage(primaryConn));
}
static void
checkClusterConfiguration(void)
checkClusterConfiguration(PGconn *conn, PGconn *primary)
{
PGresult *res;
PGresult *res;
sprintf(sqlquery, "SELECT oid FROM pg_class "
" WHERE oid = 'repmgr_%s.repl_nodes'::regclass",
myClusterName);
res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s\n", PQerrorMessage(myLocalConn));
PQclear(res);
PQfinish(myLocalConn);
PQfinish(primaryConn);
exit(1);
}
log_info(_("%s Checking cluster configuration with schema '%s'\n"),
progname, repmgr_schema);
sqlquery_snprintf(sqlquery, "SELECT oid FROM pg_class "
" WHERE oid = '%s.repl_nodes'::regclass",
repmgr_schema);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err("PQexec failed: %s\n", PQerrorMessage(conn));
PQclear(res);
CloseConnections();
exit(ERR_DB_QUERY);
}
/*
* If there isn't any results then we have not configured a primary node yet
* in repmgr or the connection string is pointing to the wrong database.
* If there isn't any results then we have not configured a primary node
* yet in repmgr or the connection string is pointing to the wrong
* database.
*
* XXX if we are the primary, should we try to create the tables needed?
*/
if (PQntuples(res) == 0)
{
fprintf(stderr, "The replication cluster is not configured\n");
PQclear(res);
PQfinish(myLocalConn);
PQfinish(primaryConn);
exit(1);
log_err("The replication cluster is not configured\n");
PQclear(res);
CloseConnections();
exit(ERR_BAD_CONFIG);
}
PQclear(res);
}
@@ -363,92 +411,106 @@ checkClusterConfiguration(void)
static void
checkNodeConfiguration(char *conninfo)
{
PGresult *res;
PGresult *res;
/*
* Check if we have my node information in repl_nodes
*/
sprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes "
" WHERE id = %d AND cluster = '%s' ",
myClusterName, myLocalId, myClusterName);
log_info(_("%s Checking node %d in cluster '%s'\n"),
progname, local_options.node, local_options.cluster_name);
sqlquery_snprintf(sqlquery, "SELECT * FROM %s.repl_nodes "
" WHERE id = %d AND cluster = '%s' ",
repmgr_schema, local_options.node,
local_options.cluster_name);
res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "PQexec failed: %s\n", PQerrorMessage(myLocalConn));
PQclear(res);
PQfinish(myLocalConn);
PQfinish(primaryConn);
exit(1);
}
res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err("PQexec failed: %s\n", PQerrorMessage(myLocalConn));
PQclear(res);
CloseConnections();
exit(ERR_BAD_CONFIG);
}
/*
* If there isn't any results then we have not configured this node yet
* in repmgr, if that is the case we will insert the node to the cluster
* in repmgr, if that is the case we will insert the node to the cluster
*/
if (PQntuples(res) == 0)
{
PQclear(res);
/* Adding the node */
sprintf(sqlquery, "INSERT INTO repmgr_%s.repl_nodes "
"VALUES (%d, '%s', '%s')",
myClusterName, myLocalId, myClusterName, conninfo);
PQclear(res);
if (!PQexec(primaryConn, sqlquery))
/* Adding the node */
log_info(_("%s Adding node %d to cluster '%s'\n"),
progname, local_options.node, local_options.cluster_name);
sqlquery_snprintf(sqlquery, "INSERT INTO %s.repl_nodes "
"VALUES (%d, '%s', '%s')",
repmgr_schema, local_options.node,
local_options.cluster_name,
local_options.conninfo);
if (!PQexec(primaryConn, sqlquery))
{
fprintf(stderr, "Cannot insert node details, %s\n",
PQerrorMessage(primaryConn));
PQfinish(myLocalConn);
PQfinish(primaryConn);
exit(1);
log_err("Cannot insert node details, %s\n",
PQerrorMessage(primaryConn));
CloseConnections();
exit(ERR_BAD_CONFIG);
}
}
PQclear(res);
else
{
PQclear(res);
}
}
static unsigned long long int
static unsigned long long int
walLocationToBytes(char *wal_location)
{
unsigned int xlogid;
unsigned int xrecoff;
unsigned int xlogid;
unsigned int xrecoff;
if (sscanf(wal_location, "%X/%X", &xlogid, &xrecoff) != 2)
{
fprintf(stderr, "wrong log location format: %s\n", wal_location);
return 0;
}
return ((xlogid * 16 * 1024 * 1024 * 255) + xrecoff);
if (sscanf(wal_location, "%X/%X", &xlogid, &xrecoff) != 2)
{
log_err("wrong log location format: %s\n", wal_location);
return 0;
}
return (( (long long) xlogid * 16 * 1024 * 1024 * 255) + xrecoff);
}
static void
help(const char *progname)
void usage(void)
{
printf(_("\n%s: Replicator manager daemon \n"), progname);
printf(_("Usage:\n"));
printf(_(" %s [OPTIONS]\n"), progname);
printf(_("\nOptions:\n"));
log_err(_("%s: Replicator manager daemon \n"), progname);
log_err(_("Try \"%s --help\" for more information.\n"), progname);
}
void help(const char *progname)
{
printf(_("Usage: %s [OPTIONS]\n"), progname);
printf(_("Replicator manager daemon for PostgreSQL.\n"));
printf(_("\nOptions:\n"));
printf(_(" --help show this help, then exit\n"));
printf(_(" --version output version information, then exit\n"));
printf(_(" --verbose output verbose activity information\n"));
printf(_(" -f, --config_file=PATH database to connect to\n"));
printf(_("\n%s monitors a cluster of servers.\n"), progname);
printf(_(" -f, --config_file=PATH configuration file\n"));
printf(_("\n%s monitors a cluster of servers.\n"), progname);
}
#ifndef WIN32
static void
handle_sigint(SIGNAL_ARGS)
{
CloseConnections();
CloseConnections();
}
static void
setup_cancel_handler(void)
{
pqsignal(SIGINT, handle_sigint);
pqsignal(SIGINT, handle_sigint);
}
#endif
@@ -456,13 +518,13 @@ setup_cancel_handler(void)
static void
CancelQuery(void)
{
char errbuf[256];
PGcancel *pgcancel;
char errbuf[ERRBUFF_SIZE];
PGcancel *pgcancel;
pgcancel = PQgetCancel(primaryConn);
pgcancel = PQgetCancel(primaryConn);
if (!pgcancel || PQcancel(pgcancel, errbuf, 256) == 0)
fprintf(stderr, "Can't stop current query: %s", errbuf);
if (!pgcancel || PQcancel(pgcancel, errbuf, ERRBUFF_SIZE) == 0)
log_warning("Can't stop current query: %s\n", errbuf);
PQfreeCancel(pgcancel);
PQfreeCancel(pgcancel);
}

96
strutil.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* strutil.c
*
* Copyright (C) 2ndQuadrant, 2011
*
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "log.h"
#include "strutil.h"
static int xvsnprintf(char *str, size_t size, const char *format, va_list ap);
/* Add strnlen on platforms that don't have it, like OS X */
#ifndef strnlen
size_t
strnlen(const char *s, size_t n)
{
const char *end = (const char *) memchr(s, '\0', n);
return(end ? end - s : n);
}
#endif
static int
xvsnprintf(char *str, size_t size, const char *format, va_list ap)
{
int retval;
retval = vsnprintf(str, size, format, ap);
if (retval >= size)
{
log_err(_("Buffer of size not large enough to format entire string '%s'\n"),
str);
exit(ERR_STR_OVERFLOW);
}
return retval;
}
int
xsnprintf(char *str, size_t size, const char *format, ...)
{
va_list arglist;
int retval;
va_start(arglist, format);
retval = xvsnprintf(str, size, format, arglist);
va_end(arglist);
return retval;
}
int
sqlquery_snprintf(char *str, const char *format, ...)
{
va_list arglist;
int retval;
va_start(arglist, format);
retval = xvsnprintf(str, QUERY_STR_LEN, format, arglist);
va_end(arglist);
return retval;
}
int maxlen_snprintf(char *str, const char *format, ...)
{
va_list arglist;
int retval;
va_start(arglist, format);
retval = xvsnprintf(str, MAXLEN, format, arglist);
va_end(arglist);
return retval;
}

43
strutil.h Normal file
View File

@@ -0,0 +1,43 @@
/*
* strutil.h
* Copyright (C) 2ndQuadrant, 2010-2011
*
*
* 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/>.
*
*/
#ifndef _STRUTIL_H_
#define _STRUTIL_H_
#include <stdlib.h>
#include <errcode.h>
#define QUERY_STR_LEN 8192
#define MAXLEN 1024
#define MAXLINELENGTH 4096
#define MAXVERSIONSTR 16
#define MAXCONNINFO 1024
extern int xsnprintf(char *str, size_t size, const char *format, ...);
extern int sqlquery_snprintf(char *str, const char *format, ...);
extern int maxlen_snprintf(char *str, const char *format, ...);
/* Add strnlen on platforms that don't have it, like OS X */
#ifndef strnlen
extern size_t strnlen(const char *s, size_t n);
#endif
#endif /* _STRUTIL_H_ */

13
uninstall_repmgr.sql Normal file
View File

@@ -0,0 +1,13 @@
/*
* uninstall_repmgr.sql
*
* Copyright (C) 2ndQuadrant, 2010-2011
*
*/
DROP TABLE IF EXISTS repl_nodes;
DROP TABLE IF EXISTS repl_monitor;
DROP VIEW IF EXISTS repl_status;
DROP SCHEMA repmgr;
DROP USER repmgr;

4
version.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef _VERSION_H_
#define _VERSION_H_
#define REPMGR_VERSION "1.2.0"
#endif