Skip to content

Commit

Permalink
Merge commit '3ef1fa126d9c9b9ba3b29deab7f67218cdf7ce10'
Browse files Browse the repository at this point in the history
Conflicts:
	.gitignore
	Makefile
	README.rst
	check_dir.c
	config.c
	config.h
	dbutils.h
	repmgr.c
	repmgr.conf
	repmgr.h
	repmgrd.c
  • Loading branch information
Greg Smith authored and Greg Smith committed Feb 15, 2011
2 parents ce06e6c + 3ef1fa1 commit 20af4ff
Show file tree
Hide file tree
Showing 15 changed files with 542 additions and 234 deletions.
1 change: 1 addition & 0 deletions CREDITS
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Bas van Oostveen <v.oostveen@gmail.com>
Hannu Krosing <hannu@2ndQuadrant.com> Hannu Krosing <hannu@2ndQuadrant.com>
Cédric Villemain <cedric@2ndquadrant.com> Cédric Villemain <cedric@2ndquadrant.com>
Charles Duffy <charles@dyfis.net> Charles Duffy <charles@dyfis.net>
Daniel Farina <daniel@heroku.com>
15 changes: 13 additions & 2 deletions Makefile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# Makefile # Makefile
# Copyright (c) 2ndQuadrant, 2010-2011 # Copyright (c) 2ndQuadrant, 2010-2011


repmgrd_OBJS = dbutils.o config.o repmgrd.o log.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 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_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport) PG_LIBS = $(libpq_pgport)
Expand All @@ -26,10 +28,19 @@ include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk include $(top_srcdir)/contrib/contrib-global.mk
endif endif


# XXX: Try to use PROGRAM construct (see pgxs.mk) someday. Right now
# is overriding pgxs install.
install: install:
$(INSTALL_PROGRAM) repmgrd$(X) '$(DESTDIR)$(bindir)' $(INSTALL_PROGRAM) repmgrd$(X) '$(DESTDIR)$(bindir)'
$(INSTALL_PROGRAM) repmgr$(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: clean:
rm -f *.o rm -f *.o
rm -f repmgrd rm -f repmgrd
Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ following
* ERR_DB_CON 6: Error when trying to connect to a database. * ERR_DB_CON 6: Error when trying to connect to a database.
* ERR_DB_QUERY 7: Error executing a database query. * ERR_DB_QUERY 7: Error executing a database query.
* ERR_PROMOTED 8: Exiting program because the node has been promoted to master. * ERR_PROMOTED 8: Exiting program because the node has been promoted to master.
* ERR_BAD_PASSWORD 9: Password used to connect to a database was rejected.


Detailed walkthrough Detailed walkthrough
==================== ====================
Expand Down
16 changes: 10 additions & 6 deletions check_dir.c
Original file line number Original file line Diff line number Diff line change
@@ -1,6 +1,6 @@
/* /*
* check_dir.c - Directories management functions * check_dir.c - Directories management functions
* Copyright (C) 2ndQuadrant, 2011 * Copyright (C) 2ndQuadrant, 2010-2011
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
Expand All @@ -24,9 +24,12 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>


/* NB: postgres_fe must be included BEFORE check_dir */
#include "postgres_fe.h" #include "postgres_fe.h"
#include "check_dir.h" #include "check_dir.h"


#include "strutil.h"



static int mkdir_p(char *path, mode_t omode); static int mkdir_p(char *path, mode_t omode);


Expand Down Expand Up @@ -64,7 +67,7 @@ check_dir(char *dir)
} }
else else
{ {
result = 2; /* not empty */ result = 2; /* not empty */
break; break;
} }
} }
Expand Down Expand Up @@ -111,7 +114,7 @@ set_directory_permissions(char *dir)




/* function from initdb.c */ /* 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 * this tries to build all the elements of a path to a directory a la mkdir -p
Expand Down Expand Up @@ -219,10 +222,11 @@ mkdir_p(char *path, mode_t omode)
bool bool
is_pg_dir(char *dir) is_pg_dir(char *dir)
{ {
char path[8192]; const size_t buf_sz = 8192;
struct stat sb; 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;
} }
2 changes: 2 additions & 0 deletions config.c
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/ */


