>From 6de7841715e446a9fc3726f3dd0e5ae37525e141 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Sat, 13 Jun 2015 16:29:26 +0200
Subject: [PATCH 2/4] Add pg_resetsysid utility

---
 doc/src/sgml/ref/allfiles.sgml       |   1 +
 doc/src/sgml/ref/pg_resetsysid.sgml  |  85 +++++++++++
 doc/src/sgml/reference.sgml          |   1 +
 src/bin/pg_resetxlog/.gitignore      |   1 +
 src/bin/pg_resetxlog/Makefile        |  10 +-
 src/bin/pg_resetxlog/nls.mk          |   2 +-
 src/bin/pg_resetxlog/pg_resetsysid.c | 265 +++++++++++++++++++++++++++++++++++
 7 files changed, 361 insertions(+), 4 deletions(-)
 create mode 100644 doc/src/sgml/ref/pg_resetsysid.sgml
 create mode 100644 src/bin/pg_resetxlog/pg_resetsysid.c

diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index bf95453..46c23f3 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -192,6 +192,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pgIsready          SYSTEM "pg_isready.sgml">
 <!ENTITY pgReceivexlog      SYSTEM "pg_receivexlog.sgml">
 <!ENTITY pgRecvlogical      SYSTEM "pg_recvlogical.sgml">
+<!ENTITY pgResetsysid       SYSTEM "pg_resetsysid.sgml">
 <!ENTITY pgResetxlog        SYSTEM "pg_resetxlog.sgml">
 <!ENTITY pgRestore          SYSTEM "pg_restore.sgml">
 <!ENTITY pgRewind           SYSTEM "pg_rewind.sgml">
diff --git a/doc/src/sgml/ref/pg_resetsysid.sgml b/doc/src/sgml/ref/pg_resetsysid.sgml
new file mode 100644
index 0000000..e64caad
--- /dev/null
+++ b/doc/src/sgml/ref/pg_resetsysid.sgml
@@ -0,0 +1,85 @@
+<!--
+doc/src/sgml/ref/pg_resetsysid.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="APP-PGRESETSYSID">
+ <indexterm zone="app-pgresetsysid">
+  <primary>pg_resetsysid</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle><application>pg_resetsysid</application></refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>Application</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pg_resetsysid</refname>
+  <refpurpose>reset the system identifier of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pg_resetsysid</command>
+   <arg choice="opt"><option>-f</option></arg>
+   <arg choice="opt"><option>-n</option></arg>
+   <arg choice="req"><arg choice="opt"><option>-D</option></arg> <replaceable class="parameter">datadir</replaceable></arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-APP-PGRESETSYSID-1">
+  <title>Description</title>
+  <para>
+   <command>pg_resetsysid</command> generates new system identifier
+   using same algorithm as <command>initdb</command> and sets it in the
+   <filename>pg_control</> file.
+  </para>
+
+  <para>
+   Note that <command>pg_resetsysid</command> will also clear the
+   write-ahead log (WAL) as existing WALs cannot be replayed after the
+   system identifier change.
+  </para>
+
+  <para>
+   The <option>-n</> (no operation) option instructs
+   <command>pg_resetsysid</command> to print the values reconstructed from
+   <filename>pg_control</> and values about to be changed, and then exit
+   without modifying anything. This is mainly a debugging tool, but can be
+   useful as a sanity check before allowing <command>pg_resetsysid</command>
+   to proceed for real.
+  </para>
+
+  <para>
+   The <option>-V</> and <option>--version</> options print
+   the <application>pg_resetsysid</application> version and exit.  The
+   options <option>-?</> and <option>--help</> show supported arguments,
+   and exit.
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   This command must not be used when the server is
+   running.  <command>pg_resetsysid</command> will refuse to start up if
+   it finds a server lock file in the data directory.  If the
+   server crashed then a lock file might have been left
+   behind; in that case you can remove the lock file to allow
+   <command>pg_resetsysid</command> to run.  But before you do
+   so, make doubly certain that there is no server process still alive.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="app-pgresetxlog"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 03020df..7e02a5e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -263,6 +263,7 @@
    &pgarchivecleanup;
    &pgControldata;
    &pgCtl;
+   &pgResetsysid;
    &pgResetxlog;
    &pgRewind;
    &pgtestfsync;
diff --git a/src/bin/pg_resetxlog/.gitignore b/src/bin/pg_resetxlog/.gitignore
index 6b84208..9a0e1b9 100644
--- a/src/bin/pg_resetxlog/.gitignore
+++ b/src/bin/pg_resetxlog/.gitignore
@@ -1 +1,2 @@
 /pg_resetxlog
