]> granicus.if.org Git - postgresql/blobdiff - src/bin/pg_ctl/pg_ctl.c
Assorted message style improvements
[postgresql] / src / bin / pg_ctl / pg_ctl.c
index dc30f5de9f535d928cf3d83bc470133592a22db2..72fc4c1abf6ae80b92aafea727c9f07d36d9efb3 100644 (file)
@@ -2,7 +2,7 @@
  *
  * pg_ctl --- start/stops/restarts the PostgreSQL server
  *
- * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
  *
  * src/bin/pg_ctl/pg_ctl.c
  *
@@ -22,6 +22,7 @@
 
 #include <locale.h>
 #include <signal.h>
+#include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -63,6 +64,7 @@ typedef enum
        RESTART_COMMAND,
        RELOAD_COMMAND,
        STATUS_COMMAND,
+       PROMOTE_COMMAND,
        KILL_COMMAND,
        REGISTER_COMMAND,
        UNREGISTER_COMMAND,
@@ -79,6 +81,7 @@ static ShutdownMode shutdown_mode = SMART_MODE;
 static int     sig = SIGTERM;          /* default */
 static CtlCommand ctl_command = NO_COMMAND;
 static char *pg_data = NULL;
+static char *pg_config = NULL;
 static char *pgdata_opt = NULL;
 static char *post_opts = NULL;
 static const char *progname;
@@ -89,12 +92,31 @@ static char *register_username = NULL;
 static char *register_password = NULL;
 static char *argv0 = NULL;
 static bool allow_core_files = false;
+static time_t start_time;
+
+static char postopts_file[MAXPGPATH];
+static char pid_file[MAXPGPATH];
+static char backup_file[MAXPGPATH];
+static char recovery_file[MAXPGPATH];
+static char promote_file[MAXPGPATH];
+
+#if defined(WIN32) || defined(__CYGWIN__)
+static DWORD pgctl_start_type = SERVICE_AUTO_START;
+static SERVICE_STATUS status;
+static SERVICE_STATUS_HANDLE hStatus = (SERVICE_STATUS_HANDLE) 0;
+static HANDLE shutdownHandles[2];
+static pid_t postmasterPID = -1;
+
+#define shutdownEvent    shutdownHandles[0]
+#define postmasterProcess shutdownHandles[1]
+#endif
+
 
 static void
 write_stderr(const char *fmt,...)
 /* This extension allows gcc to check the format string for consistency with
    the supplied arguments. */
-__attribute__((format(printf, 1, 2)));
+__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
 static void *pg_malloc(size_t size);
 static char *xstrdup(const char *s);
 static void do_advice(void);
@@ -107,8 +129,10 @@ static void do_stop(void);
 static void do_restart(void);
 static void do_reload(void);
 static void do_status(void);
+static void do_promote(void);
 static void do_kill(pgpid_t pid);
 static void print_msg(const char *msg);
+static void adjust_data_dir(void);
 
 #if defined(WIN32) || defined(__CYGWIN__)
 static bool pgwin32_IsInstalled(SC_HANDLE);
@@ -120,15 +144,6 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
 static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
 static void pgwin32_doRunAsService(void);
 static int     CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
-
-static DWORD pgctl_start_type = SERVICE_AUTO_START;
-static SERVICE_STATUS status;
-static SERVICE_STATUS_HANDLE hStatus = (SERVICE_STATUS_HANDLE) 0;
-static HANDLE shutdownHandles[2];
-static pid_t postmasterPID = -1;
-
-#define shutdownEvent    shutdownHandles[0]
-#define postmasterProcess shutdownHandles[1]
 #endif
 
 static pgpid_t get_pgpid(void);
@@ -139,12 +154,6 @@ static void read_post_opts(void);
 static PGPing test_postmaster_connection(bool);
 static bool postmaster_is_alive(pid_t pid);
 
-static char postopts_file[MAXPGPATH];
-static char pid_file[MAXPGPATH];
-static char conf_file[MAXPGPATH];
-static char backup_file[MAXPGPATH];
-static char recovery_file[MAXPGPATH];
-
 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
 static void unlimit_core_size(void);
 #endif