#include "config.h" #include "config.h"
#include "repmgr.h"
#include "strutil.h"


void void
parse_config(const char* config_file, t_configuration_options* options) parse_config(const char* config_file, t_configuration_options* options)
Expand Down
1 change: 1 addition & 0 deletions config.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define _REPMGR_CONFIG_H_ #define _REPMGR_CONFIG_H_


#include "repmgr.h" #include "repmgr.h"
#include "strutil.h"


typedef struct typedef struct
{ {
Expand Down
110 changes: 77 additions & 33 deletions dbutils.c
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@


#include "repmgr.h" #include "repmgr.h"


#define MAXQUERY 8192 #include "strutil.h"
#define MAXCONNINFO 1024


PGconn * PGconn *
establishDBConnection(const char *conninfo, const bool exit_on_error) establishDBConnection(const char *conninfo, const bool exit_on_error)
Expand All @@ -33,6 +32,7 @@ establishDBConnection(const char *conninfo, const bool exit_on_error)
{ {
fprintf(stderr, "Connection to database failed: %s", fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn)); PQerrorMessage(conn));

if (exit_on_error) if (exit_on_error)
{ {
PQfinish(conn); PQfinish(conn);
Expand All @@ -44,14 +44,14 @@ establishDBConnection(const char *conninfo, const bool exit_on_error)
} }





bool bool
is_standby(PGconn *conn) is_standby(PGconn *conn)
{ {
PGresult *res; PGresult *res;
bool result; bool result;


res = PQexec(conn, "SELECT pg_is_in_recovery()"); res = PQexec(conn, "SELECT pg_is_in_recovery()");

if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
fprintf(stderr, "Can't query server mode: %s", PQerrorMessage(conn)); fprintf(stderr, "Can't query server mode: %s", PQerrorMessage(conn));
Expand Down Expand Up @@ -79,43 +79,51 @@ pg_version(PGconn *conn, char* major_version)
{ {
PGresult *res; PGresult *res;


int major_version1; int major_version1;
char *major_version2; 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");


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) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn)); fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
PQclear(res); PQclear(res);
PQfinish(conn); PQfinish(conn);
exit(ERR_DB_QUERY); exit(ERR_DB_QUERY);
} }

major_version1 = atoi(PQgetvalue(res, 0, 0)); major_version1 = atoi(PQgetvalue(res, 0, 0));
major_version2 = PQgetvalue(res, 0, 1); major_version2 = PQgetvalue(res, 0, 1);
PQclear(res);


if (major_version1 >= 9) if (major_version1 >= 9)
{ {
/* form a major version string */ /* form a major version string */
snprintf(major_version, MAXVERSIONSTR, "%d.%s", major_version1, major_version2); xsnprintf(major_version, MAXVERSIONSTR, "%d.%s", major_version1,
major_version2);
} }
else else
strcpy(major_version, ""); strcpy(major_version, "");


PQclear(res);

return major_version; return major_version;
} }




bool 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; PGresult *res;
char sqlquery[MAXQUERY]; char sqlquery[QUERY_STR_LEN];


sprintf(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)
Expand All @@ -140,11 +148,13 @@ const char *
get_cluster_size(PGconn *conn) get_cluster_size(PGconn *conn)
{ {
PGresult *res; PGresult *res;
const char *size; const char *size;
char sqlquery[MAXQUERY]; char sqlquery[QUERY_STR_LEN];


sprintf(sqlquery, "SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) " sqlquery_snprintf(
" FROM pg_database "); sqlquery,
"SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
" FROM pg_database ");


res = PQexec(conn, sqlquery); res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
Expand All @@ -162,26 +172,57 @@ get_cluster_size(PGconn *conn)
/* /*
* 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 * 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 * 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; PGconn *master_conn = NULL;
PGresult *res1; PGresult *res1;
PGresult *res2; PGresult *res2;
char sqlquery[MAXQUERY]; char sqlquery[QUERY_STR_LEN];
char master_conninfo[MAXCONNINFO]; char master_conninfo_stack[MAXCONNINFO];
char *master_conninfo = &*master_conninfo_stack;
char schema_str[MAXLEN];
char schema_quoted[MAXLEN];

int i; int i;


/*
* 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;

/*
* 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 */ /* find all nodes belonging to this cluster */
sprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes " sqlquery_snprintf(sqlquery, "SELECT * FROM %s.repl_nodes "
" WHERE cluster = '%s' and id <> %d", " WHERE cluster = '%s' and id <> %d",
cluster, cluster, id); schema_quoted, cluster, id);