+/pg_resetsysid
diff --git a/src/bin/pg_resetxlog/Makefile b/src/bin/pg_resetxlog/Makefile
index acaa3f7..37aa07c 100644
--- a/src/bin/pg_resetxlog/Makefile
+++ b/src/bin/pg_resetxlog/Makefile
@@ -17,19 +17,23 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS= common.o $(WIN32RES)
 
-all: pg_resetxlog
+all: pg_resetxlog pg_resetsysid
 
 pg_resetxlog: $(OBJS) pg_resetxlog.o | submake-libpgport
 	$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
 
+pg_resetsysid: $(OBJS) pg_resetsysid.o | submake-libpgport
+	$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+
 install: all installdirs
 	$(INSTALL_PROGRAM) pg_resetxlog$(X) '$(DESTDIR)$(bindir)/pg_resetxlog$(X)'
+	$(INSTALL_PROGRAM) pg_resetsysid$(X) '$(DESTDIR)$(bindir)/pg_resetsysid$(X)'
 
 installdirs:
 	$(MKDIR_P) '$(DESTDIR)$(bindir)'
 
 uninstall:
-	rm -f '$(DESTDIR)$(bindir)/pg_resetxlog$(X)'
+	rm -f '$(DESTDIR)$(bindir)/pg_resetxlog$(X)' '$(DESTDIR)$(bindir)/pg_resetsysid$(X)'
 
 clean distclean maintainer-clean:
-	rm -f pg_resetxlog$(X) $(OBJS) pg_resetxlog.o
+	rm -f pg_resetxlog$(X) $(OBJS) pg_resetxlog.o pg_resetsysid.o
diff --git a/src/bin/pg_resetxlog/nls.mk b/src/bin/pg_resetxlog/nls.mk
index b753ace..9064d30 100644
--- a/src/bin/pg_resetxlog/nls.mk
+++ b/src/bin/pg_resetxlog/nls.mk
@@ -1,4 +1,4 @@
 # src/bin/pg_resetxlog/nls.mk
 CATALOG_NAME     = pg_resetxlog
 AVAIL_LANGUAGES  = cs de es fr it ja pl pt_BR ru sv zh_CN