@@ -156,6 +165,9 @@ write_eventlog(int level, const char *line)
 {
        static HANDLE evtHandle = INVALID_HANDLE_VALUE;
 
+       if (silent_mode && level == EVENTLOG_INFORMATION_TYPE)
+               return;
+
        if (evtHandle == INVALID_HANDLE_VALUE)
        {
                evtHandle = RegisterEventSource(NULL, "PostgreSQL");
@@ -359,6 +371,10 @@ start_postmaster(void)
        /*
         * Since there might be quotes to handle here, it is easier simply to pass
         * everything to a shell to process them.
+        *
+        * XXX it would be better to fork and exec so that we would know the child
+        * postmaster's PID directly; then test_postmaster_connection could use
+        * the PID without having to rely on reading it back from the pidfile.
         */
        if (log_file != NULL)
                snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &" SYSTEMQUOTE,
@@ -397,144 +413,217 @@ start_postmaster(void)
 
 /*
  * Find the pgport and try a connection
+ *
  * Note that the checkpoint parameter enables a Windows service control
  * manager checkpoint, it's got nothing to do with database checkpoints!!
  */
 static PGPing
 test_postmaster_connection(bool do_checkpoint)
 {
-       PGPing          ret = PQACCESS; /* assume success for zero wait */
+       PGPing          ret = PQPING_NO_RESPONSE;
+       bool            found_stale_pidfile = false;
+       pgpid_t         pm_pid = 0;
+       char            connstr[MAXPGPATH * 2 + 256];
        int                     i;
-       char            portstr[32];
-       char       *p;
-       char       *q;
-       char            connstr[128];   /* Should be way more than enough! */
 
-       *portstr = '\0';
+       /* if requested wait time is zero, return "still starting up" code */
+       if (wait_seconds <= 0)
+               return PQPING_REJECT;
 
-       /*
-        * Look in post_opts for a -p switch.
-        *
-        * This parsing code is not amazingly bright; it could for instance get
-        * fooled if ' -p' occurs within a quoted argument value.  Given that few
-        * people pass complicated settings in post_opts, it's probably good
-        * enough.
-        */
-       for (p = post_opts; *p;)
-       {
-               /* advance past whitespace */
-               while (isspace((unsigned char) *p))
-                       p++;
+       connstr[0] = '\0';
 
-               if (strncmp(p, "-p", 2) == 0)
-               {
-                       p += 2;
-                       /* advance past any whitespace/quoting */
-                       while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
-                               p++;
-                       /* find end of value (not including any ending quote!) */
-                       q = p;
-                       while (*q &&
-                                  !(isspace((unsigned char) *q) || *q == '\'' || *q == '"'))
-                               q++;
-                       /* and save the argument value */
-                       strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr)));
-                       /* keep looking, maybe there is another -p */
-                       p = q;
-               }
-               /* Advance to next whitespace */
-               while (*p && !isspace((unsigned char) *p))
-                       p++;
-       }
-
-       /*
-        * Search config file for a 'port' option.
-        *
-        * This parsing code isn't amazingly bright either, but it should be okay
-        * for valid port settings.
-        */
-       if (!*portstr)
+       for (i = 0; i < wait_seconds; i++)
        {
-               char      **optlines;
-
-               optlines = readfile(conf_file);
-               if (optlines != NULL)
+               /* Do we need a connection string? */
+               if (connstr[0] == '\0')
                {
-                       for (; *optlines != NULL; optlines++)
+                       /*----------
+                        * The number of lines in postmaster.pid tells us several things:
+                        *
+                        * # of lines
+                        *              0       lock file created but status not written
+                        *              2       pre-9.1 server, shared memory not created
+                        *              3       pre-9.1 server, shared memory created
+                        *              5       9.1+ server, ports not opened
+                        *              6       9.1+ server, shared memory not created
+                        *              7       9.1+ server, shared memory created
+                        *
+                        * This code does not support pre-9.1 servers.  On Unix machines
+                        * we could consider extracting the port number from the shmem
+                        * key, but that (a) is not robust, and (b) doesn't help with
+                        * finding out the socket directory.  And it wouldn't work anyway
+                        * on Windows.
+                        *
+                        * If we see less than 6 lines in postmaster.pid, just keep
+                        * waiting.
+                        *----------
+                        */
+                       char      **optlines;
+
+                       /* Try to read the postmaster.pid file */
+                       if ((optlines = readfile(pid_file)) != NULL &&
+                               optlines[0] != NULL &&
+                               optlines[1] != NULL &&
+                               optlines[2] != NULL)
                        {
-                               p = *optlines;
-
-                               while (isspace((unsigned char) *p))
-                                       p++;
-                               if (strncmp(p, "port", 4) != 0)
-                                       continue;
-                               p += 4;
-                               while (isspace((unsigned char) *p))
-                                       p++;
-                               if (*p != '=')
-                                       continue;
-                               p++;
-                               /* advance past any whitespace/quoting */
-                               while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
-                                       p++;
-                               /* find end of value (not including any ending quote/comment!) */
-                               q = p;
-                               while (*q &&
-                                          !(isspace((unsigned char) *q) ||
-                                                *q == '\'' || *q == '"' || *q == '#'))
-                                       q++;
-                               /* and save the argument value */
-                               strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr)));
-                               /* keep looking, maybe there is another */
+                               if (optlines[3] == NULL)
+                               {
+                                       /* File is exactly three lines, must be pre-9.1 */
+                                       write_stderr(_("\n%s: -w option is not supported when starting a pre-9.1 server\n"),
+                                                                progname);
+                                       return PQPING_NO_ATTEMPT;
+                               }
+                               else if (optlines[4] != NULL &&
+                                                optlines[5] != NULL)
+                               {
+                                       /* File is complete enough for us, parse it */
+                                       long            pmpid;
+                                       time_t          pmstart;
+
+                                       /*
+                                        * Make sanity checks.  If it's for a standalone backend
+                                        * (negative PID), or the recorded start time is before
+                                        * pg_ctl started, then either we are looking at the wrong
+                                        * data directory, or this is a pre-existing pidfile that
+                                        * hasn't (yet?) been overwritten by our child postmaster.
+                                        * Allow 2 seconds slop for possible cross-process clock
+                                        * skew.
+                                        */
+                                       pmpid = atol(optlines[LOCK_FILE_LINE_PID - 1]);
+                                       pmstart = atol(optlines[LOCK_FILE_LINE_START_TIME - 1]);
+                                       if (pmpid <= 0 || pmstart < start_time - 2)
+                                       {
+                                               /*
+                                                * Set flag to report stale pidfile if it doesn't get
+                                                * overwritten before we give up waiting.
+                                                */
+                                               found_stale_pidfile = true;
+                                       }
+                                       else
+                                       {
+                                               /*
+                                                * OK, seems to be a valid pidfile from our child.
+                                                */
+                                               int                     portnum;
+                                               char       *sockdir;
+                                               char       *hostaddr;
+                                               char            host_str[MAXPGPATH];
+
+                                               found_stale_pidfile = false;
+                                               pm_pid = (pgpid_t) pmpid;
+
+                                               /*
+                                                * Extract port number and host string to use. Prefer
+                                                * using Unix socket if available.
+                                                */
+                                               portnum = atoi(optlines[LOCK_FILE_LINE_PORT - 1]);
+                                               sockdir = optlines[LOCK_FILE_LINE_SOCKET_DIR - 1];
+                                               hostaddr = optlines[LOCK_FILE_LINE_LISTEN_ADDR - 1];
+
+                                               /*
+                                                * While unix_socket_directory can accept relative
+                                                * directories, libpq's host parameter must have a
+                                                * leading slash to indicate a socket directory.  So,
+                                                * ignore sockdir if it's relative, and try to use TCP
+                                                * instead.
+                                                */
+                                               if (sockdir[0] == '/')
+                                                       strlcpy(host_str, sockdir, sizeof(host_str));
+                                               else
+                                                       strlcpy(host_str, hostaddr, sizeof(host_str));
+
+                                               /* remove trailing newline */
+                                               if (strchr(host_str, '\n') != NULL)
+                                                       *strchr(host_str, '\n') = '\0';
+
+                                               /* Fail if couldn't get either sockdir or host addr */
+                                               if (host_str[0] == '\0')
+                                               {
+                                                       write_stderr(_("\n%s: -w option cannot use a relative socket directory specification\n"),
+                                                                                progname);
+                                                       return PQPING_NO_ATTEMPT;
+                                               }
+
+                                               /* If postmaster is listening on "*", use localhost */
+                                               if (strcmp(host_str, "*") == 0)
+                                                       strcpy(host_str, "localhost");
+
+                                               /*
+                                                * We need to set connect_timeout otherwise on Windows
+                                                * the Service Control Manager (SCM) will probably
+                                                * timeout first.
+                                                */
+                                               snprintf(connstr, sizeof(connstr),
+                                               "dbname=postgres port=%d host='%s' connect_timeout=5",
+                                                                portnum, host_str);
+                                       }
+                               }
                        }
                }
