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>
This commit is contained in:
Dan Farina
2011-02-04 18:27:28 -08:00
13 changed files with 340 additions and 201 deletions

4
.gitignore vendored
View File

@@ -0,0 +1,4 @@
*~
*.o
repmgr
repmgrd

7
CREDITS Normal file
View File

@@ -0,0 +1,7 @@
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>

3
HISTORY Normal file
View File

@@ -0,0 +1,3 @@
1.0.0 2010-12-05 First public release
1.0.1 2010-12-XX Fix missing "--force" option in help

View File

@@ -212,6 +212,8 @@ If you give a password to the user, you need to create a ``.pgpass`` file for
them as well to allow automatic login. In this case you might use the them as well to allow automatic login. In this case you might use the
``md5`` authentication method instead of ``trust`` for the repmgr user. ``md5`` authentication method instead of ``trust`` for the repmgr user.
Don't forget to restart the database server after making all these changes.
Configuration File Configuration File
================== ==================
@@ -627,3 +629,16 @@ and on “prime."
The servers are now again acting as primary on “prime" and standby on “standby". The servers are now again acting as primary on “prime" and standby on “standby".
License and Contributions
=========================
repmgr is licensed under the GPL v3. All of its code and documentation is
Copyright 2010, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
details.
Contributions to repmgr are welcome, and listed in the file CREDITS.
2ndQuadrant Limited requires that any contributions provide a copyright
assignment and a disclaimer of any work-for-hire ownership claims from the
employer of the developer. This lets us make sure that all of the repmgr
distribution remains free code. Please contact info@2ndQuadrant.com for a
copy of the relevant Copyright Assignment Form.

View File