-GETTEXT_FILES    = common.c pg_resetxlog.c ../../common/restricted_token.c
+GETTEXT_FILES    = common.c pg_resetxlog.c pg_resetsysid.c ../../common/restricted_token.c
diff --git a/src/bin/pg_resetxlog/pg_resetsysid.c b/src/bin/pg_resetxlog/pg_resetsysid.c
new file mode 100644
index 0000000..dee1387
--- /dev/null
+++ b/src/bin/pg_resetxlog/pg_resetsysid.c
@@ -0,0 +1,265 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_resetsysid.c
+ *	  A utility to reset system identifier.
+ *
+ * Inner workings are shared with pg_resetxlog.c
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/bin/pg_resetxlog/pg_resetsysid.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "common.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "access/transam.h"
+#include "access/tuptoaster.h"
+#include "access/multixact.h"
+#include "catalog/catversion.h"
+#include "common/fe_memutils.h"
+#include "storage/large_object.h"
+#include "pg_getopt.h"
+
+const char *progname;
+static ControlFileData ControlFile;             /* pg_control values */
+static XLogSegNo newXlogSegNo;  /* new XLOG segment # */
+static bool guessed = false;    /* T if we had to guess at any values */
+static uint64 set_sysid = 0;
+
+static void PrintNewControlValues(void);
+static void usage(void);
+
+
+int
+main(int argc, char *argv[])
+{
+	int			c;
+	bool		force = false;
+	bool		noupdate = false;
+	char	   *DataDir = NULL;
+	int			fd;
+
+	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetsysid"));
+
+	progname = get_progname(argv[0]);
+
+	if (argc > 1)
+	{
+		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
+		{
+			usage();
+			exit(0);
+		}
+		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
+		{
+			puts("pg_resetsysid (PostgreSQL) " PG_VERSION);
+			exit(0);
+		}
+	}
+
+
+	while ((c = getopt(argc, argv, "D:fn")) != -1)
+	{
+		switch (c)
+		{
+			case 'D':
+				DataDir = optarg;
+				break;
+
+			case 'f':
+				force = true;
+				break;
+
+			case 'n':
+				noupdate = true;
+				break;
+
+			default:
+				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				exit(1);
+		}
+	}
+
+	if (DataDir == NULL && optind < argc)
+		DataDir = argv[optind++];
+
+	/* Complain if any arguments remain */
+	if (optind < argc)
+	{
+		fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
+				progname, argv[optind]);
+		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
+				progname);
+		exit(1);
+	}
+
+	if (DataDir == NULL)
+	{
+		fprintf(stderr, _("%s: no data directory specified\n"), progname);
+		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		exit(1);
+	}
+
+	/*
+	 * Don't allow pg_resetsysid to be run as root, to avoid overwriting the
+	 * ownership of files in the data directory. We need only check for root
+	 * -- any other user won't have sufficient permissions to modify files in
+	 * the data directory.
+	 */
+#ifndef WIN32
+	if (geteuid() == 0)
+	{
+		fprintf(stderr, _("%s: cannot be executed by \"root\"\n"),
+				progname);
+		fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
+				progname);
+		exit(1);
+	}
+#endif
+
+	if (chdir(DataDir) < 0)
+	{
+		fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"),
+				progname, DataDir, strerror(errno));
+		exit(1);
+	}
+
+	/*
+	 * Check for a postmaster lock file --- if there is one, refuse to
+	 * proceed, on grounds we might be interfering with a live installation.
+	 */
+	if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
+	{
+		if (errno != ENOENT)
+		{
+			fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+					progname, "postmaster.pid", strerror(errno));
+			exit(1);
+		}
+	}
+	else
+	{
+		fprintf(stderr, _("%s: lock file \"%s\" exists\n"
+						  "Is a server running?  If not, delete the lock file and try again.\n"),
+				progname, "postmaster.pid");
+		exit(1);
+	}
+
+	/*
+	 * Generate new system identifier.
+	 */
+	set_sysid = GenerateSystemIdentifier();
+
+	/*
+	 * Attempt to read the existing pg_control file
+	 */
+	if (!ReadControlFile(&ControlFile, &guessed))
+		GuessControlValues(&ControlFile);
+
+	/*
+	 * Also look at existing segment files to set up newXlogSegNo
+	 */
+	newXlogSegNo = FindEndOfXLOG(&ControlFile);
+
+	/*
+	 * If we're not going to proceed with the reset, print the current control
+	 * file parameters.
+	 */
+	if ((guessed && !force) || noupdate)
+		PrintControlValues(&ControlFile, guessed);
+
+	ControlFile.system_identifier = set_sysid;
+
+	/*
+	 * If we had to guess anything, and -f was not given, just print the
+	 * guessed values and exit.  Also print if -n is given.
+	 */
+	if ((guessed && !force) || noupdate)
+	{
+		PrintNewControlValues();
+		if (!noupdate)
+		{
+			printf(_("\nIf these values seem acceptable, use -f to force reset.\n"));
+			exit(1);
+		}
+		else
+			exit(0);
+	}
+
+	/*
+	 * Don't reset from a dirty pg_control without -f, either.
+	 */
+	if (ControlFile.state != DB_SHUTDOWNED && !force)
+	{
+		printf(_("The database server was not shut down cleanly.\n"
+			   "Resetting the transaction log might cause data to be lost.\n"
+			   "If you want to proceed anyway, use -f to force reset.\n"));
+		exit(1);
+	}
+
+	/*
+	 * Else, do the dirty deed.
+	 */
+	XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD,
+							ControlFile.checkPointCopy.redo);
+	RewriteControlFile(&ControlFile);
+	KillExistingXLOG();
+	KillExistingArchiveStatus();
+	WriteEmptyXLOG(&ControlFile);
+
+	printf(_("System identifier and transaction log reset\n"));
+	return 0;
+}
+
+
+/*
+ * Print the values to be changed.
+ */
+static void
+PrintNewControlValues(void)
+{
+	char		fname[MAXFNAMELEN];
+	char		sysident_str[32];
+
+	/* This will be always printed in order to keep format same. */
+	printf(_("\n\nValues to be changed:\n\n"));
+
+	XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
+	printf(_("First log segment after reset:        %s\n"), fname);
+
+
+	/*
+	 * Format system_identifier separately to keep platform-dependent format
+	 * code out of the translatable message string.
+	 */
+	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
+		 ControlFile.system_identifier);
+
+	printf(_("Database system identifier:           %s\n"),
+		   sysident_str);
+}
+
+
+static void
+usage(void)
+{
+	printf(_("%s generates new system identifier for the PostgreSQL instance.\n\n"), progname);
+	printf(_("Usage:\n  %s [OPTION]... {[-D] DATADIR}\n\n"), progname);
+	printf(_("Options:\n"));
+	printf(_("  -f               force update to be done\n"));
+	printf(_("  -n               no update, just show what would be done (for testing)\n"));
+	printf(_("  -V, --version    output version information, then exit\n"));
+	printf(_("  -?, --help       show this help, then exit\n"));
+	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+}
-- 
1.9.1