-       }
 
-       /* Check environment */
-       if (!*portstr && getenv("PGPORT") != NULL)
-               strlcpy(portstr, getenv("PGPORT"), sizeof(portstr));
+               /* If we have a connection string, ping the server */
+               if (connstr[0] != '\0')
+               {
+                       ret = PQping(connstr);
+                       if (ret == PQPING_OK || ret == PQPING_NO_ATTEMPT)
+                               break;
+               }
 
-       /* Else use compiled-in default */
-       if (!*portstr)
-               snprintf(portstr, sizeof(portstr), "%d", DEF_PGPORT);
+               /*
+                * The postmaster should create postmaster.pid very soon after being
+                * started.  If it's not there after we've waited 5 or more seconds,
+                * assume startup failed and give up waiting.  (Note this covers both
+                * cases where the pidfile was never created, and where it was created
+                * and then removed during postmaster exit.)  Also, if there *is* a
+                * file there but it appears stale, issue a suitable warning and give
+                * up waiting.
+                */
+               if (i >= 5)
+               {
+                       struct stat statbuf;
 
-       /*
-        * We need to set a connect timeout otherwise on Windows the SCM will
-        * probably timeout first
-        */
-       snprintf(connstr, sizeof(connstr),
-                        "dbname=postgres port=%s connect_timeout=5", portstr);
+                       if (stat(pid_file, &statbuf) != 0)
+                               return PQPING_NO_RESPONSE;
 
-       for (i = 0; i < wait_seconds; i++)
-       {
-               if ((ret = PQping(connstr)) != PQNORESPONSE)
-                       return ret;
-               else
-               {
-#if defined(WIN32)
-                       if (do_checkpoint)
+                       if (found_stale_pidfile)
                        {
-                               /*
-                                * Increment the wait hint by 6 secs (connection timeout +
-                                * sleep) We must do this to indicate to the SCM that our
-                                * startup time is changing, otherwise it'll usually send a
-                                * stop signal after 20 seconds, despite incrementing the
-                                * checkpoint counter.
-                                */
-                               status.dwWaitHint += 6000;
-                               status.dwCheckPoint++;
-                               SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
+                               write_stderr(_("\n%s: this data directory appears to be running a pre-existing postmaster\n"),
+                                                        progname);
+                               return PQPING_NO_RESPONSE;
                        }
+               }
 
-                       else
-#endif
-                               print_msg(".");
+               /*
+                * If we've been able to identify the child postmaster's PID, check
+                * the process is still alive.  This covers cases where the postmaster
+                * successfully created the pidfile but then crashed without removing
+                * it.
+                */
+               if (pm_pid > 0 && !postmaster_is_alive((pid_t) pm_pid))
+                       return PQPING_NO_RESPONSE;
 
-                       pg_usleep(1000000); /* 1 sec */
+               /* No response, or startup still in process; wait */
+#if defined(WIN32)
+               if (do_checkpoint)
+               {
+                       /*
+                        * Increment the wait hint by 6 secs (connection timeout + sleep)
+                        * We must do this to indicate to the SCM that our startup time is
+                        * changing, otherwise it'll usually send a stop signal after 20
+                        * seconds, despite incrementing the checkpoint counter.
+                        */
+                       status.dwWaitHint += 6000;
+                       status.dwCheckPoint++;
+                       SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
                }
+               else
+#endif
+                       print_msg(".");
+
+               pg_usleep(1000000);             /* 1 sec */
        }
 
-       /* value of last call to PQping */
+       /* return result of last call to PQping */
        return ret;
 }
 
