pg_ctl: Detect current standby state from pg_control
authorPeter Eisentraut <[email protected]>
Tue, 26 Jul 2016 15:23:43 +0000 (11:23 -0400)
committerPeter Eisentraut <[email protected]>
Wed, 21 Sep 2016 16:00:00 +0000 (12:00 -0400)
pg_ctl used to determine whether a server was in standby mode by looking
for a recovery.conf file.  With this change, it instead looks into
pg_control, which is potentially more accurate.  There are also
occasional discussions about removing recovery.conf, so this removes one
dependency.

Reviewed-by: Michael Paquier <[email protected]>
src/backend/utils/misc/pg_controldata.c
src/bin/pg_controldata/pg_controldata.c
src/bin/pg_ctl/pg_ctl.c
src/common/controldata_utils.c
src/include/common/controldata_utils.h

index 34ee76a237ed9ef9e618f88f1bc669337d942954..4f9de830974456adaafc7b5cbe55455660c7c46f 100644 (file)
@@ -52,6 +52,9 @@ pg_control_system(PG_FUNCTION_ARGS)
 
    /* read the control file */
    ControlFile = get_controlfile(DataDir, NULL);
+   if (!ControlFile)
+       ereport(ERROR,
+               (errmsg("calculated CRC checksum does not match value stored in file")));
 
    values[0] = Int32GetDatum(ControlFile->pg_control_version);
    nulls[0] = false;
@@ -128,6 +131,9 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 
    /* Read the control file. */
    ControlFile = get_controlfile(DataDir, NULL);
+   if (!ControlFile)
+       ereport(ERROR,
+               (errmsg("calculated CRC checksum does not match value stored in file")));
 
    /*
     * Calculate name of the WAL file containing the latest checkpoint's REDO
@@ -230,6 +236,9 @@ pg_control_recovery(PG_FUNCTION_ARGS)
 
    /* read the control file */
    ControlFile = get_controlfile(DataDir, NULL);
+   if (!ControlFile)
+       ereport(ERROR,
+               (errmsg("calculated CRC checksum does not match value stored in file")));
 
    values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
    nulls[0] = false;
@@ -295,6 +304,9 @@ pg_control_init(PG_FUNCTION_ARGS)
 
    /* read the control file */
    ControlFile = get_controlfile(DataDir, NULL);
+   if (!ControlFile)
+       ereport(ERROR,
+               (errmsg("calculated CRC checksum does not match value stored in file")));
 
    values[0] = Int32GetDatum(ControlFile->maxAlign);
    nulls[0] = false;
index 96619a20769f835343cf2a77ecf32fcea40b9be6..e92feabadef4a005701467eec4e860e913f69d11 100644 (file)
@@ -156,6 +156,10 @@ main(int argc, char *argv[])
 
    /* get a copy of the control file */
    ControlFile = get_controlfile(DataDir, progname);