@@ -1,10 +1,21 @@
/* /*
* check_dir.c * check_dir.c - Directories management functions
* *
* Copyright (c) 2ndQuadrant, 2010 * Copyright (C) 2ndQuadrant, 2010
* Copyright (c) Heroku, 2010 *
* 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> #include <sys/stat.h>
@@ -36,9 +47,9 @@ static int mkdir_p(char *path, mode_t omode);
int int
check_dir(char *dir) check_dir(char *dir)
{ {
DIR *chkdir; DIR *chkdir;
struct dirent *file; struct dirent *file;
int result = 1; int result = 1;
errno = 0; errno = 0;
@@ -50,7 +61,7 @@ check_dir(char *dir)
while ((file = readdir(chkdir)) != NULL) while ((file = readdir(chkdir)) != NULL)
{ {
if (strcmp(".", file->d_name) == 0 || if (strcmp(".", file->d_name) == 0 ||
strcmp("..", file->d_name) == 0) strcmp("..", file->d_name) == 0)
{ {
/* skip this and parent directory */ /* skip this and parent directory */
continue; continue;
@@ -74,7 +85,7 @@ check_dir(char *dir)
closedir(chkdir); closedir(chkdir);
if (errno != 0) if (errno != 0)
return -1; /* some kind of I/O error? */ return -1; /* some kind of I/O error? */
return result; return result;
} }
@@ -90,7 +101,7 @@ create_directory(char *dir)
return true; return true;
fprintf(stderr, _("Could not create directory \"%s\": %s\n"), fprintf(stderr, _("Could not create directory \"%s\": %s\n"),
dir, strerror(errno)); dir, strerror(errno));
return false; return false;
} }
@@ -119,10 +130,10 @@ mkdir_p(char *path, mode_t omode)
{ {
struct stat sb; struct stat sb;
mode_t numask, mode_t numask,
oumask; oumask;
int first, int first,
last, last,
retval; retval;
char *p; char *p;
p = path; p = path;
@@ -141,8 +152,8 @@ mkdir_p(char *path, mode_t omode)
return 1; return 1;
} }
else if (p[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 */ /* local drive */
p += 2; p += 2;

View File

@@ -2,6 +2,19 @@
* check_dir.h * check_dir.h
* Copyright (c) 2ndQuadrant, 2010 * Copyright (c) 2ndQuadrant, 2010
* *
* 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/>.
*
*/ */
int check_dir(char *dir); int check_dir(char *dir);

View File

@@ -1,10 +1,20 @@
/* /*
* config.c * config.c - Functions to parse the config file
* Copyright (C) 2ndQuadrant, 2010
* *
* Copyright (c) 2ndQuadrant, 2010 * This program is free software: you can redistribute it and/or modify
* Copyright (c) Heroku, 2010 * 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 "repmgr.h" #include "repmgr.h"

View File

@@ -2,6 +2,19 @@
* config.h * config.h
* Copyright (c) 2ndQuadrant, 2010 * Copyright (c) 2ndQuadrant, 2010
* *
* 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, void parse_config(const char *config_file, char *cluster_name, int *node,

View File

@@ -1,10 +1,19 @@
/* /*
* dbutils.c * dbutils.c - Database connection/management functions
* Copyright (C) 2ndQuadrant, 2010
* *
* Copyright (c) 2ndQuadrant, 2010 * This program is free software: you can redistribute it and/or modify
* Copyright (c) Heroku, 2010 * 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.
* *
* Database connection/management functions * 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/>.
* *
*/ */
@@ -113,8 +122,8 @@ guc_setted(PGconn *conn, const char *parameter, const char *op,
char sqlquery[QUERY_STR_LEN]; char sqlquery[QUERY_STR_LEN];
sqlquery_snprintf(sqlquery, "SELECT true FROM pg_settings " sqlquery_snprintf(sqlquery, "SELECT true FROM pg_settings "
" WHERE name = '%s' AND setting %s '%s'", " WHERE name = '%s' AND setting %s '%s'",
parameter, op, value); parameter, op, value);
res = PQexec(conn, sqlquery); res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -190,7 +199,7 @@ getMasterConnection(PGconn *standby_conn, int id, char *cluster,
/* find all nodes belonging to this cluster */ /* find all nodes belonging to this cluster */
sqlquery_snprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes " sqlquery_snprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes "
" WHERE cluster = '%s' and id <> %d", " WHERE cluster = '%s' and id <> %d",
cluster, cluster, id); cluster, cluster, id);
res1 = PQexec(standby_conn, sqlquery); res1 = PQexec(standby_conn, sqlquery);
if (PQresultStatus(res1) != PGRES_TUPLES_OK) if (PQresultStatus(res1) != PGRES_TUPLES_OK)

View File

@@ -2,6 +2,19 @@
* dbutils.h * dbutils.h
* Copyright (c) 2ndQuadrant, 2010 * Copyright (c) 2ndQuadrant, 2010
* *
* 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/>.
*
*/ */
PGconn *establishDBConnection(const char *conninfo, const bool exit_on_error); PGconn *establishDBConnection(const char *conninfo, const bool exit_on_error);

325
repmgr.c
View File

@@ -1,16 +1,27 @@
/* /*
* repmgr.c * repmgr.c - Command interpreter for the repmgr
* *
* Copyright (c) 2ndQuadrant, 2010
* Copyright (c) Heroku, 2010
* *
* Command interpreter for the repmgr
* This module is a command-line utility to easily setup a cluster of * This module is a command-line utility to easily setup a cluster of
* hot standby servers for an HA environment * hot standby servers for an HA environment
* *
* Commands implemented are. * Commands implemented are.
* MASTER REGISTER, STANDBY REGISTER, STANDBY CLONE, STANDBY FOLLOW, * MASTER REGISTER, STANDBY REGISTER, STANDBY CLONE, STANDBY FOLLOW,
* STANDBY PROMOTE * STANDBY PROMOTE
*
* 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 "repmgr.h"
@@ -71,7 +82,8 @@ char *server_cmd = NULL;
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
static struct option long_options[] = { static struct option long_options[] =
{
{"dbname", required_argument, NULL, 'd'}, {"dbname", required_argument, NULL, 'd'},
{"host", required_argument, NULL, 'h'}, {"host", required_argument, NULL, 'h'},
{"port", required_argument, NULL, 'p'}, {"port", required_argument, NULL, 'p'},
@@ -111,40 +123,40 @@ main(int argc, char **argv)
{ {
switch (c) switch (c)
{ {
case 'd': case 'd':
dbname = optarg; dbname = optarg;
break; break;
case 'h': case 'h':
host = optarg; host = optarg;
break; break;
case 'p': case 'p':
masterport = optarg; masterport = optarg;
break; break;
case 'U': case 'U':
username = optarg; username = optarg;
break;
case 'D':
dest_dir = optarg;
break; break;
case 'D': case 'f':
dest_dir = optarg; config_file = optarg;
break; break;
case 'f': case 'R':
config_file = optarg; remote_user = optarg;
break; break;
case 'R': case 'w':
remote_user = optarg; wal_keep_segments = optarg;
break; break;
case 'w': case 'F':
wal_keep_segments = optarg; force = true;
break; break;
case 'F': case 'v':
force = true; verbose = true;
break; break;
case 'v': default:
verbose = true;
break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
exit(1); exit(1);
} }
} }
@@ -214,14 +226,14 @@ main(int argc, char **argv)
switch (optind < argc) switch (optind < argc)
{ {
case 0: case 0:
break; break;
default: default:
fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
progname, argv[optind + 1]); progname, argv[optind + 1]);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
exit(1); exit(1);
} }
if (!check_parameters_for_action(action)) if (!check_parameters_for_action(action))
@@ -262,25 +274,25 @@ main(int argc, char **argv)
switch (action) switch (action)
{ {
case MASTER_REGISTER: case MASTER_REGISTER:
do_master_register(); do_master_register();
break; break;
case STANDBY_REGISTER: case STANDBY_REGISTER:
do_standby_register(); do_standby_register();
break; break;
case STANDBY_CLONE: case STANDBY_CLONE:
do_standby_clone(); do_standby_clone();
break; break;
case STANDBY_PROMOTE: case STANDBY_PROMOTE:
do_standby_promote(); do_standby_promote();
break; break;
case STANDBY_FOLLOW: case STANDBY_FOLLOW:
do_standby_follow(); do_standby_follow();
break; break;
default: default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
exit(1); exit(1);
} }
return 0; return 0;
@@ -641,29 +653,29 @@ do_standby_clone(void)
{ {
case 0: case 0:
/* dest_dir not there, must create it */ /* dest_dir not there, must create it */
if (verbose) if (verbose)
printf(_("creating directory %s ... "), dest_dir); printf(_("creating directory %s ... "), dest_dir);
fflush(stdout); fflush(stdout);
if (!create_directory(dest_dir)) if (!create_directory(dest_dir))
{ {
fprintf(stderr, _("%s: couldn't create directory %s ... "), fprintf(stderr, _("%s: couldn't create directory %s ... "),
progname, dest_dir); progname, dest_dir);
return; return;
} }
break; break;
case 1: case 1:
/* Present but empty, fix permissions and use it */ /* Present but empty, fix permissions and use it */
if (verbose) if (verbose)
printf(_("fixing permissions on existing directory %s ... "), printf(_("fixing permissions on existing directory %s ... "),
dest_dir); dest_dir);
fflush(stdout); fflush(stdout);
if (!set_directory_permissions(dest_dir)) if (!set_directory_permissions(dest_dir))
{ {
fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"), fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"),
progname, dest_dir, strerror(errno)); progname, dest_dir, strerror(errno));
return; return;
} }
break; break;
case 2: case 2:
@@ -674,20 +686,20 @@ do_standby_clone(void)
pg_dir = is_pg_dir(dest_dir); pg_dir = is_pg_dir(dest_dir);
if (pg_dir && !force) if (pg_dir && !force)
{ {
fprintf(stderr, _("\nThis looks like a PostgreSQL directroy.\n" fprintf(stderr, _("\nThis looks like a PostgreSQL directroy.\n"
"If you are sure you want to clone here, " "If you are sure you want to clone here, "
"please check there is no PostgreSQL server " "please check there is no PostgreSQL server "
"running and use the --force option\n")); "running and use the --force option\n"));
return; return;
} }
else if (pg_dir && force) else if (pg_dir && force)
{ {
/* Let it continue */ /* Let it continue */
break; break;
} }
else else
return; return;
default: default:
/* Trouble accessing directory */ /* Trouble accessing directory */
fprintf(stderr, _("%s: could not access directory \"%s\": %s\n"), fprintf(stderr, _("%s: could not access directory \"%s\": %s\n"),
@@ -775,23 +787,23 @@ do_standby_clone(void)
{ {
case 0: case 0:
/* tblspc_dir not there, must create it */ /* tblspc_dir not there, must create it */
if (verbose) if (verbose)
printf(_("creating directory \"%s\"... "), tblspc_dir); printf(_("creating directory \"%s\"... "), tblspc_dir);
fflush(stdout); fflush(stdout);
if (!create_directory(tblspc_dir)) if (!create_directory(tblspc_dir))
{ {
fprintf(stderr, fprintf(stderr,
_("%s: couldn't create directory \"%s\"... "), _("%s: couldn't create directory \"%s\"... "),
progname, tblspc_dir); progname, tblspc_dir);
PQclear(res); PQclear(res);
PQfinish(conn); PQfinish(conn);
return; return;
} }
break; break;
case 1: case 1:
/* Present but empty, fix permissions and use it */ /* Present but empty, fix permissions and use it */
if (verbose) if (verbose)
printf(_("fixing permissions on existing directory \"%s\"... "), printf(_("fixing permissions on existing directory \"%s\"... "),
tblspc_dir); tblspc_dir);
fflush(stdout); fflush(stdout);
@@ -800,9 +812,9 @@ do_standby_clone(void)
{ {
fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"), fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"),
progname, tblspc_dir, strerror(errno)); progname, tblspc_dir, strerror(errno));
PQclear(res); PQclear(res);
PQfinish(conn); PQfinish(conn);
return; return;
} }
break; break;
case 2: case 2:
@@ -1426,12 +1438,12 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
strcat(options, strcat(options,
" --exclude=pg_xlog* --exclude=pg_control --exclude=*.pid"); " --exclude=pg_xlog* --exclude=pg_control --exclude=*.pid");
maxlen_snprintf(script, "rsync %s %s:%s/* %s", maxlen_snprintf(script, "rsync %s %s:%s/* %s",
options, host_string, remote_path, local_path); options, host_string, remote_path, local_path);
} }
else else
{ {
maxlen_snprintf(script, "rsync %s %s:%s %s/.", maxlen_snprintf(script, "rsync %s %s:%s %s/.",
options, host_string, remote_path, local_path); options, host_string, remote_path, local_path);
} }
if (verbose) if (verbose)
@@ -1458,105 +1470,110 @@ check_parameters_for_action(const int action)
switch (action) switch (action)
{ {
case MASTER_REGISTER: case MASTER_REGISTER:
/* /*
* To register a master we only need the repmgr.conf * To register a master we only need the repmgr.conf
* all other parameters are at least useless and could be * all other parameters are at least useless and could be
* confusing so reject them * confusing so reject them
*/ */
if ((host != NULL) || (masterport != NULL) || if ((host != NULL) || (masterport != NULL) ||
(username != NULL) || (dbname != NULL)) (username != NULL) || (dbname != NULL))
{ {
fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a MASTER REGISTER command."); fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a MASTER REGISTER command.");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
if (dest_dir != NULL) { if (dest_dir != NULL)
fprintf(stderr, "\nYou don't need a destination directory for MASTER REGISTER command"); {
fprintf(stderr, "\nYou don't need a destination directory for MASTER REGISTER command");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
break; break;
case STANDBY_REGISTER: case STANDBY_REGISTER:
/* /*
* To register a standby we only need the repmgr.conf * To register a standby we only need the repmgr.conf
* we don't need connection parameters to the master * we don't need connection parameters to the master
* because we can detect the master in repl_nodes * because we can detect the master in repl_nodes
*/ */
if ((host != NULL) || (masterport != NULL) || if ((host != NULL) || (masterport != NULL) ||
(username != NULL) || (dbname != NULL)) (username != NULL) || (dbname != NULL))
{ {
fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a STANDBY REGISTER command."); fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a STANDBY REGISTER command.");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
if (dest_dir != NULL) { if (dest_dir != NULL)
fprintf(stderr, "\nYou don't need a destination directory for STANDBY REGISTER command"); {
fprintf(stderr, "\nYou don't need a destination directory for STANDBY REGISTER command");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
break; break;
case STANDBY_PROMOTE: case STANDBY_PROMOTE:
/* /*
* To promote a standby we only need the repmgr.conf * To promote a standby we only need the repmgr.conf
* we don't want connection parameters to the master * we don't want connection parameters to the master
* because we will try to detect the master in repl_nodes * because we will try to detect the master in repl_nodes
* if we can't find it then the promote action will be cancelled * if we can't find it then the promote action will be cancelled
*/ */
if ((host != NULL) || (masterport != NULL) || if ((host != NULL) || (masterport != NULL) ||
(username != NULL) || (dbname != NULL)) (username != NULL) || (dbname != NULL))
{ {
fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a STANDBY PROMOTE command."); fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a STANDBY PROMOTE command.");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
if (dest_dir != NULL) { if (dest_dir != NULL)
fprintf(stderr, "\nYou don't need a destination directory for STANDBY PROMOTE command"); {
fprintf(stderr, "\nYou don't need a destination directory for STANDBY PROMOTE command");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
break; break;
case STANDBY_FOLLOW: case STANDBY_FOLLOW:
/* /*
* To make a standby follow a master we only need the repmgr.conf * To make a standby follow a master we only need the repmgr.conf
* we don't want connection parameters to the new master * we don't want connection parameters to the new master
* because we will try to detect the master in repl_nodes * because we will try to detect the master in repl_nodes
* if we can't find it then the follow action will be cancelled * if we can't find it then the follow action will be cancelled
*/ */
if ((host != NULL) || (masterport != NULL) || if ((host != NULL) || (masterport != NULL) ||
(username != NULL) || (dbname != NULL)) (username != NULL) || (dbname != NULL))
{ {
fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a STANDBY FOLLOW command."); fprintf(stderr, "\nYou can't use connection parameters to the master when issuing a STANDBY FOLLOW command.");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
if (dest_dir != NULL) { if (dest_dir != NULL)
fprintf(stderr, "\nYou don't need a destination directory for STANDBY FOLLOW command"); {
fprintf(stderr, "\nYou don't need a destination directory for STANDBY FOLLOW command");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
break; break;
case STANDBY_CLONE: case STANDBY_CLONE:
/* /*
* To clone a master into a standby we need connection parameters * To clone a master into a standby we need connection parameters
* repmgr.conf is useless because we don't have a server running * repmgr.conf is useless because we don't have a server running
* in the standby * in the standby
*/ */
if (config_file != NULL) { if (config_file != NULL)
fprintf(stderr, "\nYou need to use connection parameters to the master when issuing a STANDBY CLONE command."); {
fprintf(stderr, "\nYou need to use connection parameters to the master when issuing a STANDBY CLONE command.");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
ok = false; ok = false;
} }
break; break;
} }
return ok; return ok;

View File

@@ -2,7 +2,19 @@
* repmgr.h * repmgr.h
* *
* Copyright (c) 2ndQuadrant, 2010 * Copyright (c) 2ndQuadrant, 2010
* Copyright (c) Heroku, 2010 *
* 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/>.
* *
*/ */

View File

@@ -1,12 +1,24 @@
/* /*
* repmgrd.c * repmgrd.c - Replication manager daemon
* *
* Copyright (c) 2ndQuadrant, 2010 * Copyright (C) 2ndQuadrant, 2010
* Copyright (c) Heroku, 2010
* *
* Replication manager daemon
* This module connects to the nodes of a replication cluster and monitors * 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> #include <signal.h>
@@ -74,7 +86,8 @@ static void setup_cancel_handler(void);
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
static struct option long_options[] = { static struct option long_options[] =
{
{"config", required_argument, NULL, 'f'}, {"config", required_argument, NULL, 'f'},
{"verbose", no_argument, NULL, 'v'}, {"verbose", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
@@ -107,16 +120,16 @@ main(int argc, char **argv)
{ {
switch (c) switch (c)
{ {
case 'f': case 'f':
config_file = optarg; config_file = optarg;
break; break;
case 'v': case 'v':
verbose = true; verbose = true;
break; break;
default: default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname); progname);
exit(1); exit(1);
} }
} }
@@ -235,7 +248,6 @@ MonitorExecute(void)
{ {
fprintf(stderr, "\n%s: We couldn't reconnect to master, checking if ", fprintf(stderr, "\n%s: We couldn't reconnect to master, checking if ",
progname); progname);
fprintf(stderr, "%s: another node has been promoted.\n", progname);
for (connection_retries = 0; connection_retries < 6; for (connection_retries = 0; connection_retries < 6;
connection_retries++) connection_retries++)
{ {
@@ -265,7 +277,7 @@ MonitorExecute(void)
if (!is_standby(myLocalConn)) if (!is_standby(myLocalConn))
{ {
fprintf(stderr, "\n%s: seems like we have been promoted, so exit from monitoring...\n", fprintf(stderr, "\n%s: seems like we have been promoted, so exit from monitoring...\n",
progname); progname);
CloseConnections(); CloseConnections();
exit(1); exit(1);
} }
@@ -324,7 +336,7 @@ MonitorExecute(void)
"INSERT INTO repmgr_%s.repl_monitor " "INSERT INTO repmgr_%s.repl_monitor "
"VALUES(%d, %d, '%s'::timestamp with time zone, " "VALUES(%d, %d, '%s'::timestamp with time zone, "
" '%s', '%s', " " '%s', '%s', "
" %lld, %lld)", myClusterName, " %lld, %lld)", myClusterName,
primaryId, myLocalId, monitor_standby_timestamp, primaryId, myLocalId, monitor_standby_timestamp,
last_wal_primary_location, last_wal_primary_location,
last_wal_standby_received, last_wal_standby_received,
@@ -347,8 +359,8 @@ checkClusterConfiguration(void)
PGresult *res; PGresult *res;
sqlquery_snprintf(sqlquery, "SELECT oid FROM pg_class " sqlquery_snprintf(sqlquery, "SELECT oid FROM pg_class "
" WHERE oid = 'repmgr_%s.repl_nodes'::regclass", " WHERE oid = 'repmgr_%s.repl_nodes'::regclass",
myClusterName); myClusterName);
res = PQexec(myLocalConn, sqlquery); res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
@@ -386,7 +398,7 @@ checkNodeConfiguration(char *conninfo)
/* Check if we have my node information in repl_nodes */ /* Check if we have my node information in repl_nodes */
sqlquery_snprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes " sqlquery_snprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes "
" WHERE id = %d AND cluster = '%s' ", " WHERE id = %d AND cluster = '%s' ",
myClusterName, myLocalId, myClusterName); myClusterName, myLocalId, myClusterName);
res = PQexec(myLocalConn, sqlquery); res = PQexec(myLocalConn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -408,7 +420,7 @@ checkNodeConfiguration(char *conninfo)
/* Adding the node */ /* Adding the node */
sqlquery_snprintf(sqlquery, "INSERT INTO repmgr_%s.repl_nodes " sqlquery_snprintf(sqlquery, "INSERT INTO repmgr_%s.repl_nodes "
"VALUES (%d, '%s', '%s')", "VALUES (%d, '%s', '%s')",
myClusterName, myLocalId, myClusterName, conninfo); myClusterName, myLocalId, myClusterName, conninfo);
if (!PQexec(primaryConn, sqlquery)) if (!PQexec(primaryConn, sqlquery))