@@ -673,7 +762,6 @@ do_init(void)
 static void
 do_start(void)
 {
-       pgpid_t         pid;
        pgpid_t         old_pid = 0;
        int                     exitcode;
 
@@ -723,39 +811,32 @@ do_start(void)
                exit(1);
        }
 
-       if (old_pid != 0)
-       {
-               pg_usleep(1000000);
-               pid = get_pgpid();
-               if (pid == old_pid)
-               {
-                       write_stderr(_("%s: could not start server\n"
-                                                  "Examine the log output.\n"),
-                                                progname);
-                       exit(1);
-               }
-       }
-
        if (do_wait)
        {
-               int status;
-               
                print_msg(_("waiting for server to start..."));
 
-               if ((status = test_postmaster_connection(false)) == PQNORESPONSE)
-               {
-                       write_stderr(_("%s: could not start server\n"
-                                                  "Examine the log output.\n"),
-                                                progname);
-                       exit(1);
-               }
-               else
+               switch (test_postmaster_connection(false))
                {
-                       print_msg(_(" done\n"));
-                       print_msg(_("server started\n"));
-                       if (status == PQREJECT)
-                               write_stderr(_("warning:  could not connect; might be caused by invalid authentication or\n"
-                                                               "misconfiguration.\n"));
+                       case PQPING_OK:
+                               print_msg(_(" done\n"));
+                               print_msg(_("server started\n"));
+                               break;
+                       case PQPING_REJECT:
+                               print_msg(_(" stopped waiting\n"));
+                               print_msg(_("server is still starting up\n"));
+                               break;
+                       case PQPING_NO_RESPONSE:
+                               print_msg(_(" stopped waiting\n"));
+                               write_stderr(_("%s: could not start server\n"
+                                                          "Examine the log output.\n"),
+                                                        progname);
+                               exit(1);
+                               break;
+                       case PQPING_NO_ATTEMPT:
+                               print_msg(_(" failed\n"));
+                               write_stderr(_("%s: could not wait for server because of misconfiguration\n"),
+                                                        progname);
+                               exit(1);
                }
        }
        else
@@ -802,8 +883,8 @@ do_stop(void)
        else
        {
                /*
-                * If backup_label exists, an online backup is running. Warn the
-                * user that smart shutdown will wait for it to finish. However, if
+                * 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
                 * backup instead of performing one.
                 */
@@ -833,6 +914,9 @@ do_stop(void)
                        print_msg(_(" failed\n"));
 
                        write_stderr(_("%s: server does not shut down\n"), progname);
+                       if (shutdown_mode == SMART_MODE)
+                               write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
+                                                 "waiting for session-initiated disconnection.\n"));
                        exit(1);
                }
                print_msg(_(" done\n"));
@@ -887,8 +971,8 @@ 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
+                * 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
                 * backup instead of performing one.
                 */