+   if (!ControlFile)
+       printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
+                "Either the file is corrupt, or it has a different layout than this program\n"
+                "is expecting.  The results below are untrustworthy.\n\n"));
 
    /*
     * This slightly-chintzy coding will work as long as the control file
index efc07291adea715b0f39266da723c6cd47f1851d..eb8a67a9033e9d2fc65b186ab6be8a5ad402f195 100644 (file)
@@ -19,6 +19,8 @@
 
 #include "postgres_fe.h"
 
+#include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
 
@@ -96,7 +98,6 @@ static char postopts_file[MAXPGPATH];
 static char version_file[MAXPGPATH];
 static char pid_file[MAXPGPATH];
 static char backup_file[MAXPGPATH];
-static char recovery_file[MAXPGPATH];
 static char promote_file[MAXPGPATH];
 
 #ifdef WIN32
@@ -158,6 +159,8 @@ static bool postmaster_is_alive(pid_t pid);
 static void unlimit_core_size(void);
 #endif
 
+static DBState get_control_dbstate(void);
+
 
 #ifdef WIN32
 static void
@@ -988,12 +991,12 @@ do_stop(void)
        /*
         * If backup_label exists, an online backup is running. Warn the user
         * that smart shutdown will wait for it to finish. However, if
-        * recovery.conf is also present, we're recovering from an online
+        * the server is in archive recovery, we're recovering from an online
         * backup instead of performing one.
         */
        if (shutdown_mode == SMART_MODE &&
            stat(backup_file, &statbuf) == 0 &&
-           stat(recovery_file, &statbuf) != 0)
+           get_control_dbstate() != DB_IN_ARCHIVE_RECOVERY)
        {
            print_msg(_("WARNING: online backup mode is active\n"
                        "Shutdown will not complete until pg_stop_backup() is called.\n\n"));
@@ -1076,12 +1079,12 @@ do_restart(void)
        /*
         * If backup_label exists, an online backup is running. Warn the user
         * that smart shutdown will wait for it to finish. However, if
-        * recovery.conf is also present, we're recovering from an online
+        * the server is in archive recovery, we're recovering from an online
         * backup instead of performing one.
         */
        if (shutdown_mode == SMART_MODE &&
            stat(backup_file, &statbuf) == 0 &&
-           stat(recovery_file, &statbuf) != 0)
+           get_control_dbstate() != DB_IN_ARCHIVE_RECOVERY)
        {
            print_msg(_("WARNING: online backup mode is active\n"
                        "Shutdown will not complete until pg_stop_backup() is called.\n\n"));
@@ -1168,7 +1171,6 @@ do_promote(void)
 {
    FILE       *prmfile;
    pgpid_t     pid;
-   struct stat statbuf;
 
    pid = get_pgpid(false);
 
@@ -1187,8 +1189,7 @@ do_promote(void)
        exit(1);
    }
 
-   /* If recovery.conf doesn't exist, the server is not in standby mode */
-   if (stat(recovery_file, &statbuf) != 0)
+   if (get_control_dbstate() != DB_IN_ARCHIVE_RECOVERY)
    {
        write_stderr(_("%s: cannot promote server; "
                       "server is not in standby mode\n"),
@@ -2115,6 +2116,35 @@ adjust_data_dir(void)
 }
 
 
+static DBState
+get_control_dbstate(void)
+{
+   DBState ret;
+
+   for (;;)
+   {
+       ControlFileData *control_file_data = get_controlfile(pg_data, progname);
+
+       if (control_file_data)
+       {
+           ret = control_file_data->state;
+           pfree(control_file_data);
+           return ret;
+       }
+
+       if (wait_seconds > 0)
+       {
+           pg_usleep(1000000);     /* 1 sec */
+           wait_seconds--;
+           continue;
+       }
+
+       write_stderr(_("%s: control file appears to be corrupt\n"), progname);
+       exit(1);
+   }
+}
+
+
 int
 main(int argc, char **argv)
 {
@@ -2401,7 +2431,6 @@ main(int argc, char **argv)
        snprintf(version_file, MAXPGPATH, "%s/PG_VERSION", pg_data);
        snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
        snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
-       snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
    }
 
    switch (ctl_command)
index 5592fe7039c92bbf875b5e24d4b886f730fe2ba1..f218d2558c196ca79ee7854c96975a554b650fd6 100644 (file)
  *
  * Get controlfile values. The caller is responsible
  * for pfreeing the result.
+ *
+ * Returns NULL if the CRC did not match.
  */
 ControlFileData *
-get_controlfile(char *DataDir, const char *progname)
+get_controlfile(const char *DataDir, const char *progname)
 {
    ControlFileData *ControlFile;
    int         fd;
@@ -82,13 +84,10 @@ get_controlfile(char *DataDir, const char *progname)
    FIN_CRC32C(crc);
 
    if (!EQ_CRC32C(crc, ControlFile->crc))
-#ifndef FRONTEND
-       elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
-#else
-       printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
-                "Either the file is corrupt, or it has a different layout than this program\n"
-                "is expecting.  The results below are untrustworthy.\n\n"));
-#endif
+   {
+       pfree(ControlFile);
+       return NULL;
+   }
 
    /* Make sure the control file is valid byte order. */
    if (ControlFile->pg_control_version % 65536 == 0 &&
index a355d2252df7a0fab64ae968cd799d4f6c3e319c..f834624e4e70b952740936858e01395f8029c4dc 100644 (file)
@@ -12,6 +12,6 @@
 
 #include "catalog/pg_control.h"
 
-extern ControlFileData *get_controlfile(char *DataDir, const char *progname);
+extern ControlFileData *get_controlfile(const char *DataDir, const char *progname);
 
 #endif   /* COMMON_CONTROLDATA_UTILS_H */