From: Antonin Houska Date: Fri, 5 Jul 2019 16:24:02 +0200 Subject: [PATCH 16/17] Teach the remaining frontend application about encryption. pg_checksums, pg_rewind, pg_waldump --- src/bin/pg_checksums/Makefile | 9 +- src/bin/pg_checksums/pg_checksums.c | 158 +++++++++++++++++++++++++++++++++--- src/bin/pg_rewind/Makefile | 9 +- src/bin/pg_rewind/parsexlog.c | 9 ++ src/bin/pg_rewind/pg_rewind.c | 57 ++++++++++++- src/bin/pg_waldump/Makefile | 9 +- src/bin/pg_waldump/pg_waldump.c | 90 +++++++++++++++++--- 7 files changed, 309 insertions(+), 32 deletions(-) diff --git a/src/bin/pg_checksums/Makefile b/src/bin/pg_checksums/Makefile index 278b7a0f2e..74e66ea0b7 100644 --- a/src/bin/pg_checksums/Makefile +++ b/src/bin/pg_checksums/Makefile @@ -15,13 +15,19 @@ subdir = src/bin/pg_checksums top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -OBJS= pg_checksums.o $(WIN32RES) +OBJS= encryption.o pg_checksums.o $(WIN32RES) + +override CPPFLAGS := -DFRONTEND $(CPPFLAGS) +LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport) all: pg_checksums pg_checksums: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) +encryption.c: % : $(top_srcdir)/src/backend/storage/file/% + rm -f $@ && $(LN_S) $< . + install: all installdirs $(INSTALL_PROGRAM) pg_checksums$(X) '$(DESTDIR)$(bindir)/pg_checksums$(X)' @@ -34,6 +40,7 @@ uninstall: clean distclean maintainer-clean: rm -f pg_checksums$(X) $(OBJS) rm -rf tmp_check + rm -f encryption.c check: $(prove_check) diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c index b591fcc864..587132faf9 100644 --- a/src/bin/pg_checksums/pg_checksums.c +++ b/src/bin/pg_checksums/pg_checksums.c @@ -20,15 +20,18 @@ #include #include "access/xlog_internal.h" +#include "catalog/pg_tablespace_d.h" #include "common/controldata_utils.h" #include "common/file_perm.h" #include "common/file_utils.h" #include "common/logging.h" +#include "fe_utils/encryption.h" #include "getopt_long.h" #include "pg_getopt.h" #include "storage/bufpage.h" #include "storage/checksum.h" #include "storage/checksum_impl.h" +#include "storage/encryption.h" static int64 files = 0; @@ -80,6 +83,10 @@ usage(void) printf(_(" -c, --check check data checksums (default)\n")); printf(_(" -d, --disable disable data checksums\n")); printf(_(" -e, --enable enable data checksums\n")); +#ifdef USE_ENCRYPTION + printf(_(" -K, --encryption-key-command=COMMAND\n" + " command that returns encryption key\n")); +#endif /* USE_ENCRYPTION */ printf(_(" -f, --filenode=FILENODE check only relation with specified filenode\n")); printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n")); printf(_(" -P, --progress show progress information\n")); @@ -167,7 +174,8 @@ skipfile(const char *fn) } static void -scan_file(const char *fn, BlockNumber segmentno) +scan_file(const char *fn, Oid relnode, BlockNumber segmentno, + ForkNumber forkno, Oid tbspace, Oid db) { PGAlignedBlock buf; PageHeader header = (PageHeader) buf.data; @@ -193,6 +201,7 @@ scan_file(const char *fn, BlockNumber segmentno) { uint16 csum; int r = read(f, buf.data, BLCKSZ); + char tweak[TWEAK_SIZE]; if (r == 0) break; @@ -204,6 +213,18 @@ scan_file(const char *fn, BlockNumber segmentno) } blocks++; + if (data_encrypted) + { + RelFileNode node; + + node.spcNode = tbspace; + node.dbNode = db; + node.relNode = relnode; + + mdtweak(tweak, &node, forkno, blockno); + decrypt_block(buf.data, buf.data, BLCKSZ, tweak, false); + } + /* New pages have no checksum yet */ if (PageIsNew(header)) continue; @@ -232,6 +253,9 @@ scan_file(const char *fn, BlockNumber segmentno) exit(1); } + if (data_encrypted) + encrypt_block(buf.data, buf.data, BLCKSZ, tweak, false); + /* Write block with checksum */ if (write(f, buf.data, BLCKSZ) != BLCKSZ) { @@ -262,9 +286,13 @@ scan_file(const char *fn, BlockNumber segmentno) * all the items which have checksums is computed and returned back * to the caller without operating on the files. This is used to compile * the total size of the data directory for progress reports. + * + * If db is a valid pointer, *db contains database OID. If it's NULL, the + * database OID needs to be recognized, possibly by recursive call. */ static int64 -scan_directory(const char *basedir, const char *subdir, bool sizeonly) +scan_directory(const char *basedir, const char *subdir, bool sizeonly, + Oid tbspace, Oid *db) { int64 dirsize = 0; char path[MAXPGPATH]; @@ -282,6 +310,9 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly) { char fn[MAXPGPATH]; struct stat st; + bool tbsp_identified = false; + Oid database; + bool db_identified = false; if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) @@ -305,12 +336,58 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly) pg_log_error("could not stat file \"%s\": %m", fn); exit(1); } + + if (tbspace != InvalidOid && db == NULL) + { + /* + * If tablespace is passed by caller or identified by upper call + * of this function, and if caller could not identify the database + * OID, try to do it now. + */ + if (strcmp(de->d_name, TABLESPACE_VERSION_DIRECTORY) == 0) + { + /* + * Major-version-specific tablespace subdirectory needs no + * special attention, we'll recurse into it below. + */ + } + else + { + errno = 0; + database = strtol(de->d_name, NULL, 10); + if (errno != 0 || database == InvalidOid) + { + fprintf(stderr, _("%s: invalid database oid \"%s\"\n"), + progname, de->d_name); + exit(1); + } + db_identified = true; + } + } + else if (tbspace == InvalidOid) + { + /* + * This entry should be a direct subdirectory of pg_tblspc, so the + * name is supposedly tablespace oid. + */ + errno = 0; + tbspace = strtol(de->d_name, NULL, 10); + if (errno != 0 || tbspace == InvalidOid) + { + fprintf(stderr, _("%s: invalid tablespace oid \"%s\"\n"), + progname, de->d_name); + exit(1); + } + tbsp_identified = true; + } + if (S_ISREG(st.st_mode)) { char fnonly[MAXPGPATH]; char *forkpath, *segmentpath; BlockNumber segmentno = 0; + ForkNumber forkno; if (skipfile(de->d_name)) continue; @@ -337,10 +414,16 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly) forkpath = strchr(fnonly, '_'); if (forkpath != NULL) + { *forkpath++ = '\0'; + forkno = forkname_to_number(forkpath); + } + else + forkno = MAIN_FORKNUM; + if (only_filenode && strcmp(only_filenode, fnonly) != 0) - /* filenode not to be included */ + /* Relfilenode not to be included */ continue; dirsize += st.st_size; @@ -350,14 +433,31 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly) * the items in the data folder. */ if (!sizeonly) - scan_file(fn, segmentno); + { + Oid relnode = atoi(fnonly); + + scan_file(fn, relnode, segmentno, forkno, tbspace, *db); + } } #ifndef WIN32 else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) #else else if (S_ISDIR(st.st_mode) || pgwin32_is_junction(fn)) #endif - dirsize += scan_directory(path, de->d_name, sizeonly); + + /* + * If database OID is not passed by caller, use the one we + * identified ourselves. + */ + dirsize = scan_directory(path, de->d_name, sizeonly, tbspace, + db_identified ? &database : db); + + /* + * If tablespace is not passed by caller, forget what we found out so + * that next directory entry is examined in the same way. + */ + if (tbsp_identified) + tbspace = InvalidOid; } closedir(dir); return dirsize; @@ -371,6 +471,9 @@ main(int argc, char *argv[]) {"pgdata", required_argument, NULL, 'D'}, {"disable", no_argument, NULL, 'd'}, {"enable", no_argument, NULL, 'e'}, +#ifdef USE_ENCRYPTION + {"encryption-key-command", required_argument, NULL, 'K'}, +#endif /* USE_ENCRYPTION */ {"filenode", required_argument, NULL, 'f'}, {"no-sync", no_argument, NULL, 'N'}, {"progress", no_argument, NULL, 'P'}, @@ -401,7 +504,7 @@ main(int argc, char *argv[]) } } - while ((c = getopt_long(argc, argv, "cD:deNPf:v", long_options, &option_index)) != -1) + while ((c = getopt_long(argc, argv, "cD:deNK:Pf:v", long_options, &option_index)) != -1) { switch (c) { @@ -422,6 +525,12 @@ main(int argc, char *argv[]) } only_filenode = pstrdup(optarg); break; +#ifdef USE_ENCRYPTION + case 'K': + encryption_key_command = pg_strdup(optarg); + data_encrypted = true; + break; +#endif /* USE_ENCRYPTION */ case 'N': do_sync = false; break; @@ -483,6 +592,24 @@ main(int argc, char *argv[]) exit(1); } + if (ControlFile->data_cipher > PG_CIPHER_NONE) + { + if (encryption_key_command == NULL) + { +#ifdef USE_ENCRYPTION + fprintf(stderr, _("%s: please specify command to retrieve encryption key\n"), + progname); +#else + fprintf(stderr, _("%s: compile postgres with --with-openssl to use encryption\n"), + progname); +#endif /* USE_ENCRYPTION */ + exit(1); + } + + run_encryption_key_command(encryption_key, DataDir); + setup_encryption(); + } + if (ControlFile->pg_control_version != PG_CONTROL_VERSION) { pg_log_error("cluster is not compatible with this version of pg_checksums"); @@ -533,6 +660,8 @@ main(int argc, char *argv[]) /* Operate on all files if checking or enabling checksums */ if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE) { + Oid db_shared = 0; + /* * If progress status information is requested, we need to scan the * directory tree twice: once to know how much total data needs to be @@ -540,14 +669,19 @@ main(int argc, char *argv[]) */ if (showprogress) { - total_size = scan_directory(DataDir, "global", true); - total_size += scan_directory(DataDir, "base", true); - total_size += scan_directory(DataDir, "pg_tblspc", true); + total_size = scan_directory(DataDir, "global", true, + GLOBALTABLESPACE_OID, &db_shared); + total_size += scan_directory(DataDir, "base", true, + DEFAULTTABLESPACE_OID, NULL); + total_size += scan_directory(DataDir, "pg_tblspc", true, + InvalidOid, NULL); } - (void) scan_directory(DataDir, "global", false); - (void) scan_directory(DataDir, "base", false); - (void) scan_directory(DataDir, "pg_tblspc", false); + (void) scan_directory(DataDir, "global", false, GLOBALTABLESPACE_OID, + &db_shared); + (void) scan_directory(DataDir, "base", false, DEFAULTTABLESPACE_OID, + NULL); + (void) scan_directory(DataDir, "pg_tblspc", false, InvalidOid, NULL); if (showprogress) { diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile index 859d3abc41..e41d2035f4 100644 --- a/src/bin/pg_rewind/Makefile +++ b/src/bin/pg_rewind/Makefile @@ -16,10 +16,10 @@ 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 \ + fetch.o file_ops.o copy_fetch.o libpq_fetch.o filemap.o encryption.o\ $(WIN32RES) EXTRA_CLEAN = xlogreader.c @@ -32,6 +32,9 @@ pg_rewind: $(OBJS) | submake-libpq submake-libpgport xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/% rm -f $@ && $(LN_S) $< . +encryption.c: % : $(top_srcdir)/src/backend/storage/file/% + rm -f $@ && $(LN_S) $< . + install: all installdirs $(INSTALL_PROGRAM) pg_rewind$(X) '$(DESTDIR)$(bindir)/pg_rewind$(X)' @@ -42,7 +45,7 @@ uninstall: rm -f '$(DESTDIR)$(bindir)/pg_rewind$(X)' clean distclean maintainer-clean: - rm -f pg_rewind$(X) $(OBJS) xlogreader.c + rm -f pg_rewind$(X) $(OBJS) xlogreader.c encryption.c rm -rf tmp_check check: diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c index 287af60c4e..0624c4c5ef 100644 --- a/src/bin/pg_rewind/parsexlog.c +++ b/src/bin/pg_rewind/parsexlog.c @@ -320,6 +320,15 @@ SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, return -1; } + if (data_encrypted) + { + char tweak[TWEAK_SIZE]; + + XLogEncryptionTweak(tweak, targetHistory[private->tliIndex].tli, + xlogreadsegno, targetPageOff); + decrypt_block(readBuf, readBuf, XLOG_BLCKSZ, tweak, true); + } + Assert(targetSegNo == xlogreadsegno); *pageTLI = targetHistory[private->tliIndex].tli; diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c index d378053de4..58c192adad 100644 --- a/src/bin/pg_rewind/pg_rewind.c +++ b/src/bin/pg_rewind/pg_rewind.c @@ -27,8 +27,10 @@ #include "common/file_perm.h" #include "common/file_utils.h" #include "common/restricted_token.h" +#include "fe_utils/encryption.h" #include "getopt_long.h" #include "storage/bufpage.h" +#include "storage/encryption.h" static void usage(const char *progname); @@ -75,6 +77,10 @@ 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")); +#ifdef USE_ENCRYPTION + printf(_(" -K, --encryption-key-command=COMMAND\n" + " command that returns encryption key\n")); +#endif /* USE_ENCRYPTION */ printf(_(" -n, --dry-run stop before modifying anything\n")); printf(_(" -N, --no-sync do not wait for changes to be written\n" " safely to disk\n")); @@ -99,6 +105,9 @@ main(int argc, char **argv) {"no-sync", no_argument, NULL, 'N'}, {"progress", no_argument, NULL, 'P'}, {"debug", no_argument, NULL, 3}, +#ifdef USE_ENCRYPTION + {"encryption-key-command", required_argument, NULL, 'K'}, +#endif /* USE_ENCRYPTION */ {NULL, 0, NULL, 0} }; int option_index; @@ -134,7 +143,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:K:nNP", long_options, &option_index)) != -1) { switch (c) { @@ -169,6 +178,12 @@ main(int argc, char **argv) case 2: /* --source-server */ connstr_source = pg_strdup(optarg); break; +#ifdef USE_ENCRYPTION + case 4: /* --encryption-key-command */ + case 'K': + encryption_key_command = strdup(optarg); + break; +#endif /* USE_ENCRYPTION */ } } @@ -248,6 +263,28 @@ main(int argc, char **argv) sanityChecks(); /* + * Setup encryption if it's obvious that we'll have to deal with encrypted + * XLOG. + */ + if (ControlFile_target.data_cipher > PG_CIPHER_NONE) + { + if (encryption_key_command == NULL) + { + pg_log_error("-K option must be passed for encrypted cluster"); + exit(EXIT_FAILURE); + } + + /* + * It should not matter whether we pass the source or target data + * directory. It should have been checked earlier that both clusters + * are encrypted using the same key. + */ + run_encryption_key_command(encryption_key, datadir_source); + setup_encryption(); + data_encrypted = true; + } + + /* * If both clusters are already on the same timeline, there's nothing to * do. */ @@ -446,6 +483,24 @@ sanityChecks(void) ControlFile_source.state != DB_SHUTDOWNED && ControlFile_source.state != DB_SHUTDOWNED_IN_RECOVERY) pg_fatal("source data directory must be shut down cleanly"); + + /* + * Since slave receives XLOG stream encrypted by master, handling + * differently encrypted clusters is not the typical use case for + * pg_rewind. Yet we should check the encryption. + */ + if (ControlFile_source.data_cipher > PG_CIPHER_NONE || + ControlFile_target.data_cipher > PG_CIPHER_NONE) + { + if (ControlFile_source.data_cipher != + ControlFile_target.data_cipher) + pg_fatal("source and target server must be both unencrypted or both encrypted\n"); + + if (memcmp(ControlFile_source.encryption_verification, + ControlFile_target.encryption_verification, + ENCRYPTION_SAMPLE_SIZE)) + pg_fatal("both source and target server must use the same encryption key"); + } } /* diff --git a/src/bin/pg_waldump/Makefile b/src/bin/pg_waldump/Makefile index 135979cef3..9eb1c2f01d 100644 --- a/src/bin/pg_waldump/Makefile +++ b/src/bin/pg_waldump/Makefile @@ -7,10 +7,11 @@ subdir = src/bin/pg_waldump top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -OBJS = pg_waldump.o compat.o xlogreader.o rmgrdesc.o \ +OBJS = pg_waldump.o compat.o xlogreader.o rmgrdesc.o encryption.o \ $(RMGRDESCOBJS) $(WIN32RES) override CPPFLAGS := -DFRONTEND $(CPPFLAGS) +LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport) RMGRDESCSOURCES = $(sort $(notdir $(wildcard $(top_srcdir)/src/backend/access/rmgrdesc/*desc.c))) RMGRDESCOBJS = $(patsubst %.c,%.o,$(RMGRDESCSOURCES)) @@ -27,6 +28,9 @@ xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/% $(RMGRDESCSOURCES): % : $(top_srcdir)/src/backend/access/rmgrdesc/% rm -f $@ && $(LN_S) $< . +encryption.c: % : $(top_srcdir)/src/backend/storage/file/% + rm -f $@ && $(LN_S) $< . + install: all installdirs $(INSTALL_PROGRAM) pg_waldump$(X) '$(DESTDIR)$(bindir)/pg_waldump$(X)' @@ -37,7 +41,8 @@ uninstall: rm -f '$(DESTDIR)$(bindir)/pg_waldump$(X)' clean distclean maintainer-clean: - rm -f pg_waldump$(X) $(OBJS) $(RMGRDESCSOURCES) xlogreader.c + rm -f pg_waldump$(X) $(OBJS) $(RMGRDESCSOURCES) xlogreader.c \ +encryption.c rm -rf tmp_check check: diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c index b95d467805..4df84efb01 100644 --- a/src/bin/pg_waldump/pg_waldump.c +++ b/src/bin/pg_waldump/pg_waldump.c @@ -22,6 +22,8 @@ #include "access/transam.h" #include "common/fe_memutils.h" #include "common/logging.h" +#include "fe_utils/encryption.h" +#include "storage/encryption.h" #include "getopt_long.h" #include "rmgrdesc.h" @@ -194,27 +196,59 @@ search_directory(const char *directory, const char *fname) PGAlignedXLogBlock buf; int r; - r = read(fd, buf.data, XLOG_BLCKSZ); - if (r == XLOG_BLCKSZ) + if (data_encrypted) { - XLogLongPageHeader longhdr = (XLogLongPageHeader) buf.data; - - WalSegSz = longhdr->xlp_seg_size; + /* + * Segment size affects calculation of segNo and thus also the + * encryption tweak, so we cannot get the size from the header + * until the page is decrypted. Here we need to take more + * expensive approach and really check the file size. + */ + WalSegSz = (int) lseek(fd, 0, SEEK_END); + if (WalSegSz <= 0) + fatal_error("Could not determine size of WAL segment \"%s\"", fname); + /* + * Verification of the file size is the only useful thing we can + * do. If anything else is wrong, the XLOG reader should find out + * after decryption. + */ if (!IsValidWalSegSize(WalSegSz)) - fatal_error(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d byte", - "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d bytes", + fatal_error(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but size of the WAL file \"%s\" is %d byte", + "WAL segment size must be a power of two between 1 MB and 1 GB, but size of the WAL file \"%s\" is %d bytes", WalSegSz), fname, WalSegSz); } else { - if (errno != 0) - fatal_error("could not read file \"%s\": %s", - fname, strerror(errno)); + r = read(fd, buf.data, XLOG_BLCKSZ); + if (r == XLOG_BLCKSZ) + { + XLogLongPageHeader longhdr; + + longhdr = (XLogLongPageHeader) buf.data; + + if (longhdr->std.xlp_magic != XLOG_PAGE_MAGIC) + fatal_error(gettext("WAL segment \"%s\" has an incorrect magic number. If it's encrypted, use the -K option to pass encryption credentials"), + fname); + + WalSegSz = longhdr->xlp_seg_size; + + if (!IsValidWalSegSize(WalSegSz)) + fatal_error(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d byte", + "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d bytes", + WalSegSz), + fname, WalSegSz); + } else - fatal_error("could not read file \"%s\": read %d of %zu", - fname, r, (Size) XLOG_BLCKSZ); + { + if (errno != 0) + fatal_error("could not read file \"%s\": %s", + fname, strerror(errno)); + else + fatal_error("could not read file \"%s\": read %d of %zu", + fname, r, (Size) XLOG_BLCKSZ); + } } close(fd); return true; @@ -444,6 +478,19 @@ XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogDumpXLogRead(private->inpath, private->timeline, targetPagePtr, readBuff, count); + if (data_encrypted) + { + char tweak[TWEAK_SIZE]; + XLogSegNo readSegNo; + uint32 readSegOff; + + XLByteToSeg(targetPagePtr, readSegNo, WalSegSz); + readSegOff = targetPagePtr % WalSegSz; + + XLogEncryptionTweak(tweak, private->timeline, readSegNo, readSegOff); + decrypt_block(readBuff, readBuff, count, tweak, true); + } + return count; } @@ -792,6 +839,10 @@ usage(void) printf(_(" -b, --bkp-details output detailed information about backup blocks\n")); printf(_(" -e, --end=RECPTR stop reading at WAL location RECPTR\n")); printf(_(" -f, --follow keep retrying after reaching end of WAL\n")); +#ifdef USE_ENCRYPTION + printf(_(" -K, --encryption-key-command=COMMAND\n" + " command that returns encryption key\n")); +#endif /* USE_ENCRYPTION */ printf(_(" -n, --limit=N number of records to display\n")); printf(_(" -p, --path=PATH directory in which to find log segment files or a\n" " directory with a ./pg_wal that contains such files\n" @@ -827,6 +878,7 @@ main(int argc, char **argv) {"end", required_argument, NULL, 'e'}, {"follow", no_argument, NULL, 'f'}, {"help", no_argument, NULL, '?'}, + {"encryption-key-command", required_argument, NULL, 'K'}, {"limit", required_argument, NULL, 'n'}, {"path", required_argument, NULL, 'p'}, {"rmgr", required_argument, NULL, 'r'}, @@ -884,7 +936,7 @@ main(int argc, char **argv) goto bad_argument; } - while ((option = getopt_long(argc, argv, "be:fn:p:r:s:t:x:z", + while ((option = getopt_long(argc, argv, "be:fK:n:p:r:s:t:x:z", long_options, &optindex)) != -1) { switch (option) @@ -904,6 +956,12 @@ main(int argc, char **argv) case 'f': config.follow = true; break; +#ifdef USE_ENCRYPTION + case 'K': + encryption_key_command = pg_strdup(optarg); + data_encrypted = true; + break; +#endif /* USE_ENCRYPTION */ case 'n': if (sscanf(optarg, "%d", &config.stop_after_records) != 1) { @@ -1099,6 +1157,12 @@ main(int argc, char **argv) /* done with argument parsing, do the actual work */ + if (data_encrypted) + { + run_encryption_key_command(encryption_key, NULL); + setup_encryption(); + } + /* we have everything we need, start reading */ xlogreader_state = XLogReaderAllocate(WalSegSz, XLogDumpReadPage, &private); -- 2.13.7 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v04-0017-User-and-development-documentation.patch