@@ -920,6 +1004,9 @@ do_restart(void)
                        print_msg(_(" failed\n"));
 
                        write_stderr(_("%s: server does not shut down\n"), progname);
+                       if (shutdown_mode == SMART_MODE)
+                               write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
+                                                 "waiting for session-initiated disconnection.\n"));
                        exit(1);
                }
 
@@ -936,7 +1023,6 @@ do_restart(void)
        do_start();
 }
 
-
 static void
 do_reload(void)
 {
@@ -969,6 +1055,72 @@ do_reload(void)
        print_msg(_("server signaled\n"));
 }
 
+
+/*
+ * promote
+ */
+
+static void
+do_promote(void)
+{
+       FILE       *prmfile;
+       pgpid_t         pid;
+       struct stat statbuf;
+
+       pid = get_pgpid();
+
+       if (pid == 0)                           /* no pid file */
+       {
+               write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
+               write_stderr(_("Is server running?\n"));
+               exit(1);
+       }
+       else if (pid < 0)                       /* standalone backend, not postmaster */
+       {
+               pid = -pid;
+               write_stderr(_("%s: cannot promote server; "
+                                          "single-user server is running (PID: %ld)\n"),
+                                        progname, pid);
+               exit(1);
+       }
+
+       /* If recovery.conf doesn't exist, the server is not in standby mode */
+       if (stat(recovery_file, &statbuf) != 0)
+       {
+               write_stderr(_("%s: cannot promote server; "
+                                          "server is not in standby mode\n"),
+                                        progname);
+               exit(1);
+       }
+
+       if ((prmfile = fopen(promote_file, "w")) == NULL)
+       {
+               write_stderr(_("%s: could not create promote signal file \"%s\": %s\n"),
+                                        progname, promote_file, strerror(errno));
+               exit(1);
+       }
+       if (fclose(prmfile))
+       {
+               write_stderr(_("%s: could not write promote signal file \"%s\": %s\n"),
+                                        progname, promote_file, strerror(errno));
+               exit(1);
+       }
+
+       sig = SIGUSR1;
+       if (kill((pid_t) pid, sig) != 0)
+       {
+               write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"),
+                                        progname, pid, strerror(errno));
+               if (unlink(promote_file) != 0)
+                       write_stderr(_("%s: could not remove promote signal file \"%s\": %s\n"),
+                                                progname, promote_file, strerror(errno));
+               exit(1);
+       }
+
+       print_msg(_("server promoting\n"));
+}
+
+
 /*
  *     utility routines
  */
@@ -1003,9 +1155,11 @@ do_status(void)
        pgpid_t         pid;
 
        pid = get_pgpid();
-       if (pid != 0)                           /* 0 means no pid file */
+       /* Is there a pid file? */
+       if (pid != 0)
        {
-               if (pid < 0)                    /* standalone backend */
+               /* standalone backend? */
+               if (pid < 0)
                {
                        pid = -pid;
                        if (postmaster_is_alive((pid_t) pid))
@@ -1016,7 +1170,7 @@ do_status(void)
                        }
                }
                else
-                       /* postmaster */
+                       /* must be a postmaster */
                {
                        if (postmaster_is_alive((pid_t) pid))
                        {
@@ -1034,7 +1188,14 @@ do_status(void)
                }
        }
        printf(_("%s: no server running\n"), progname);
-       exit(1);
+
+       /*
+        * The Linux Standard Base Core Specification 3.1 says this should return
+        * '3'
+        * http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-ge
+        * neric/iniscrptact.html
+        */
+       exit(3);
 }
 
 
