Changes when trying to compile:

- Fix Makefile to include check_dir.c
- Add function mkdir_p that was taken from initdb.c
- Replace some strcpy for assignment to const char * to keep compiler quite
- Add STANDBY_NORMAL to initialize action
- fix typos and add headers needed to compile
This commit is contained in:
Jaime Casanova
2010-09-29 05:13:23 -05:00
parent c809558c3f
commit 3b8e8183cc
7 changed files with 232 additions and 88 deletions

View File

@@ -3,7 +3,7 @@
# Copyright (c) 2ndQuadrant, 2010
repmgrd_OBJS = dbutils.o config.o repmgrd.o
repmgr_OBJS = dbutils.o config.o repmgr.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport)

View File

@@ -8,9 +8,16 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "postgres_fe.h"
#include "check_dir.h"
static int mkdir_p(char *path, mode_t omode);
/*
* make sure the directory either doesn't exist or is empty
* we use this function to check the new data directory and
@@ -22,15 +29,12 @@
* or -1 if trouble accessing directory
*/
int
check_dir(const char *dir)
check_dir(char *dir)
{
DIR *chkdir;
struct dirent *file;
int result = 1;
char *dummy_file;
FILE *dummy_fd;
errno = 0;
chkdir = opendir(dir);
@@ -65,7 +69,7 @@ check_dir(const char *dir)
closedir(chkdir);
if (errno != 0)
return -1 /* some kind of I/O error? */
return -1; /* some kind of I/O error? */
return result;
}
@@ -75,7 +79,7 @@ check_dir(const char *dir)
* Create directory
*/
bool
create_directory(const char *dir)
create_directory(char *dir)
{
if (mkdir_p(dir, 0700) == 0)
return true;
@@ -87,7 +91,114 @@ create_directory(const char *dir)
}
bool
set_directory_permissions(const char *dir)
set_directory_permissions(char *dir)
{
return (chmod(data_dir, 0700) != 0) ? false : true;
return (chmod(dir, 0700) != 0) ? false : true;
}
/* function from initdb.c */
/* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */
/*
* this tries to build all the elements of a path to a directory a la mkdir -p
* we assume the path is in canonical form, i.e. uses / as the separator
* we also assume it isn't null.
*
* note that on failure, the path arg has been modified to show the particular
* directory level we had problems with.
*/
static int
mkdir_p(char *path, mode_t omode)
{
struct stat sb;
mode_t numask,
oumask;
int first,
last,
retval;
char *p;
p = path;
oumask = 0;
retval = 0;
#ifdef WIN32
/* skip network and drive specifiers for win32 */
if (strlen(p) >= 2)
{
if (p[0] == '/' && p[1] == '/')
{
/* network drive */
p = strstr(p + 2, "/");
if (p == NULL)
return 1;
}
else if (p[1] == ':' &&
((p[0] >= 'a' && p[0] <= 'z') ||
(p[0] >= 'A' && p[0] <= 'Z')))
{
/* local drive */
p += 2;
}
}
#endif
if (p[0] == '/') /* Skip leading '/'. */
++p;
for (first = 1, last = 0; !last; ++p)
{
if (p[0] == '\0')
last = 1;
else if (p[0] != '/')
continue;
*p = '\0';
if (!last && p[1] == '\0')
last = 1;
if (first)
{
/*
* POSIX 1003.2: For each dir operand that does not name an
* existing directory, effects equivalent to those caused by the
* following command shall occcur:
*
* mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode]
* dir
*
* We change the user's umask and then restore it, instead of
* doing chmod's.
*/
oumask = umask(0);
numask = oumask & ~(S_IWUSR | S_IXUSR);
(void) umask(numask);
first = 0;
}
if (last)
(void) umask(oumask);
/* check for pre-existing directory; ok if it's a parent */
if (stat(path, &sb) == 0)
{
if (!S_ISDIR(sb.st_mode))
{
if (last)
errno = EEXIST;
else
errno = ENOTDIR;
retval = 1;
break;
}
}
else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
{
retval = 1;
break;
}
if (!last)
*p = '/';
}
if (!first && !last)
(void) umask(oumask);
return retval;
}

View File

@@ -4,6 +4,6 @@
*
*/
int check_dir(const char *dir);
bool create_directory(const char *dir);
bool set_directory_permissions(const char *dir);
int check_dir(char *dir);
bool create_directory(char *dir);
bool set_directory_permissions(char *dir);

