mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 07:06:30 +00:00
Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c792c8013 | ||
|
|
e7903a7971 | ||
|
|
f372793114 | ||
|
|
295a183d0b | ||
|
|
c6693340c1 | ||
|
|
cc0c516810 | ||
|
|
58ec43106e | ||
|
|
3e960286c7 | ||
|
|
bf0bbd747b | ||
|
|
459f14b594 | ||
|
|
f928edf897 | ||
|
|
c7b62003b2 | ||
|
|
59b7b90169 | ||
|
|
3cdd6a57fd | ||
|
|
5dcec5818f | ||
|
|
9c6288993b | ||
|
|
2675101330 | ||
|
|
02daccd6e7 | ||
|
|
18ef5b3cf3 | ||
|
|
3b2203c38c | ||
|
|
fee7da1ba7 | ||
|
|
a98aec12e1 | ||
|
|
757457ad85 | ||
|
|
45022debc6 | ||
|
|
f6618a01c7 | ||
|
|
20af4ffc2c | ||
|
|
3ef1fa126d | ||
|
|
ce06e6c8e0 | ||
|
|
0ad3d91e9c | ||
|
|
0130420947 | ||
|
|
c3af4274ae | ||
|
|
5460517a43 | ||
|
|
1d32133d49 | ||
|
|
1be62fa6b2 | ||
|
|
b62ad056f1 | ||
|
|
ec30caef0f | ||
|
|
78b925ea54 | ||
|
|
7e7c45c61d | ||
|
|
a02cd43115 | ||
|
|
a67688f198 | ||
|
|
f74b806e4f | ||
|
|
657aa3013b | ||
|
|
8d2aff558d | ||
|
|
6e1306cfb2 | ||
|
|
308efba95e | ||
|
|
556f256bb6 | ||
|
|
83eee2ed96 | ||
|
|
cb36aaddff | ||
|
|
3146d2c676 | ||
|
|
3f1c6a5852 | ||
|
|
716a0ae9d3 | ||
|
|
7a3be3a2f9 | ||
|
|
f6a6632169 | ||
|
|
2c1eafd7a9 | ||
|
|
1787cf1c21 | ||
|
|
287e2c1a67 | ||
|
|
b487772b84 | ||
|
|
52313a2d6e | ||
|
|
814863edf2 | ||
|
|
e4f431c355 | ||
|
|
c3816d8586 | ||
|
|
4ea0fc4214 | ||
|
|
03ed0e692a | ||
|
|
ce21291418 | ||
|
|
4b007bf4bc | ||
|
|
da82829659 | ||
|
|
ebbb7c3a47 | ||
|
|
630d716819 | ||
|
|
c1b84fe9fc | ||
|
|
bfd3aed33b | ||
|
|
c356f9e97f | ||
|
|
a52ebc412c | ||
|
|
fbf97807b8 | ||
|
|
3cce9efac0 | ||
|
|
ec8678379f | ||
|
|
d017edbe47 | ||
|
|
f41ba482eb | ||
|
|
4c5fad2070 | ||
|
|
0dd31b09dd | ||
|
|
943dda0ad0 | ||
|
|
d4de19bc78 | ||
|
|
1999b534fd | ||
|
|
57169f51cf | ||
|
|
fc2405f17d | ||
|
|
4a0e827464 | ||
|
|
745392e3a2 | ||
|
|
170f519d84 | ||
|
|
cf5717ad59 | ||
|
|
cb1192b912 | ||
|
|
156714f3f1 | ||
|
|
dd5ac660bf | ||
|
|
3a430397dc | ||
|
|
1f098c60ac | ||
|
|
0bae682a0d | ||
|
|
f969dca821 | ||
|
|
29c39c21f6 | ||
|
|
620974ba04 | ||
|
|
9843205a4f | ||
|
|
1ddb78ddd8 | ||
|
|
b0e1428caa | ||
|
|
47349b01e0 | ||
|
|
e5d886d651 | ||
|
|
91a304e3ee | ||
|
|
faddaed316 | ||
|
|
7b0a142075 | ||
|
|
ec73a07e2f | ||
|
|
fc13d50e37 | ||
|
|
309bb92d95 | ||
|
|
6cea339697 | ||
|
|
3f2094a242 | ||
|
|
84b69b3bd4 | ||
|
|
916c0492fb | ||
|
|
846c0b92e8 | ||
|
|
778303bb6e | ||
|
|
af2edf10a0 | ||
|
|
d88783a4d9 | ||
|
|
f2bec9a08f | ||
|
|
05e88a2cc8 | ||
|
|
763a1e8b3d | ||
|
|
c687d0d670 | ||
|
|
9b7a078e0e | ||
|
|
56c65acd99 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -0,0 +1,6 @@
|
||||
*~
|
||||
*.o
|
||||
repmgr
|
||||
repmgrd
|
||||
README.htm*
|
||||
README.pdf
|
||||
|
||||
@@ -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
|
||||
|
||||
12
CREDITS
Normal file
12
CREDITS
Normal file
@@ -0,0 +1,12 @@
|
||||
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>
|
||||
33
HISTORY
Normal file
33
HISTORY
Normal file
@@ -0,0 +1,33 @@
|
||||
1.0.0 2010-12-05
|
||||
First public release
|
||||
|
||||
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.1.0 2011-03-09
|
||||
Make options -U, -R and -p not mandatory (Jaime)
|
||||
|
||||
24
Makefile
24
Makefile
@@ -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
|
||||
|
||||
|
||||
893
README.rst
893
README.rst
File diff suppressed because it is too large
Load Diff
21
TODO
Normal file
21
TODO
Normal file
@@ -0,0 +1,21 @@
|
||||
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.
|
||||
|
||||
Planned feature improvements
|
||||
============================
|
||||
|
||||
* Before running ``pg_start_backup()``, a sanity check that there is a
|
||||
a working ssh connection to the destination would help find
|
||||
configuration errors before disturbing the database.
|
||||
114
check_dir.c
114
check_dir.c
@@ -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;
|
||||
}
|
||||
|
||||
20
check_dir.h
20
check_dir.h
@@ -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
136
config.c
@@ -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);
|
||||
}
|
||||
|
||||
35
config.h
35
config.h
@@ -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
297
dbutils.c
@@ -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, int id, 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' and id <> %d",
|
||||
schema_quoted, cluster, id);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
35
dbutils.h
35
dbutils.h
@@ -1,12 +1,35 @@
|
||||
/*
|
||||
* 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_
|
||||
|
||||
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, int id, char *cluster,
|
||||
int *master_id, char *master_conninfo_out);
|
||||
|
||||
#endif
|
||||
|
||||
9
debian/DEBIAN/control
vendored
Normal file
9
debian/DEBIAN/control
vendored
Normal 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
37
errcode.h
Normal 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
213
log.c
Normal 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
121
log.h
Normal 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
|
||||
18
repmgr.conf
18
repmgr.conf
@@ -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
|
||||
|
||||
51
repmgr.h
51
repmgr.h
@@ -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,40 @@
|
||||
#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;
|
||||
|
||||
char masterport[MAXLEN];
|
||||
|
||||
} t_runtime_options;
|
||||
|
||||
#endif
|
||||
|
||||
36
repmgr.sql
36
repmgr.sql
@@ -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;
|
||||
|
||||
456
repmgrd.c
456
repmgrd.c
@@ -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,40 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "strutil.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 +72,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 +103,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 +112,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);
|
||||
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.node,
|
||||
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 +229,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 +241,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.node,
|
||||
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 +287,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 +349,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 +410,103 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
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 +514,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);
|
||||
}
|
||||
|
||||
87
strutil.c
Normal file
87
strutil.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
38
strutil.h
Normal file
38
strutil.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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, ...);
|
||||
|
||||
#endif /* _STRUTIL_H_ */
|
||||
13
uninstall_repmgr.sql
Normal file
13
uninstall_repmgr.sql
Normal 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;
|
||||
Reference in New Issue
Block a user