@@ -1105,7 +1266,7 @@ pgwin32_CommandLine(bool registration)
 
        if (registration)
        {
-               if (pg_strcasecmp(cmdLine + strlen(cmdLine) - 4, ".exe"))
+               if (pg_strcasecmp(cmdLine + strlen(cmdLine) - 4, ".exe") != 0)
                {
                        /* If commandline does not end in .exe, append it */
                        strcat(cmdLine, ".exe");
@@ -1115,10 +1276,10 @@ pgwin32_CommandLine(bool registration)
                strcat(cmdLine, "\"");
        }
 
-       if (pg_data)
+       if (pg_config)
        {
                strcat(cmdLine, " -D \"");
-               strcat(cmdLine, pg_data);
+               strcat(cmdLine, pg_config);
                strcat(cmdLine, "\"");
        }
 
@@ -1129,6 +1290,9 @@ pgwin32_CommandLine(bool registration)
                /* concatenate */
                sprintf(cmdLine + strlen(cmdLine), " -t %d", wait_seconds);
 
+       if (registration && silent_mode)
+               strcat(cmdLine, " -s");
+
        if (post_opts)
        {
                strcat(cmdLine, " ");
@@ -1161,13 +1325,13 @@ pgwin32_doRegister(void)
        }
 
        if ((hService = CreateService(hSCM, register_servicename, register_servicename,
-                                                                 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+                                                          SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                                                                  pgctl_start_type, SERVICE_ERROR_NORMAL,
                                                                  pgwin32_CommandLine(true),
           NULL, NULL, "RPCSS\0", register_username, register_password)) == NULL)
        {
                CloseServiceHandle(hSCM);
-               write_stderr(_("%s: could not register service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+               write_stderr(_("%s: could not register service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
                exit(1);
        }
        CloseServiceHandle(hService);
@@ -1195,14 +1359,14 @@ pgwin32_doUnregister(void)
        if ((hService = OpenService(hSCM, register_servicename, DELETE)) == NULL)
        {
                CloseServiceHandle(hSCM);
-               write_stderr(_("%s: could not open service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+               write_stderr(_("%s: could not open service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
                exit(1);
        }
        if (!DeleteService(hService))
        {
                CloseServiceHandle(hService);
                CloseServiceHandle(hSCM);
-               write_stderr(_("%s: could not unregister service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+               write_stderr(_("%s: could not unregister service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
                exit(1);
        }
        CloseServiceHandle(hService);
@@ -1253,7 +1417,6 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR *argv)
 {
        PROCESS_INFORMATION pi;
        DWORD           ret;
-       DWORD           check_point_start;
 
        /* Initialize variables */
        status.dwWin32ExitCode = S_OK;
@@ -1289,21 +1452,15 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR *argv)
        if (do_wait)
        {
                write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Waiting for server startup...\n"));
-               if (test_postmaster_connection(true) == false)
+               if (test_postmaster_connection(true) != PQPING_OK)
                {
-                       write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Timed out waiting for server startup\n"));
+                       write_eventlog(EVENTLOG_ERROR_TYPE, _("Timed out waiting for server startup\n"));
                        pgwin32_SetServiceStatus(SERVICE_STOPPED);
                        return;
                }
                write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Server started and accepting connections\n"));
        }
 
-       /*
-        * Save the checkpoint value as it might have been incremented in
-        * test_postmaster_connection
-        */
-       check_point_start = status.dwCheckPoint;
-
        pgwin32_SetServiceStatus(SERVICE_RUNNING);
 
        /* Wait for quit... */
@@ -1345,7 +1502,7 @@ pgwin32_doRunAsService(void)
 
        if (StartServiceCtrlDispatcher(st) == 0)
        {
-               write_stderr(_("%s: could not start service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+               write_stderr(_("%s: could not start service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
                exit(1);
        }
 }
@@ -1364,8 +1521,10 @@ typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, L
 typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
 typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
 
-/* Windows API define missing from MingW headers */
+/* Windows API define missing from some versions of MingW headers */
+#ifndef  DISABLE_MAX_PRIVILEGE
 #define DISABLE_MAX_PRIVILEGE  0x1
+#endif
 
 /*
  * Create a restricted token, a job object sandbox, and execute the specified
@@ -1415,7 +1574,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
                 * NT4 doesn't have CreateRestrictedToken, so just call ordinary
                 * CreateProcess
                 */
-               write_stderr("WARNING: cannot create restricted tokens on this platform\n");
+               write_stderr(_("%s: WARNING: cannot create restricted tokens on this platform\n"), progname);
                if (Advapi32Handle != NULL)
                        FreeLibrary(Advapi32Handle);
                return CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, processInfo);
@@ -1424,7 +1583,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
        /* Open the current token to use as a base for the restricted one */
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
        {
-               write_stderr("Failed to open process token: %lu\n", GetLastError());
+               write_stderr(_("%s: could not open process token: error code %lu\n"), progname, GetLastError());
                return 0;
        }
 
@@ -1437,7 +1596,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
        SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
                                                                  0, &dropSids[1].Sid))
        {
-               write_stderr("Failed to allocate SIDs: %lu\n", GetLastError());
+               write_stderr(_("%s: could not allocate SIDs: error code %lu\n"), progname, GetLastError());
                return 0;
        }
 
@@ -1456,7 +1615,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
 
        if (!b)
        {
-               write_stderr("Failed to create restricted token: %lu\n", GetLastError());
+               write_stderr(_("%s: could not create restricted token: error code %lu\n"), progname, GetLastError());
                return 0;
        }
 
@@ -1494,7 +1653,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
                         * Log error if we can't get version, or if we're on WinXP/2003 or
                         * newer
                         */
-                       write_stderr("WARNING: could not locate all job object functions in system API\n");
+                       write_stderr(_("%s: WARNING: could not locate all job object functions in system API\n"), progname);
        }
        else
        {
@@ -1588,8 +1747,7 @@ do_advice(void)
 static void
 do_help(void)
 {
-       printf(_("%s is a utility to start, stop, restart, reload configuration files,\n"
-                        "report the status of a PostgreSQL server, or signal a PostgreSQL process.\n\n"), progname);
+       printf(_("%s is a utility to initialize, start, stop, or control a PostgreSQL server.\n\n"), progname);
        printf(_("Usage:\n"));
        printf(_("  %s init[db]               [-D DATADIR] [-s] [-o \"OPTIONS\"]\n"), progname);
        printf(_("  %s start   [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
@@ -1598,21 +1756,22 @@ do_help(void)
                         "                 [-o \"OPTIONS\"]\n"), progname);
        printf(_("  %s reload  [-D DATADIR] [-s]\n"), progname);
        printf(_("  %s status  [-D DATADIR]\n"), progname);
+       printf(_("  %s promote [-D DATADIR] [-s]\n"), progname);
        printf(_("  %s kill    SIGNALNAME PID\n"), progname);
 #if defined(WIN32) || defined(__CYGWIN__)
        printf(_("  %s register   [-N SERVICENAME] [-U USERNAME] [-P PASSWORD] [-D DATADIR]\n"
-                "                    [-S START-TYPE] [-w] [-t SECS] [-o \"OPTIONS\"]\n"), progname);
+                        "                    [-S START-TYPE] [-w] [-t SECS] [-o \"OPTIONS\"]\n"), progname);
        printf(_("  %s unregister [-N SERVICENAME]\n"), progname);
 #endif
 
        printf(_("\nCommon options:\n"));
-       printf(_("  -D, --pgdata DATADIR   location of the database storage area\n"));
+       printf(_("  -D, --pgdata=DATADIR   location of the database storage area\n"));
        printf(_("  -s, --silent           only print errors, no informational messages\n"));
-       printf(_("  -t SECS                seconds to wait when using -w option\n"));
+       printf(_("  -t, --timeout=SECS     seconds to wait when using -w option\n"));
+       printf(_("  -V, --version          output version information, then exit\n"));
        printf(_("  -w                     wait until operation completes\n"));
        printf(_("  -W                     do not wait until operation completes\n"));
-       printf(_("  --help                 show this help, then exit\n"));
-       printf(_("  --version              output version information, then exit\n"));
+       printf(_("  -?, --help             show this help, then exit\n"));
        printf(_("(The default is to wait for shutdown, but not for start or restart.)\n\n"));
        printf(_("If the -D option is omitted, the environment variable PGDATA is used.\n"));
 
@@ -1622,12 +1781,12 @@ do_help(void)
 #else
        printf(_("  -c, --core-files       not applicable on this platform\n"));
 #endif
-       printf(_("  -l, --log FILENAME     write (or append) server log to FILENAME\n"));
+       printf(_("  -l, --log=FILENAME     write (or append) server log to FILENAME\n"));
        printf(_("  -o OPTIONS             command line options to pass to postgres\n"
         "                         (PostgreSQL server executable) or initdb\n"));
        printf(_("  -p PATH-TO-POSTGRES    normally not necessary\n"));
        printf(_("\nOptions for stop or restart:\n"));
-       printf(_("  -m SHUTDOWN-MODE   can be \"smart\", \"fast\", or \"immediate\"\n"));
+       printf(_("  -m, --mode=MODE        MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
 
        printf(_("\nShutdown modes are:\n"));
        printf(_("  smart       quit after all clients have disconnected\n"));
@@ -1635,7 +1794,7 @@ do_help(void)
        printf(_("  immediate   quit without complete shutdown; will lead to recovery on restart\n"));
 
        printf(_("\nAllowed signal names for kill:\n"));
-       printf("  HUP INT QUIT ABRT TERM USR1 USR2\n");
+       printf("  ABRT HUP INT QUIT TERM USR1 USR2\n");
 
 #if defined(WIN32) || defined(__CYGWIN__)
        printf(_("\nOptions for register and unregister:\n"));
@@ -1685,25 +1844,24 @@ set_mode(char *modeopt)
 static void
 set_sig(char *signame)
 {
-       if (!strcmp(signame, "HUP"))
+       if (strcmp(signame, "HUP") == 0)
                sig = SIGHUP;
-       else if (!strcmp(signame, "INT"))
+       else if (strcmp(signame, "INT") == 0)
                sig = SIGINT;
-       else if (!strcmp(signame, "QUIT"))
+       else if (strcmp(signame, "QUIT") == 0)
                sig = SIGQUIT;
-       else if (!strcmp(signame, "ABRT"))
+       else if (strcmp(signame, "ABRT") == 0)
                sig = SIGABRT;
-
-       /*
-        * probably should NOT provide SIGKILL
-        *
-        * else if (!strcmp(signame,"KILL")) sig = SIGKILL;
-        */
-       else if (!strcmp(signame, "TERM"))
+#if 0
+       /* probably should NOT provide SIGKILL */
+       else if (strcmp(signame, "KILL") == 0)
+               sig = SIGKILL;
+#endif
+       else if (strcmp(signame, "TERM") == 0)
                sig = SIGTERM;
-       else if (!strcmp(signame, "USR1"))
+       else if (strcmp(signame, "USR1") == 0)
                sig = SIGUSR1;
-       else if (!strcmp(signame, "USR2"))
+       else if (strcmp(signame, "USR2") == 0)
                sig = SIGUSR2;
        else
        {
@@ -1731,6 +1889,67 @@ set_starttype(char *starttypeopt)
 }
 #endif
 
+/*
+ * adjust_data_dir
+ *
+ * If a configuration-only directory was specified, find the real data dir.
+ */
+static void
+adjust_data_dir(void)
+{
+       char            cmd[MAXPGPATH],
+                               filename[MAXPGPATH],
+                          *my_exec_path;
+       FILE       *fd;
+
+       /* do nothing if we're working without knowledge of data dir */
+       if (pg_config == NULL)
+               return;
+
+       /* If there is no postgresql.conf, it can't be a config-only dir */
+       snprintf(filename, sizeof(filename), "%s/postgresql.conf", pg_config);
+       if ((fd = fopen(filename, "r")) == NULL)
+               return;
+       fclose(fd);
+
+       /* If PG_VERSION exists, it can't be a config-only dir */
+       snprintf(filename, sizeof(filename), "%s/PG_VERSION", pg_config);
+       if ((fd = fopen(filename, "r")) != NULL)
+       {
+               fclose(fd);
+               return;
+       }
+
+       /* Must be a configuration directory, so find the data directory */
+
+       /* we use a private my_exec_path to avoid interfering with later uses */
+       if (exec_path == NULL)
+               my_exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR);
+       else
+               my_exec_path = xstrdup(exec_path);
+
+       snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s -C data_directory" SYSTEMQUOTE,
+                        my_exec_path, pgdata_opt ? pgdata_opt : "", post_opts ?
+                        post_opts : "");
+
+       fd = popen(cmd, "r");
+       if (fd == NULL || fgets(filename, sizeof(filename), fd) == NULL)
+       {
+               write_stderr(_("%s: could not determine the data directory using command \"%s\"\n"), progname, cmd);
+               exit(1);
+       }
+       pclose(fd);
+       free(my_exec_path);
+
+       /* Remove trailing newline */
+       if (strchr(filename, '\n') != NULL)
+               *strchr(filename, '\n') = '\0';
+
+       free(pg_data);
+       pg_data = xstrdup(filename);
+       canonicalize_path(pg_data);
+}
+
 
 int
 main(int argc, char **argv)
@@ -1757,6 +1976,7 @@ main(int argc, char **argv)
 
        progname = get_progname(argv[0]);
        set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_ctl"));
+       start_time = time(NULL);
 
        /*
         * save argv[0] so do_start() can look for the postmaster if necessary. we
@@ -1764,7 +1984,7 @@ main(int argc, char **argv)
         */
        argv0 = argv[0];
 
-       umask(077);
+       umask(S_IRWXG | S_IRWXO);
 
        /* support --help and --version even if invoked as root */
        if (argc > 1)
@@ -1924,6 +2144,8 @@ main(int argc, char **argv)
                                ctl_command = RELOAD_COMMAND;
                        else if (strcmp(argv[optind], "status") == 0)
                                ctl_command = STATUS_COMMAND;
+                       else if (strcmp(argv[optind], "promote") == 0)
+                               ctl_command = PROMOTE_COMMAND;
                        else if (strcmp(argv[optind], "kill") == 0)
                        {
                                if (argc - optind < 3)
@@ -1962,14 +2184,19 @@ main(int argc, char **argv)
        }
 
        /* Note we put any -D switch into the env var above */
-       pg_data = getenv("PGDATA");
-       if (pg_data)
+       pg_config = getenv("PGDATA");
+       if (pg_config)
        {
-               pg_data = xstrdup(pg_data);
-               canonicalize_path(pg_data);
+               pg_config = xstrdup(pg_config);
+               canonicalize_path(pg_config);
+               pg_data = xstrdup(pg_config);
        }
 
-       if (pg_data == NULL &&
+       /* -D might point at config-only directory; if so find the real PGDATA */
+       adjust_data_dir();
+
+       /* Complain if -D needed and not provided */
+       if (pg_config == NULL &&
                ctl_command != KILL_COMMAND && ctl_command != UNREGISTER_COMMAND)
        {
                write_stderr(_("%s: no database directory specified and environment variable PGDATA unset\n"),
@@ -2004,9 +2231,9 @@ main(int argc, char **argv)
        {
                snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
                snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
-               snprintf(conf_file, MAXPGPATH, "%s/postgresql.conf", pg_data);
                snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
                snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
+               snprintf(promote_file, MAXPGPATH, "%s/promote", pg_data);
        }
 
        switch (ctl_command)
@@ -2029,6 +2256,9 @@ main(int argc, char **argv)
                case RELOAD_COMMAND:
                        do_reload();
                        break;
+               case PROMOTE_COMMAND:
+                       do_promote();
+                       break;
                case KILL_COMMAND:
                        do_kill(killproc);
                        break;