res1 = PQexec(standby_conn, sqlquery); res1 = PQexec(standby_conn, sqlquery);
if (PQresultStatus(res1) != PGRES_TUPLES_OK) if (PQresultStatus(res1) != PGRES_TUPLES_OK)
{ {
fprintf(stderr, "Can't get nodes info: %s\n", PQerrorMessage(standby_conn)); fprintf(stderr, "Can't get nodes info: %s\n",
PQerrorMessage(standby_conn));
PQclear(res1); PQclear(res1);
PQfinish(standby_conn); PQfinish(standby_conn);
exit(ERR_DB_QUERY); exit(ERR_DB_QUERY);
Expand All @@ -193,18 +234,21 @@ getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id)
*master_id = atoi(PQgetvalue(res1, i, 0)); *master_id = atoi(PQgetvalue(res1, i, 0));
strncpy(master_conninfo, PQgetvalue(res1, i, 2), MAXCONNINFO); strncpy(master_conninfo, PQgetvalue(res1, i, 2), MAXCONNINFO);
master_conn = establishDBConnection(master_conninfo, false); master_conn = establishDBConnection(master_conninfo, false);

if (PQstatus(master_conn) != CONNECTION_OK) if (PQstatus(master_conn) != CONNECTION_OK)
continue; continue;


/* /*
* I can't use the is_standby() function here because on error that * 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 * function closes the connection i pass and exit, but i still need to
* standby_conn * close standby_conn
*/ */
res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()"); res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()");

if (PQresultStatus(res2) != PGRES_TUPLES_OK) if (PQresultStatus(res2) != PGRES_TUPLES_OK)
{ {
fprintf(stderr, "Can't get recovery state from this node: %s\n", PQerrorMessage(master_conn)); fprintf(stderr, "Can't get recovery state from this node: %s\n",
PQerrorMessage(master_conn));
PQclear(res2); PQclear(res2);
PQfinish(master_conn); PQfinish(master_conn);
continue; continue;
Expand All @@ -229,12 +273,12 @@ getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id)
/* If we finish this loop without finding a master then /* If we finish this loop without finding a master then
* we doesn't have the info or the master has failed (or we * we doesn't have the info or the master has failed (or we
* reached max_connections or superuser_reserved_connections, * reached max_connections or superuser_reserved_connections,
* anything else i'm missing?), * anything else I'm missing?).
*
* Probably we will need to check the error to know if we need * Probably we will need to check the error to know if we need
* to start failover procedure or just fix some situation on the * to start failover procedure or just fix some situation on the
* standby. * standby.
*/ */
PQclear(res1); PQclear(res1);
return NULL; return NULL;
} }

10 changes: 6 additions & 4 deletions dbutils.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
#define _REPMGR_DBUTILS_H_ #define _REPMGR_DBUTILS_H_


PGconn *establishDBConnection(const char *conninfo, const bool exit_on_error); PGconn *establishDBConnection(const char *conninfo, const bool exit_on_error);
bool is_standby(PGconn *conn); bool is_standby(PGconn *conn);
char *pg_version(PGconn *conn, char* major_version); char *pg_version(PGconn *conn, char* major_version);
bool guc_setted(PGconn *conn, const char *parameter, const char *op, const char *value); bool guc_setted(PGconn *conn, const char *parameter, const char *op,
const char *get_cluster_size(PGconn *conn); const char *value);
PGconn * getMasterConnection(PGconn *standby_conn, int id, char *cluster, int *master_id); const char *get_cluster_size(PGconn *conn);
PGconn *getMasterConnection(PGconn *standby_conn, int id, char *cluster,
int *master_id, char *master_conninfo_out);


#endif #endif
Loading

0 comments on commit 20af4ff

Please sign in to comment.