View File

@@ -8,7 +8,7 @@
#include "repmgr.h"
void
parse_config(const *char config_file, char *cluster_name, int *node, char *conninfo)
parse_config(const char *config_file, char *cluster_name, int *node, char *conninfo)
{
char *s, buff[256];
FILE *fp = fopen (config_file, "r");

View File

@@ -111,11 +111,11 @@ guc_setted(PGconn *conn, const char *parameter, const char *op, const char *valu
}
char *
const char *
get_cluster_size(PGconn *conn)
{
PGresult *res;
char *size;
const char *size;
char sqlquery[8192];
sprintf(sqlquery, "SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
@@ -129,7 +129,7 @@ get_cluster_size(PGconn *conn)
PQfinish(conn);
exit(1);
}
strcpy(size, PQgetvalue(res, 0, 0))
size = PQgetvalue(res, 0, 0);
PQclear(res);
return size;
}

146
repmgr.c
View File

@@ -21,12 +21,13 @@
#define RECOVERY_FILE "recovery.conf"
#define RECOVERY_DONE_FILE "recovery.done"
#define STANDBY_NORMAL 0 /* Not a real action, just to initialize */
#define STANDBY_CLONE 1
#define STANDBY_PROMOTE 2
#define STANDBY_FOLLOW 3
static void help(const char *progname);
static bool create_recovery_file(void);
static bool create_recovery_file(const char *data_dir);
static void do_standby_clone(void);
static void do_standby_promote(void);
@@ -66,7 +67,7 @@ main(int argc, char **argv)
int optindex;
int c;
int action;
int action = STANDBY_NORMAL;
progname = get_progname(argv[0]);
@@ -236,19 +237,14 @@ do_standby_clone(void)
char data_dir_full_path[MAXLEN];
char data_dir[MAXLEN];
char *first_wal_segment, *last_wal_segment;
/* Connection parameters for master only */
keywords[0] = "host";
values[0] = host;
keywords[1] = "port";
values[1] = masterport;
const char *first_wal_segment = NULL;
const char *last_wal_segment = NULL;
if (data_dir == NULL)
strcpy(data_dir, ".");
/* Check this directory could be used as a PGDATA dir */
switch (check_data_dir(data_dir))
switch (check_dir(data_dir))
{
case 0:
/* data_dir not there, must create it */
@@ -289,6 +285,12 @@ do_standby_clone(void)
progname, data_dir, strerror(errno));
}
/* Connection parameters for master only */
keywords[0] = "host";
values[0] = host;
keywords[1] = "port";
values[1] = masterport;
/* We need to connect to check configuration and start a backup */
conn = PQconnectdbParams(keywords, values, true);
if (!conn)
@@ -302,7 +304,7 @@ do_standby_clone(void)
if (!is_supported_version(conn))
{
PQfinish(conn);
fprintf(stderr, _("%s needs PostgreSQL 9.0 or better\n", progname));
fprintf(stderr, _("%s needs PostgreSQL 9.0 or better\n"), progname);
return;
}
@@ -315,26 +317,27 @@ do_standby_clone(void)
}
/* And check if it is well configured */
if (!guc_setted("wal_level", "=", "hot_standby"))
if (!guc_setted(conn, "wal_level", "=", "hot_standby"))
{
PQfinish(conn);
fprintf(stderr, _("%s needs parameter 'wal_level' to be set to 'hot_standby'\n", progname));
fprintf(stderr, _("%s needs parameter 'wal_level' to be set to 'hot_standby'\n"), progname);
return;
}
if (!guc_setted("wal_keep_segments", ">=", "5000"))
if (!guc_setted(conn, "wal_keep_segments", ">=", "5000"))
{
PQfinish(conn);
fprintf(stderr, _("%s needs parameter 'wal_keep_segments' to be set to 5000 or greater\n", progname));
fprintf(stderr, _("%s needs parameter 'wal_keep_segments' to be set to 5000 or greater\n"), progname);
return;
}
if (!guc_setted("archive_mode", "=", "on"))
if (!guc_setted(conn, "archive_mode", "=", "on"))
{
PQfinish(conn);
fprintf(stderr, _("%s needs parameter 'archive_mode' to be set to 'on'\n", progname));
fprintf(stderr, _("%s needs parameter 'archive_mode' to be set to 'on'\n"), progname);
return;
}
printf(_("Succesfully connected to primary. Current installation size is %s\n", get_cluster_size(conn)));
if (verbose)
printf(_("Succesfully connected to primary. Current installation size is %s\n"), get_cluster_size(conn));
/* Check if the tablespace locations exists and that we can write to them */
sprintf(sqlquery, "select location from pg_tablespace where spcname not in ('pg_default', 'pg_global')");
@@ -349,7 +352,7 @@ do_standby_clone(void)
for (i = 0; i < PQntuples(res); i++)
{
/* Check this directory could be used as a PGDATA dir */
switch (check_dir(PQgetvalue(res), i, 0))
switch (check_dir(PQgetvalue(res, i, 0)))
{
case 0:
/* data_dir not there, must create it */
@@ -397,6 +400,7 @@ do_standby_clone(void)
PQclear(res);
PQfinish(conn);
return;
}
}
fprintf(stderr, "Starting backup...\n");
@@ -430,9 +434,9 @@ do_standby_clone(void)
PQfinish(conn);
return;
}
strcpy(first_wal_segment, PQgetvalue(res, 0, 0));
PQclear(res);
PQfinish(conn);
first_wal_segment = PQgetvalue(res, 0, 0);
PQclear(res);
PQfinish(conn);
/* rsync data directory to data_dir */
sprintf(script, "rsync --checksum --keep-dirlinks --compress --progress -r %s:%s %s",
@@ -467,20 +471,20 @@ do_standby_clone(void)
PQfinish(conn);
return;
}
strcpy(last_wal_segment, PQgetvalue(res, 0, 0));
last_wal_segment = PQgetvalue(res, 0, 0);
PQclear(res);
PQfinish(conn);
if (verbose)
printf(_("%s requires primary to keep WAL files %s until at least %s",
progname, first_wal_segment, last_wal_segment));
printf(_("%s requires primary to keep WAL files %s until at least %s"),
progname, first_wal_segment, last_wal_segment);
/* Now, if the rsync failed then exit */
if (r != 0)
return;
/* Finally, write the recovery.conf file */
create_recovery_file();
create_recovery_file(dest_dir);
/* We don't start the service because we still may want to move the directory */
return;
@@ -490,10 +494,6 @@ do_standby_clone(void)
static void
do_standby_promote(void)
{
char myClusterName[MAXLEN];
int myLocalId = -1;
char myConninfo[MAXLEN];
PGconn *conn;
PGresult *res;
char sqlquery[8192];
@@ -504,18 +504,18 @@ do_standby_promote(void)
char recovery_file_path[MAXLEN];
char recovery_done_path[MAXLEN];
/*
* Read the configuration file: repmgr.conf
*/
parse_config(myClusterName, &myLocalId, myConninfo);
if (myLocalId == -1)
{
fprintf(stderr, "Node information is missing. "
"Check the configuration file.\n");
exit(1);
}
/* Connection parameters for standby. always use localhost for standby */
values[0] = "localhost";
values[1] = standbyport;
conn = establishDBConnection(myConninfo, true);
/* We need to connect to check configuration */
conn = PQconnectdbParams(keywords, values, true);
if (!conn)
{
fprintf(stderr, _("%s: could not connect to master\n"),
progname);
return;
}
/* Check we are in a standby node */
if (!is_standby(conn))
@@ -524,7 +524,10 @@ do_standby_promote(void)
return;
}
fprintf(stderr, "Promoting standby...\n");
/* XXX also we need to check if there isn't any master already */
if (verbose)
printf(_("\n%s: Promoting standby...\n"), progname);
/* Get the data directory full path and the last subdirectory */
sprintf(sqlquery, "SELECT setting "
@@ -561,43 +564,47 @@ do_standby_promote(void)
static void
do_standby_follow(void)
{
char myClusterName[MAXLEN];
int myLocalId = -1;
char myConninfo[MAXLEN];
PGconn *conn;
PGresult *res;
char sqlquery[8192];
char script[8192];
char master_conninfo[MAXLEN];
int r;
char data_dir[MAXLEN];
/*
* Read the configuration file: repmgr.conf
*/
parse_config(myClusterName, &myLocalId, myConninfo);
if (myLocalId == -1)
{
fprintf(stderr, "Node information is missing. "
"Check the configuration file.\n");
exit(1);
}
/* Connection parameters for master */
values[0] = host;
values[1] = masterport;
sprintf(master_conninfo, "host=%s", host);
conn = establishDBConnection(master_conninfo, true);
conn = PQconnectdbParams(keywords, values, true);
if (!conn)
{
fprintf(stderr, _("%s: could not connect to master\n"),
progname);
return;
}
/* Check we are going to point to a primary */
/* Check we are going to point to a master */
if (is_standby(conn))
{
fprintf(stderr, "repmgr: The should follow to a primary node\n");
fprintf(stderr, "repmgr: The node to follow should be a master\n");
return;
}
PQfinish(conn);
conn = establishDBConnection(myConninfo, true);
/* Connection parameters for standby. always use localhost for standby */
values[0] = "localhost";
values[1] = standbyport;
/* We need to connect to check configuration */
conn = PQconnectdbParams(keywords, values, true);
if (!conn)
{
fprintf(stderr, _("%s: could not connect to the local standby\n"),
progname);
return;
}
/* Check we are in a standby node */
if (!is_standby(conn))
{
@@ -605,7 +612,8 @@ do_standby_follow(void)
return;
}
fprintf(stderr, "Changing standby's primary...\n");
if (verbose)
printf(_("\n%s: Changing standby's master...\n"), progname);
/* Get the data directory full path and the last subdirectory */
sprintf(sqlquery, "SELECT setting "
@@ -623,7 +631,7 @@ do_standby_follow(void)
PQfinish(conn);
/* Finally, write the recovery.conf file */
if (!create_recovery_file())
if (!create_recovery_file(data_dir))
return;
/* Finally, restart the service */
@@ -643,9 +651,9 @@ do_standby_follow(void)
static void
help(const char *progname)
{
printf(_("\n%s: Replicator manager \n", progname));
printf(_("\n%s: Replicator manager \n"), progname);
printf(_("Usage:\n"));
printf(_(" %s [OPTIONS] standby {clone|promote|follow} [master]\n", progname));
printf(_(" %s [OPTIONS] standby {clone|promote|follow} [master]\n"), progname);
printf(_("\nOptions:\n"));
printf(_(" --help show this help, then exit\n"));
printf(_(" --version output version information, then exit\n"));
@@ -655,7 +663,7 @@ help(const char *progname)
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
printf(_(" -p, --port=PORT database server port\n"));
printf(_(" -U, --username=USERNAME user name to connect as\n"));
printf(_("\n%s performs some tasks like clone a node, promote it ", progname));
printf(_("\n%s performs some tasks like clone a node, promote it "), progname);
printf(_("or making follow another node and then exits.\n"));
printf(_("COMMANDS:\n"));
printf(_(" standby clone [node] - allows creation of a new standby\n"));
@@ -666,7 +674,7 @@ help(const char *progname)
static bool
create_recovery_file(void)
create_recovery_file(const char *data_dir)
{
FILE *recovery_file;
char recovery_file_path[MAXLEN];

View File

@@ -28,10 +28,11 @@ PGconn *primaryConn;
const char *progname;
const char *config_file = NULL;
bool verbose = false;
char *config_file = NULL;
bool verbose = false;
static void help(const char *progname);
void checkClusterConfiguration(void);
void checkNodeConfiguration(char *conninfo);
void getPrimaryConnection(void);
@@ -53,7 +54,6 @@ main(int argc, char **argv)
int optindex;
int c;
int action;
char conninfo[MAXLEN];
@@ -385,3 +385,28 @@ walLocationToBytes(char *wal_location)
}
return ((xlogid * 16 * 1024 * 1024 * 255) + xrecoff);
}
static void
help(const char *progname)
{
printf(_("\n%s: Replicator manager \n"), progname);
printf(_("Usage:\n"));
printf(_(" %s [OPTIONS] standby {clone|promote|follow} [master]\n"), progname);
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(_("\nConnection options:\n"));
printf(_(" -d, --dbname=DBNAME database to connect to\n"));
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
printf(_(" -p, --port=PORT database server port\n"));
printf(_(" -U, --username=USERNAME user name to connect as\n"));
printf(_("\n%s performs some tasks like clone a node, promote it "), progname);
printf(_("or making follow another node and then exits.\n"));
printf(_("COMMANDS:\n"));
printf(_(" standby clone [node] - allows creation of a new standby\n"));
printf(_(" standby promote - allows manual promotion of a specific standby into a "));
printf(_("new master in the event of a failover\n"));
printf(_(" standby follow [node] - allows the standby to re-point itself to a new master\n"));
}