diff --git a/.gitignore b/.gitignore index 349c5013..71670b00 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.o repmgr repmgrd +README.html diff --git a/README.rst b/README.rst index 18192c99..9dd5b2d9 100644 --- a/README.rst +++ b/README.rst @@ -126,6 +126,35 @@ path either. The following recipe should work:: sudo PATH="/usr/pgsql-9.0/bin:$PATH" make USE_PGXS=1 install +Notes on Ubuntu, Debian or other Debian-based Builds +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Debian packages of PostgreSQL put ``pg_config`` into the development package +called ``postgresql-server-dev-$version``. + +When building repmgr against a Debian packages build, you may discover that some +development packages are needed as well. You will need the following development +packages installed:: + + sudo apt-get install libxslt-dev libxml2-dev libpam-dev libedit-dev + +If your using Debian packages for PostgreSQL and are building repmgr with the +USE_PGXS option you also need to install the corresponding development package:: + + sudo apt-get install postgresql-server-dev-9.0 + +If you build and install repmgr manually it will not be on the system path. The +binaries will be installed in /usr/lib/postgresql/$version/bin/ which is not on +the default path. The reason behind this is that Ubuntu/Debian systems manage +multiple installed versions of PostgreSQL on the same system through a wrapper +called pg_wrapper and repmgr is not (yet) known to this wrapper. + +You can solve this in many different ways, the most Debian like is to make an +alternate for repmgr and repmgrd:: + + sudo update-alternatives --install /usr/bin/repmgr repmgr /usr/lib/postgresql/9.0/bin/repmgr 10 + sudo update-alternatives --install /usr/bin/repmgrd repmgrd /usr/lib/postgresql/9.0/bin/repmgrd 10 + Confirm software was built correctly ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -136,8 +165,7 @@ is available by checking its version:: repmgr --version repmgrd --version -You may need to include -the full path of the binary instead, such as this RHEL example:: +You may need to include the full path of the binary instead, such as this RHEL example:: /usr/pgsql-9.0/bin/repmgr --version /usr/pgsql-9.0/bin/repmgrd --version diff --git a/config.c b/config.c index 3e14ec00..5fdd411e 100644 --- a/config.c +++ b/config.c @@ -23,7 +23,7 @@ void -parse_config(const char *config_file, char *cluster_name, int *node, +parse_config(const char *config_file, repmgr_config *config) char *conninfo) { char *s, buff[MAXLINELENGTH]; @@ -32,9 +32,17 @@ parse_config(const char *config_file, char *cluster_name, int *node, FILE *fp = fopen (config_file, "r"); - if (fp == NULL) - return; - + if (fp == NULL) { + fprintf(stderr, _("Could not find configuration file '%s'\n"), config_file); + exit(1); + } + + /* Initialize */ + memset(config->cluster_name, 0, sizeof(config->cluster_name)); + config->node = -1; + memset(config->conninfo, 0, sizeof(config->conninfo)); + memset(config->rsync_options, 0, sizeof(config->rsync_options)); + /* Read next line */ while ((s = fgets (buff, sizeof buff, fp)) != NULL) { @@ -47,18 +55,34 @@ parse_config(const char *config_file, char *cluster_name, int *node, /* Copy into correct entry in parameters struct */ if (strcmp(name, "cluster") == 0) - strncpy (cluster_name, value, MAXLEN); + strncpy (config->cluster_name, value, MAXLEN); else if (strcmp(name, "node") == 0) - *node = atoi(value); + config->node = atoi(value); else if (strcmp(name, "conninfo") == 0) - strncpy (conninfo, value, MAXLEN); + strncpy (config->conninfo, value, MAXLEN); + else if (strcmp(name, "rsync_options") == 0) + strncpy (config->rsync_options, value, QUERY_STR_LEN); else printf ("WARNING: %s/%s: Unknown name/value pair!\n", name, value); } - /* Close file */ fclose (fp); + + /* Check config settings */ + if (strnlen(config->cluster_name, MAXLEN)==0) + { + fprintf(stderr, "Cluster name is missing. " + "Check the configuration file.\n"); + exit(1); + } + + if (config->node == -1) + { + fprintf(stderr, "Node information is missing. " + "Check the configuration file.\n"); + exit(1); + } } char * diff --git a/config.h b/config.h index 88e2a0d8..b66bc6e4 100644 --- a/config.h +++ b/config.h @@ -17,7 +17,14 @@ * */ -void parse_config(const char *config_file, char *cluster_name, int *node, - char *service); +typedef struct +{ + char cluster_name[MAXLEN]; + int node; + char conninfo[MAXLEN]; + char rsync_options[QUERY_STR_LEN]; +} repmgr_config; + +void parse_config(const char *config_file, repmgr_config *config); void parse_line(char *buff, char *name, char *value); char *trim(char *s); diff --git a/repmgr.c b/repmgr.c index 4c008aa6..d9376701 100644 --- a/repmgr.c +++ b/repmgr.c @@ -44,7 +44,6 @@ #define STANDBY_PROMOTE 4 #define STANDBY_FOLLOW 5 - static void help(const char *progname); static bool create_recovery_file(const char *data_dir, char *master_conninfo); static int copy_remote_files(char *host, char *remote_user, char *remote_path, @@ -78,6 +77,7 @@ char *masterport = NULL; char *server_mode = NULL; char *server_cmd = NULL; +repmgr_config config = {}; int main(int argc, char **argv) @@ -262,6 +262,17 @@ main(int argc, char **argv) else dbname = "postgres"; } + + /* + * Read the configuration file: repmgr.conf + */ + parse_config(config_file, &config); + if (config.node == -1) + { + fprintf(stderr, "Node information is missing. " + "Check the configuration file.\n"); + exit(1); + } keywords[2] = "user"; values[2] = username; @@ -306,25 +317,10 @@ do_master_register(void) PGresult *res; char sqlquery[QUERY_STR_LEN]; - char myClusterName[MAXLEN]; - int myLocalId = -1; - char conninfo[MAXLEN]; - bool schema_exists = false; char master_version[MAXVERSIONSTR]; - /* - * Read the configuration file: repmgr.conf - */ - parse_config(config_file, myClusterName, &myLocalId, conninfo); - if (myLocalId == -1) - { - fprintf(stderr, "Node information is missing. " - "Check the configuration file.\n"); - exit(1); - } - - conn = establishDBConnection(conninfo, true); + conn = establishDBConnection(config.conninfo, true); /* master should be v9 or better */ pg_version(conn, master_version); @@ -345,9 +341,7 @@ do_master_register(void) } /* Check if there is a schema for this cluster */ - sqlquery_snprintf(sqlquery, - "SELECT 1 FROM pg_namespace WHERE nspname = 'repmgr_%s'", - myClusterName); + sqlquery_sprintf(sqlquery, "SELECT 1 FROM pg_namespace WHERE nspname = 'repmgr_%s'", config.cluster_name); res = PQexec(conn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { @@ -362,7 +356,7 @@ do_master_register(void) { if (!force) /* and we are not forcing so error */ { - fprintf(stderr, "Schema repmgr_%s already exists.", myClusterName); + fprintf(stderr, "Schema repmgr_%s already exists.", config.cluster_name); PQclear(res); PQfinish(conn); return; @@ -374,11 +368,11 @@ do_master_register(void) if (!schema_exists) { /* ok, create the schema */ - sqlquery_snprintf(sqlquery, "CREATE SCHEMA repmgr_%s", myClusterName); + sqlquery_snprintf(sqlquery, "CREATE SCHEMA repmgr_%s", config.cluster_name); if (!PQexec(conn, sqlquery)) { fprintf(stderr, "Cannot create the schema repmgr_%s: %s\n", - myClusterName, PQerrorMessage(conn)); + config.cluster_name, PQerrorMessage(conn)); PQfinish(conn); return; } @@ -387,12 +381,11 @@ do_master_register(void) sqlquery_snprintf(sqlquery, "CREATE TABLE repmgr_%s.repl_nodes ( " " id integer primary key, " " cluster text not null, " - " conninfo text not null)", myClusterName); + " conninfo text not null)", config.cluster_name); if (!PQexec(conn, sqlquery)) { fprintf(stderr, - "Cannot create the table repmgr_%s.repl_nodes: %s\n", - myClusterName, PQerrorMessage(conn)); + config.cluster_name, PQerrorMessage(conn)); PQfinish(conn); return; } @@ -404,13 +397,12 @@ do_master_register(void) " last_wal_primary_location TEXT NOT NULL, " " last_wal_standby_location TEXT NOT NULL, " " replication_lag BIGINT NOT NULL, " - " apply_lag BIGINT NOT NULL) ", + " apply_lag BIGINT NOT NULL) ", config.cluster_name); myClusterName); if (!PQexec(conn, sqlquery)) { fprintf(stderr, - "Cannot create the table repmgr_%s.repl_monitor: %s\n", - myClusterName, PQerrorMessage(conn)); + config.cluster_name, PQerrorMessage(conn)); PQfinish(conn); return; } @@ -424,12 +416,11 @@ do_master_register(void) " 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", myClusterName, myClusterName); + " WHERE row_number = 1", config.cluster_name, config.cluster_name); if (!PQexec(conn, sqlquery)) { fprintf(stderr, - "Cannot create the view repmgr_%s.repl_status: %s\n", - myClusterName, PQerrorMessage(conn)); + config.cluster_name, PQerrorMessage(conn)); PQfinish(conn); return; } @@ -440,7 +431,7 @@ do_master_register(void) int id; /* Ensure there isn't any other master already registered */ - master_conn = getMasterConnection(conn, myLocalId, myClusterName, &id, + master_conn = getMasterConnection(conn, config.node, config.cluster_name, &id); NULL); if (master_conn != NULL) { @@ -455,7 +446,7 @@ do_master_register(void) { sqlquery_snprintf(sqlquery, "DELETE FROM repmgr_%s.repl_nodes " " WHERE id = %d", - myClusterName, myLocalId); + config.cluster_name, config.node); if (!PQexec(conn, sqlquery)) { @@ -468,7 +459,7 @@ do_master_register(void) sqlquery_snprintf(sqlquery, "INSERT INTO repmgr_%s.repl_nodes " "VALUES (%d, '%s', '%s')", - myClusterName, myLocalId, myClusterName, conninfo); + config.cluster_name, config.node, config.cluster_name, config.conninfo); if (!PQexec(conn, sqlquery)) { @@ -493,25 +484,10 @@ do_standby_register(void) PGresult *res; char sqlquery[QUERY_STR_LEN]; - char myClusterName[MAXLEN]; - int myLocalId = -1; - char conninfo[MAXLEN]; - char master_version[MAXVERSIONSTR]; char standby_version[MAXVERSIONSTR]; - /* - * Read the configuration file: repmgr.conf - */ - parse_config(config_file, myClusterName, &myLocalId, conninfo); - if (myLocalId == -1) - { - fprintf(stderr, "Node information is missing. " - "Check the configuration file.\n"); - exit(1); - } - - conn = establishDBConnection(conninfo, true); + conn = establishDBConnection(config.conninfo, true); /* should be v9 or better */ pg_version(conn, standby_version); @@ -532,7 +508,7 @@ do_standby_register(void) } /* Check if there is a schema for this cluster */ - sqlquery_snprintf(sqlquery, + sqlquery_snprintf(sqlquery, "SELECT 1 FROM pg_namespace WHERE nspname = 'repmgr_%s'", config.cluster_name); "SELECT 1 FROM pg_namespace WHERE nspname = 'repmgr_%s'", myClusterName); res = PQexec(conn, sqlquery); @@ -547,7 +523,7 @@ do_standby_register(void) if (PQntuples(res) == 0) /* schema doesn't exists */ { - fprintf(stderr, "Schema repmgr_%s doesn't exists.", myClusterName); + fprintf(stderr, "Schema repmgr_%s doesn't exists.", config.cluster_name); PQclear(res); PQfinish(conn); return; @@ -555,7 +531,7 @@ do_standby_register(void) PQclear(res); /* check if there is a master in this cluster */ - master_conn = getMasterConnection(conn, myLocalId, myClusterName, + master_conn = getMasterConnection(conn, config.node, config.cluster_name, &master_id); &master_id, NULL); if (!master_conn) return; @@ -587,7 +563,7 @@ do_standby_register(void) { sqlquery_snprintf(sqlquery, "DELETE FROM repmgr_%s.repl_nodes " " WHERE id = %d", - myClusterName, myLocalId); + config.cluster_name, config.node); if (!PQexec(master_conn, sqlquery)) { @@ -601,7 +577,7 @@ do_standby_register(void) sqlquery_snprintf(sqlquery, "INSERT INTO repmgr_%s.repl_nodes " "VALUES (%d, '%s', '%s')", - myClusterName, myLocalId, myClusterName, conninfo); + config.cluster_name, config.node, config.cluster_name, config.conninfo); if (!PQexec(master_conn, sqlquery)) { @@ -749,7 +725,7 @@ do_standby_clone(void) if (!guc_setted(conn, "wal_keep_segments", ">=", wal_keep_segments)) { PQfinish(conn); - fprintf(stderr, _("%s needs parameter 'wal_keep_segments' to be set to %s or greater\n"), wal_keep_segments, progname); + fprintf(stderr, _("%s needs parameter 'wal_keep_segments' to be set to %s or greater\n"), progname, wal_keep_segments); return; } if (!guc_setted(conn, "archive_mode", "=", "on")) @@ -1048,10 +1024,6 @@ do_standby_promote(void) char sqlquery[QUERY_STR_LEN]; char script[MAXLEN]; - char myClusterName[MAXLEN]; - int myLocalId = -1; - char conninfo[MAXLEN]; - PGconn *old_master_conn; int old_master_id; @@ -1062,19 +1034,8 @@ do_standby_promote(void) char standby_version[MAXVERSIONSTR]; - /* - * Read the configuration file: repmgr.conf - */ - parse_config(config_file, myClusterName, &myLocalId, conninfo); - if (myLocalId == -1) - { - fprintf(stderr, "Node information is missing. " - "Check the configuration file.\n"); - exit(1); - } - /* We need to connect to check configuration */ - conn = establishDBConnection(conninfo, true); + conn = establishDBConnection(config.conninfo, true); /* we need v9 or better */ pg_version(conn, standby_version); @@ -1095,8 +1056,8 @@ do_standby_promote(void) } /* we also need to check if there isn't any master already */ - old_master_conn = getMasterConnection(conn, myLocalId, myClusterName, - &old_master_id, NULL); + old_master_conn = getMasterConnection(conn, config.node, config.cluster_name, &old_master_id); + if (old_master_conn != NULL) { PQfinish(old_master_conn); @@ -1141,8 +1102,7 @@ do_standby_promote(void) /* * XXX i'm removing this because it gives an annoying message saying * couldn't connect but is just the server starting up - * - * conn = establishDBConnection(conninfo, true); + * conn = establishDBConnection(config.conninfo, true); * if (is_standby(conn)) * fprintf(stderr, "\n%s: STANDBY PROMOTE failed, this is still a standby node.\n", progname); * else @@ -1162,11 +1122,7 @@ do_standby_follow(void) char sqlquery[QUERY_STR_LEN]; char script[MAXLEN]; - char myClusterName[MAXLEN]; - int myLocalId = -1; - char conninfo[MAXLEN]; char master_conninfo[MAXLEN]; - PGconn *master_conn; int master_id; @@ -1176,17 +1132,8 @@ do_standby_follow(void) char master_version[MAXVERSIONSTR]; char standby_version[MAXVERSIONSTR]; - /* Read the configuration file: repmgr.conf */ - parse_config(config_file, myClusterName, &myLocalId, conninfo); - if (myLocalId == -1) - { - fprintf(stderr, "Node information is missing. " - "Check the configuration file.\n"); - exit(1); - } - /* We need to connect to check configuration */ - conn = establishDBConnection(conninfo, true); + conn = establishDBConnection(config.conninfo, true); /* Check we are in a standby node */ if (!is_standby(conn)) @@ -1205,8 +1152,8 @@ do_standby_follow(void) } /* we also need to check if there is any master in the cluster */ - master_conn = getMasterConnection(conn, myLocalId, myClusterName, - &master_id, master_conninfo); + master_conn = getMasterConnection(conn, config.node, config.cluster_name, &master_id); + if (master_conn == NULL) { PQfinish(conn); @@ -1419,8 +1366,11 @@ copy_remote_files(char *host, char *remote_user, char *remote_path, char host_string[MAXLEN]; int r; - maxlen_snprintf(options, - "--archive --checksum --compress --progress --rsh=ssh"); + if (strnlen(config.rsync_options, QUERY_STR_LEN) == 0) + sprintf(options, "--archive --checksum --compress --progress --rsh=ssh"); + else + strncpy(options, config.rsync_options, QUERY_STR_LEN); + if (force) strcat(options, " --delete"); diff --git a/repmgr.conf b/repmgr.conf index f7287502..a707dc42 100644 --- a/repmgr.conf +++ b/repmgr.conf @@ -1,3 +1,4 @@ cluster=test node=2 conninfo='host=192.168.204.104' +rsync_options=--archive --checksum --compress --progress --rsh=ssh diff --git a/repmgr.h b/repmgr.h index d1d31c15..accf6d0e 100644 --- a/repmgr.h +++ b/repmgr.h @@ -26,12 +26,14 @@ #include "libpq-fe.h" #include "dbutils.h" -#include "config.h" #define PRIMARY_MODE 0 #define STANDBY_MODE 1 #define CONFIG_FILE "repmgr.conf" +#define QUERY_STR_LEN 8192 + +#include "config.h" #endif diff --git a/repmgrd.c b/repmgrd.c index 3f228f5b..bfde9fea 100644 --- a/repmgrd.c +++ b/repmgrd.c @@ -32,11 +32,8 @@ #include "libpq/pqsignal.h" -char myClusterName[MAXLEN]; - /* Local info */ int myLocalMode = STANDBY_MODE; -int myLocalId = -1; PGconn *myLocalConn = NULL; /* Primary info */ @@ -51,6 +48,8 @@ const char *progname; char *config_file = NULL; bool verbose = false; +// should initialize with {0} to be ANSI complaint ? but this raises error with gcc -Wall +repmgr_config config = {}; static void help(const char *progname); static void checkClusterConfiguration(void); @@ -96,7 +95,6 @@ main(int argc, char **argv) int optindex; int c; - char conninfo[MAXLEN]; char standby_version[MAXVERSIONSTR]; progname = get_progname(argv[0]); @@ -146,15 +144,15 @@ main(int argc, char **argv) /* * Read the configuration file: repmgr.conf */ - parse_config(config_file, myClusterName, &myLocalId, conninfo); - if (myLocalId == -1) + parse_config(config_file, &config); + if (config.node == -1) { fprintf(stderr, "Node information is missing. " "Check the configuration file.\n"); exit(1); } - myLocalConn = establishDBConnection(conninfo, true); + myLocalConn = establishDBConnection(config.conninfo, true); /* should be v9 or better */ pg_version(myLocalConn, standby_version); @@ -173,21 +171,21 @@ main(int argc, char **argv) myLocalMode = is_standby(myLocalConn) ? STANDBY_MODE : PRIMARY_MODE; if (myLocalMode == PRIMARY_MODE) { - primaryId = myLocalId; - strcpy(primaryConninfo, conninfo); + primaryId = config.node; + strcpy(primaryConninfo, config.conninfo); primaryConn = myLocalConn; } else { /* I need the id of the primary as well as a connection to it */ - primaryConn = getMasterConnection(myLocalConn, myLocalId, - myClusterName, &primaryId, NULL); + primaryConn = getMasterConnection(myLocalConn, config.node, config.cluster_name, &primaryId); + if (primaryConn == NULL) exit(1); } checkClusterConfiguration(); - checkNodeConfiguration(conninfo); + checkNodeConfiguration(config.conninfo); if (myLocalMode == STANDBY_MODE) { MonitorCheck(); @@ -251,8 +249,8 @@ MonitorExecute(void) for (connection_retries = 0; connection_retries < 6; connection_retries++) { - primaryConn = getMasterConnection(myLocalConn, myLocalId, - myClusterName, &primaryId, NULL); + primaryConn = getMasterConnection(myLocalConn, config.node, config.cluster_name, &primaryId); + if (PQstatus(primaryConn) == CONNECTION_OK) { /* Connected, we can continue the process so break the loop */ @@ -336,8 +334,8 @@ MonitorExecute(void) "INSERT INTO repmgr_%s.repl_monitor " "VALUES(%d, %d, '%s'::timestamp with time zone, " " '%s', '%s', " - " %lld, %lld)", myClusterName, - primaryId, myLocalId, monitor_standby_timestamp, + " %lld, %lld)", config.cluster_name, + primaryId, config.node, monitor_standby_timestamp, last_wal_primary_location, last_wal_standby_received, (lsn_primary - lsn_standby_received), @@ -360,7 +358,7 @@ checkClusterConfiguration(void) sqlquery_snprintf(sqlquery, "SELECT oid FROM pg_class " " WHERE oid = 'repmgr_%s.repl_nodes'::regclass", - myClusterName); + config.cluster_name); res = PQexec(myLocalConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { @@ -398,7 +396,7 @@ checkNodeConfiguration(char *conninfo) /* Check if we have my node information in repl_nodes */ sqlquery_snprintf(sqlquery, "SELECT * FROM repmgr_%s.repl_nodes " " WHERE id = %d AND cluster = '%s' ", - myClusterName, myLocalId, myClusterName); + config.cluster_name, config.node, config.cluster_name); res = PQexec(myLocalConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -421,7 +419,7 @@ checkNodeConfiguration(char *conninfo) /* Adding the node */ sqlquery_snprintf(sqlquery, "INSERT INTO repmgr_%s.repl_nodes " "VALUES (%d, '%s', '%s')", - myClusterName, myLocalId, myClusterName, conninfo); + config.cluster_name, config.node, config.cluster_name, conninfo); if (!PQexec(primaryConn, sqlquery)) {