From 3b8e8183cce4ac1ae7adfae89152b27ebde8440a Mon Sep 17 00:00:00 2001 From: Jaime Casanova Date: Wed, 29 Sep 2010 05:13:23 -0500 Subject: [PATCH] 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 --- Makefile | 2 +- check_dir.c | 127 ++++++++++++++++++++++++++++++++++++++++++--- check_dir.h | 6 +-- config.c | 2 +- dbutils.c | 6 +-- repmgr.c | 146 +++++++++++++++++++++++++++------------------------- repmgrd.c | 31 +++++++++-- 7 files changed, 232 insertions(+), 88 deletions(-) diff --git a/Makefile b/Makefile index 07cfb136..cf3124b0 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/check_dir.c b/check_dir.c index aa151ba0..12745f08 100644 --- a/check_dir.c +++ b/check_dir.c @@ -8,9 +8,16 @@ #include #include #include +#include +#include +#include +#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; } diff --git a/check_dir.h b/check_dir.h index 987925dc..b1e9d854 100644 --- a/check_dir.h +++ b/check_dir.h @@ -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); diff --git a/config.c b/config.c index 57848b45..4d15f0b3 100644 --- a/config.c +++ b/config.c @@ -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"); diff --git a/dbutils.c b/dbutils.c index 6664e273..c16c4d5a 100644 --- a/dbutils.c +++ b/dbutils.c @@ -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; } diff --git a/repmgr.c b/repmgr.c index f897fae6..04d3bb52 100644 --- a/repmgr.c +++ b/repmgr.c @@ -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]; diff --git a/repmgrd.c b/repmgrd.c index 567d163f..f76a4c09 100644 --- a/repmgrd.c +++ b/repmgrd.c @@ -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")); +}