From abb1068b10a5f558623d6fc8f214c29d09f6bb28 Mon Sep 17 00:00:00 2001 From: Jimmy Yih Date: Sun, 4 Nov 2018 22:54:53 -0800 Subject: [PATCH] Auto generate recovery conf at the end of pg_rewind --- src/bin/pg_rewind/Makefile | 2 +- src/bin/pg_rewind/fetch.h | 3 + src/bin/pg_rewind/libpq_fetch.c | 138 ++++++++++++++++++++++++++++++++ src/bin/pg_rewind/pg_rewind.c | 23 +++++- src/bin/pg_rewind/pg_rewind.h | 2 + 6 files changed, 166 insertions(+), 3 deletions(-) diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile index 04f3b8f520..3b7f1ce177 100644 --- a/src/bin/pg_rewind/Makefile +++ b/src/bin/pg_rewind/Makefile @@ -16,7 +16,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global override CPPFLAGS := -I$(libpq_srcdir) -DFRONTEND $(CPPFLAGS) -LDFLAGS_INTERNAL += $(libpq_pgport) +LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport) OBJS = pg_rewind.o parsexlog.o xlogreader.o datapagemap.o timeline.o \ fetch.o file_ops.o copy_fetch.o libpq_fetch.o filemap.o logging.o \ diff --git a/src/bin/pg_rewind/fetch.h b/src/bin/pg_rewind/fetch.h index a694e8b157..7db9337ced 100644 --- a/src/bin/pg_rewind/fetch.h +++ b/src/bin/pg_rewind/fetch.h @@ -41,4 +41,7 @@ extern void copy_executeFileMap(filemap_t *map); typedef void (*process_file_callback_t) (const char *path, file_type_t type, size_t size, const char *link_target); extern void traverse_datadir(const char *datadir, process_file_callback_t callback); +extern void GenerateRecoveryConf(void); +extern void WriteRecoveryConf(void); + #endif /* FETCH_H */ diff --git a/src/bin/pg_rewind/libpq_fetch.c b/src/bin/pg_rewind/libpq_fetch.c index d06e277432..47a3f68c93 100644 --- a/src/bin/pg_rewind/libpq_fetch.c +++ b/src/bin/pg_rewind/libpq_fetch.c @@ -24,10 +24,15 @@ #include "libpq-fe.h" #include "catalog/pg_type_d.h" #include "fe_utils/connect.h" +#include "fe_utils/string_utils.h" #include "port/pg_bswap.h" +#include "pqexpbuffer.h" static PGconn *conn = NULL; +/* Contents of configuration to be generated */ +static PQExpBuffer recoveryconfcontents = NULL; + /* * Files are fetched max CHUNKSIZE bytes at a time. * @@ -526,3 +531,136 @@ execute_pagemap(datapagemap_t *pagemap, const char *path) } pg_free(iter); } + +/* + * Escape a string so that it can be used as a value in a key-value pair + * a configuration file. + */ +static char * +escape_quotes(const char *src) +{ + char *result = escape_single_quotes_ascii(src); + + if (!result) + { + fprintf(stderr, _("%s: out of memory\n"), progname); + exit(1); + } + return result; +} + +/* + * Create a configuration file in memory using a PQExpBuffer + */ +void +GenerateRecoveryConf(void) +{ + PQconninfoOption *connOptions; + PQconninfoOption *option; + PQExpBufferData conninfo_buf; + char *escaped; + + recoveryconfcontents = createPQExpBuffer(); + if (!recoveryconfcontents) + { + fprintf(stderr, _("%s: out of memory\n"), progname); + exit(1); + } + + connOptions = PQconninfo(conn); + if (connOptions == NULL) + { + fprintf(stderr, _("%s: out of memory\n"), progname); + exit(1); + } + + initPQExpBuffer(&conninfo_buf); + for (option = connOptions; option && option->keyword; option++) + { + /* + * Do not emit this setting if: - the setting is "replication", + * "dbname" or "fallback_application_name", since these would be + * overridden by the libpqwalreceiver module anyway. - not set or + * empty. + */ + if (strcmp(option->keyword, "replication") == 0 || + strcmp(option->keyword, "dbname") == 0 || + strcmp(option->keyword, "fallback_application_name") == 0 || + (option->val == NULL) || + (option->val != NULL && option->val[0] == '\0')) + continue; + + /* Separate key-value pairs with spaces */ + if (conninfo_buf.len != 0) + appendPQExpBufferChar(&conninfo_buf, ' '); + + /* + * Write "keyword=value" pieces, the value string is escaped and/or + * quoted if necessary. + */ + appendPQExpBuffer(&conninfo_buf, "%s=", option->keyword); + appendConnStrVal(&conninfo_buf, option->val); + } + + /* + * Escape the connection string, so that it can be put in the config file. + * Note that this is different from the escaping of individual connection + * options above! + */ + escaped = escape_quotes(conninfo_buf.data); + appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped); + free(escaped); + + if (PQExpBufferBroken(recoveryconfcontents) || + PQExpBufferDataBroken(conninfo_buf)) + { + fprintf(stderr, _("%s: out of memory\n"), progname); + exit(1); + } + + termPQExpBuffer(&conninfo_buf); + + PQconninfoFree(connOptions); +} + + +/* + * Write the configuration file into the directory specified in datadir_target, + * with the contents already collected in memory. + * Then write the signal file into the datadir_target also. + */ +void +WriteRecoveryConf(void) +{ + char filename[MAXPGPATH]; + FILE *cf; + + snprintf(filename, MAXPGPATH, "%s/%s", datadir_target, "postgresql.auto.conf"); + + cf = fopen(filename, "a"); + if (cf == NULL) + { + fprintf(stderr, _("%s: could not open file \"%s\": %s\n"), progname, filename, strerror(errno)); + exit(1); + } + + if (fwrite(recoveryconfcontents->data, recoveryconfcontents->len, 1, cf) != 1) + { + fprintf(stderr, + _("%s: could not write to file \"%s\": %s\n"), + progname, filename, strerror(errno)); + exit(1); + } + + fclose(cf); + + snprintf(filename, MAXPGPATH, "%s/%s", datadir_target, "standby.signal"); + cf = fopen(filename, "w"); + if (cf == NULL) + { + fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno)); + exit(1); + } + + fclose(cf); +} diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c index aa753bb315..b6b288bbfe 100644 --- a/src/bin/pg_rewind/pg_rewind.c +++ b/src/bin/pg_rewind/pg_rewind.c @@ -45,6 +45,8 @@ static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex); static ControlFileData ControlFile_target; static ControlFileData ControlFile_source; +static bool writerecoveryconf = false; + const char *progname; int WalSegSz; @@ -71,6 +73,7 @@ usage(const char *progname) printf(_(" -D, --target-pgdata=DIRECTORY existing data directory to modify\n")); printf(_(" --source-pgdata=DIRECTORY source data directory to synchronize with\n")); printf(_(" --source-server=CONNSTR source server to synchronize with\n")); + printf(_(" -R, --write-recovery-conf write configuration for replication\n")); printf(_(" -n, --dry-run stop before modifying anything\n")); printf(_(" -N, --no-sync do not wait for changes to be written\n")); printf(_(" safely to disk\n")); @@ -88,6 +91,7 @@ main(int argc, char **argv) static struct option long_options[] = { {"help", no_argument, NULL, '?'}, {"target-pgdata", required_argument, NULL, 'D'}, + {"write-recovery-conf", no_argument, NULL, 'R'}, {"source-pgdata", required_argument, NULL, 1}, {"source-server", required_argument, NULL, 2}, {"version", no_argument, NULL, 'V'}, @@ -129,7 +133,7 @@ main(int argc, char **argv) } } - while ((c = getopt_long(argc, argv, "D:nNP", long_options, &option_index)) != -1) + while ((c = getopt_long(argc, argv, "D:nNPR", long_options, &option_index)) != -1) { switch (c) { @@ -149,6 +153,10 @@ main(int argc, char **argv) do_sync = false; break; + case 'R': + writerecoveryconf = true; + break; + case 3: debug = true; break; @@ -291,6 +299,13 @@ main(int argc, char **argv) if (!rewind_needed) { printf(_("no rewind required\n")); + + if (writerecoveryconf && connstr_source) + { + GenerateRecoveryConf(); + WriteRecoveryConf(); + } + exit(0); } @@ -382,6 +397,12 @@ main(int argc, char **argv) pg_log(PG_PROGRESS, "syncing target data directory\n"); syncTargetDirectory(); + if (writerecoveryconf && connstr_source) + { + GenerateRecoveryConf(); + WriteRecoveryConf(); + } + printf(_("Done!\n")); return 0; diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h index 83b2898b8b..0ce9ba0426 100644 --- a/src/bin/pg_rewind/pg_rewind.h +++ b/src/bin/pg_rewind/pg_rewind.h @@ -30,6 +30,8 @@ extern int WalSegSz; extern TimeLineHistoryEntry *targetHistory; extern int targetNentries; +extern const char *progname; + /* in parsexlog.c */ extern void extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex, XLogRecPtr endpoint); -- 2.17.2