]> 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 6bb3da78d020eebdd172814563396fcb182d4513..72fc4c1abf6ae80b92aafea727c9f07d36d9efb3 100644 (file)
@@ -2,9 +2,9 @@
  *
  * pg_ctl --- start/stops/restarts the PostgreSQL server
  *
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.92 2008/01/01 19:45:55 momjian Exp $
+ * src/bin/pg_ctl/pg_ctl.c
  *
  *-------------------------------------------------------------------------
  */
@@ -14,7 +14,7 @@
  * Need this to get defines for restricted tokens and jobs. And it
  * has to be set before any header from the Win32 API is loaded.
  */
-#define _WIN32_WINNT 0x0500
+#define _WIN32_WINNT 0x0501
 #endif
 
 #include "postgres_fe.h"
@@ -22,6 +22,7 @@
 
 #include <locale.h>
 #include <signal.h>
+#include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -33,6 +34,7 @@
 
 #include "libpq/pqsignal.h"
 #include "getopt_long.h"
+#include "miscadmin.h"
 
 #if defined(__CYGWIN__)
 #include <sys/cygwin.h>
 #undef WIN32
 #endif
 
-#ifndef HAVE_INT_OPTRESET
-int                    optreset;
-#endif
-
 /* PID can be negative for standalone backend */
 typedef long pgpid_t;
 
 
-#define WHITESPACE "\f\n\r\t\v" /* as defined by isspace() */
-
-/* postgres version ident string */
-#define PM_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"
-
-
 typedef enum
 {
        SMART_MODE,
@@ -66,11 +58,13 @@ typedef enum
 typedef enum
 {
        NO_COMMAND = 0,
+       INIT_COMMAND,
        START_COMMAND,
        STOP_COMMAND,
        RESTART_COMMAND,
        RELOAD_COMMAND,
        STATUS_COMMAND,
+       PROMOTE_COMMAND,
        KILL_COMMAND,
        REGISTER_COMMAND,
        UNREGISTER_COMMAND,
@@ -87,35 +81,58 @@ 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;
 static char *log_file = NULL;
-static char *postgres_path = NULL;
+static char *exec_path = NULL;
 static char *register_servicename = "PostgreSQL";              /* FIXME: + version ID? */
 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);
 static void do_help(void);
 static void set_mode(char *modeopt);
 static void set_sig(char *signame);
+static void do_init(void);
 static void do_start(void);
 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);
@@ -126,15 +143,7 @@ static void pgwin32_SetServiceStatus(DWORD);
 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);
-
-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]
+static int     CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
 #endif
 
 static pgpid_t get_pgpid(void);
@@ -142,14 +151,9 @@ static char **readfile(const char *path);
 static int     start_postmaster(void);
 static void read_post_opts(void);
 
-static bool test_postmaster_connection(bool);
+static PGPing test_postmaster_connection(bool);
 static bool postmaster_is_alive(pid_t pid);
 
-static char def_postopts_file[MAXPGPATH];
-static char postopts_file[MAXPGPATH];
-static char pid_file[MAXPGPATH];
-static char conf_file[MAXPGPATH];
-
 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
 static void unlimit_core_size(void);
 #endif
@@ -161,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");
@@ -301,7 +308,7 @@ static char **
 readfile(const char *path)
 {
        FILE       *infile;
-       int                     maxlength = 0,
+       int                     maxlength = 1,
                                linelen = 0;
        int                     nlines = 0;
        char      **result;
@@ -364,15 +371,18 @@ 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, "%s\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &%s",
-                                SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
-                                DEVNULL, log_file, SYSTEMQUOTE);
+               snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &" SYSTEMQUOTE,
+                                exec_path, pgdata_opt, post_opts,
+                                DEVNULL, log_file);
        else
-               snprintf(cmd, MAXPGPATH, "%s\"%s\" %s%s < \"%s\" 2>&1 &%s",
-                                SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
-                                DEVNULL, SYSTEMQUOTE);
+               snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" 2>&1 &" SYSTEMQUOTE,
+                                exec_path, pgdata_opt, post_opts, DEVNULL);
 
        return system(cmd);
 #else                                                  /* WIN32 */
@@ -385,15 +395,13 @@ start_postmaster(void)
        PROCESS_INFORMATION pi;
 
        if (log_file != NULL)
-               snprintf(cmd, MAXPGPATH, "CMD /C %s\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1%s",
-                                SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
-                                DEVNULL, log_file, SYSTEMQUOTE);
+               snprintf(cmd, MAXPGPATH, "CMD /C " SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1" SYSTEMQUOTE,
+                                exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
        else
-               snprintf(cmd, MAXPGPATH, "CMD /C %s\"%s\" %s%s < \"%s\" 2>&1%s",
-                                SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
-                                DEVNULL, SYSTEMQUOTE);
+               snprintf(cmd, MAXPGPATH, "CMD /C " SYSTEMQUOTE "\"%s\" %s%s < \"%s\" 2>&1" SYSTEMQUOTE,
+                                exec_path, pgdata_opt, post_opts, DEVNULL);
 
-       if (!CreateRestrictedProcess(cmd, &pi))
+       if (!CreateRestrictedProcess(cmd, &pi, false))
                return GetLastError();
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
@@ -405,127 +413,218 @@ 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 bool
+static PGPing
 test_postmaster_connection(bool do_checkpoint)
 {
-       PGconn     *conn;
-       bool            success = false;
+       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            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;
 
-       /* post_opts */
-       for (p = post_opts; *p;)
-       {
-               /* advance past whitespace/quoting */
-               while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
-                       p++;
-
-               if (strncmp(p, "-p", strlen("-p")) == 0)
-               {
-                       p += strlen("-p");
-                       /* advance past whitespace/quoting */
-                       while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
-                               p++;
-                       strlcpy(portstr, p, Min(strcspn(p, "\"'" WHITESPACE) + 1,
-                                                                       sizeof(portstr)));
-                       /* keep looking, maybe there is another -p */
-               }
-               /* Advance to next whitespace */
-               while (*p && !isspace((unsigned char) *p))
-                       p++;
-       }
+       connstr[0] = '\0';
 
-       /* config file */
-       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", strlen("port")) != 0)
-                                       continue;
-                               p += strlen("port");
-                               while (isspace((unsigned char) *p))
-                                       p++;
-                               if (*p != '=')
-                                       continue;
-                               p++;
-                               while (isspace((unsigned char) *p))
-                                       p++;
-                               strlcpy(portstr, p, Min(strcspn(p, "#" WHITESPACE) + 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];
 
-       /* environment */
-       if (!*portstr && getenv("PGPORT") != NULL)
-               strlcpy(portstr, getenv("PGPORT"), sizeof(portstr));
+                                               found_stale_pidfile = false;
+                                               pm_pid = (pgpid_t) pmpid;
 
-       /* default */
-       if (!*portstr)
-               snprintf(portstr, sizeof(portstr), "%d", DEF_PGPORT);
+                                               /*
+                                                * 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];
 
-       /*
-        * 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);
+                                               /*
+                                                * 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));
 
-       for (i = 0; i < wait_seconds; i++)
-       {
-               if ((conn = PQconnectdb(connstr)) != NULL &&
-                       (PQstatus(conn) == CONNECTION_OK ||
-                        PQconnectionNeedsPassword(conn)))
+                                               /* 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);
+                                       }
+                               }
+                       }
+               }
+
+               /* If we have a connection string, ping the server */
+               if (connstr[0] != '\0')
                {
-                       PQfinish(conn);
-                       success = true;
-                       break;
+                       ret = PQping(connstr);
+                       if (ret == PQPING_OK || ret == PQPING_NO_ATTEMPT)
+                               break;
                }
-               else
+
+               /*
+                * 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)
                {
-                       PQfinish(conn);
+                       struct stat statbuf;
 
-#if defined(WIN32)
-                       if (do_checkpoint)
+                       if (stat(pid_file, &statbuf) != 0)
+                               return PQPING_NO_RESPONSE;
+
+                       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 */
        }
 
-       return success;
+       /* return result of last call to PQping */
+       return ret;
 }
 
 
@@ -553,63 +652,116 @@ unlimit_core_size(void)
 static void
 read_post_opts(void)
 {
-       char       *optline = NULL;
-
        if (post_opts == NULL)
        {
-               char      **optlines;
-               int                     len;
-
-               optlines = readfile(ctl_command == RESTART_COMMAND ?
-                                                       postopts_file : def_postopts_file);
-               if (optlines == NULL)
+               post_opts = "";                 /* default */
+               if (ctl_command == RESTART_COMMAND)
                {
-                       if (ctl_command == START_COMMAND || ctl_command == RUN_AS_SERVICE_COMMAND)
-                               post_opts = "";
-                       else
+                       char      **optlines;
+
+                       optlines = readfile(postopts_file);
+                       if (optlines == NULL)
                        {
                                write_stderr(_("%s: could not read file \"%s\"\n"), progname, postopts_file);
                                exit(1);
                        }
-               }
-               else if (optlines[0] == NULL || optlines[1] != NULL)
-               {
-                       write_stderr(_("%s: option file \"%s\" must have exactly one line\n"),
-                                                progname, ctl_command == RESTART_COMMAND ?
-                                                postopts_file : def_postopts_file);
-                       exit(1);
-               }
-               else
-               {
-                       optline = optlines[0];
-                       len = strcspn(optline, "\r\n");
-                       optline[len] = '\0';
-
-                       if (ctl_command == RESTART_COMMAND)
+                       else if (optlines[0] == NULL || optlines[1] != NULL)
+                       {
+                               write_stderr(_("%s: option file \"%s\" must have exactly one line\n"),
+                                                        progname, postopts_file);
+                               exit(1);
+                       }
+                       else
                        {
+                               int                     len;
+                               char       *optline;
                                char       *arg1;
 
-                               arg1 = strchr(optline, *SYSTEMQUOTE);
-                               if (arg1 == NULL || arg1 == optline)
-                                       post_opts = "";
-                               else
+                               optline = optlines[0];
+                               /* trim off line endings */
+                               len = strcspn(optline, "\r\n");
+                               optline[len] = '\0';
+
+                               /*
+                                * Are we at the first option, as defined by space and
+                                * double-quote?
+                                */
+                               if ((arg1 = strstr(optline, " \"")) != NULL)
                                {
-                                       *(arg1 - 1) = '\0'; /* this should be a space */
-                                       post_opts = arg1;
+                                       *arg1 = '\0';           /* terminate so we get only program
+                                                                                * name */
+                                       post_opts = arg1 + 1;           /* point past whitespace */
                                }
-                               if (postgres_path != NULL)
-                                       postgres_path = optline;
+                               if (exec_path == NULL)
+                                       exec_path = optline;
                        }
-                       else
-                               post_opts = optline;
                }
        }
 }
 
+static char *
+find_other_exec_or_die(const char *argv0, const char *target, const char *versionstr)
+{
+       int                     ret;
+       char       *found_path;
+
+       found_path = pg_malloc(MAXPGPATH);
+
+       if ((ret = find_other_exec(argv0, target, versionstr, found_path)) < 0)
+       {
+               char            full_path[MAXPGPATH];
+
+               if (find_my_exec(argv0, full_path) < 0)
+                       strlcpy(full_path, progname, sizeof(full_path));
+
+               if (ret == -1)
+                       write_stderr(_("The program \"%s\" is needed by %s "
+                                                  "but was not found in the\n"
+                                                  "same directory as \"%s\".\n"
+                                                  "Check your installation.\n"),
+                                                target, progname, full_path);
+               else
+                       write_stderr(_("The program \"%s\" was found by \"%s\"\n"
+                                                  "but was not the same version as %s.\n"
+                                                  "Check your installation.\n"),
+                                                target, full_path, progname);
+               exit(1);
+       }
+
+       return found_path;
+}
+
+static void
+do_init(void)
+{
+       char            cmd[MAXPGPATH];
+
+       if (exec_path == NULL)
+               exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n");
+
+       if (pgdata_opt == NULL)
+               pgdata_opt = "";
+
+       if (post_opts == NULL)
+               post_opts = "";
+
+       if (!silent_mode)
+               snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s" SYSTEMQUOTE,
+                                exec_path, pgdata_opt, post_opts);
+       else
+               snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s > \"%s\"" SYSTEMQUOTE,
+                                exec_path, pgdata_opt, post_opts, DEVNULL);
+
+       if (system(cmd) != 0)
+       {
+               write_stderr(_("%s: database system initialization failed\n"), progname);
+               exit(1);
+       }
+}
+
 static void
 do_start(void)
 {
-       pgpid_t         pid;
        pgpid_t         old_pid = 0;
        int                     exitcode;
 
@@ -628,42 +780,29 @@ do_start(void)
        if (ctl_command == RESTART_COMMAND || pgdata_opt == NULL)
                pgdata_opt = "";
 
-       if (postgres_path == NULL)
-       {
-               char       *postmaster_path;
-               int                     ret;
-
-               postmaster_path = pg_malloc(MAXPGPATH);
-
-               if ((ret = find_other_exec(argv0, "postgres", PM_VERSIONSTR,
-                                                                  postmaster_path)) < 0)
-               {
-                       char            full_path[MAXPGPATH];
-
-                       if (find_my_exec(argv0, full_path) < 0)
-                               strlcpy(full_path, progname, sizeof(full_path));
-
-                       if (ret == -1)
-                               write_stderr(_("The program \"postgres\" is needed by %s "
-                                                          "but was not found in the\n"
-                                                          "same directory as \"%s\".\n"
-                                                          "Check your installation.\n"),
-                                                        progname, full_path);
-                       else
-                               write_stderr(_("The program \"postgres\" was found by \"%s\"\n"
-                                                          "but was not the same version as %s.\n"
-                                                          "Check your installation.\n"),
-                                                        full_path, progname);
-                       exit(1);
-               }
-               postgres_path = postmaster_path;
-       }
+       if (exec_path == NULL)
+               exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR);
 
 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
        if (allow_core_files)
                unlimit_core_size();
 #endif
 
+       /*
+        * If possible, tell the postmaster our parent shell's PID (see the
+        * comments in CreateLockFile() for motivation).  Windows hasn't got
+        * getppid() unfortunately.
+        */
+#ifndef WIN32
+       {
+               static char env_var[32];
+
+               snprintf(env_var, sizeof(env_var), "PG_GRANDPARENT_PID=%d",
+                                (int) getppid());
+               putenv(env_var);
+       }
+#endif
+
        exitcode = start_postmaster();
        if (exitcode != 0)
        {
@@ -672,32 +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)
        {
                print_msg(_("waiting for server to start..."));
 
-               if (test_postmaster_connection(false) == false)
+               switch (test_postmaster_connection(false))
                {
-                       printf(_("could not start server\n"));
-                       exit(1);
-               }
-               else
-               {
-                       print_msg(_(" done\n"));
-                       print_msg(_("server started\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
@@ -710,6 +849,7 @@ do_stop(void)
 {
        int                     cnt;
        pgpid_t         pid;
+       struct stat statbuf;
 
        pid = get_pgpid();
 
@@ -742,6 +882,20 @@ 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
+                * recovery.conf is also present, 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)
+               {
+                       print_msg(_("WARNING: online backup mode is active\n"
+                                               "Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+               }
+
                print_msg(_("waiting for server to shut down..."));
 
                for (cnt = 0; cnt < wait_seconds; cnt++)
@@ -760,11 +914,14 @@ 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"));
 
-               printf(_("server stopped\n"));
+               print_msg(_("server stopped\n"));
        }
 }
 
@@ -778,6 +935,7 @@ do_restart(void)
 {
        int                     cnt;
        pgpid_t         pid;
+       struct stat statbuf;
 
        pid = get_pgpid();
 
@@ -812,6 +970,20 @@ do_restart(void)
                        exit(1);
                }
 
+               /*
+                * 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.
+                */
+               if (shutdown_mode == SMART_MODE &&
+                       stat(backup_file, &statbuf) == 0 &&
+                       stat(recovery_file, &statbuf) != 0)
+               {
+                       print_msg(_("WARNING: online backup mode is active\n"
+                                               "Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+               }
+
                print_msg(_("waiting for server to shut down..."));
 
                /* always wait for restart */
@@ -832,11 +1004,14 @@ 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);
                }
 
                print_msg(_(" done\n"));
-               printf(_("server stopped\n"));
+               print_msg(_("server stopped\n"));
        }
        else
        {
@@ -848,7 +1023,6 @@ do_restart(void)
        do_start();
 }
 
-
 static void
 do_reload(void)
 {
@@ -881,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
  */
@@ -915,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))
@@ -928,7 +1170,7 @@ do_status(void)
                        }
                }
                else
-                       /* postmaster */
+                       /* must be a postmaster */
                {
                        if (postmaster_is_alive((pid_t) pid))
                        {
@@ -946,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);
 }
 
 
@@ -996,7 +1245,8 @@ pgwin32_CommandLine(bool registration)
        }
        else
        {
-               ret = find_other_exec(argv0, "postgres", PM_VERSIONSTR, cmdLine);
+               ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR,
+                                                         cmdLine);
                if (ret != 0)
                {
                        write_stderr(_("%s: could not find postgres program executable\n"), progname);
@@ -1006,13 +1256,17 @@ pgwin32_CommandLine(bool registration)
 
 #ifdef __CYGWIN__
        /* need to convert to windows path */
+#if CYGWIN_VERSION_DLL_MAJOR >= 1007
+       cygwin_conv_path(CCP_POSIX_TO_WIN_A, cmdLine, buf, sizeof(buf));
+#else
        cygwin_conv_to_full_win32_path(cmdLine, buf);
+#endif
        strcpy(cmdLine, buf);
 #endif
 
        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");
@@ -1022,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, "\"");
        }
 
@@ -1036,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, " ");
@@ -1069,12 +1326,12 @@ pgwin32_doRegister(void)
 
        if ((hService = CreateService(hSCM, register_servicename, register_servicename,
                                                           SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
-                                                                 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
+                                                                 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);
@@ -1102,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);
@@ -1120,7 +1377,7 @@ static void
 pgwin32_SetServiceStatus(DWORD currentState)
 {
        status.dwCurrentState = currentState;
-       SetServiceStatus(hStatus, (LPSERVICE_STATUS) & status);
+       SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
 }
 
 static void WINAPI
@@ -1156,11 +1413,10 @@ pgwin32_ServiceHandler(DWORD request)
 }
 
 static void WINAPI
-pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
+pgwin32_ServiceMain(DWORD argc, LPTSTR *argv)
 {
        PROCESS_INFORMATION pi;
        DWORD           ret;
-       DWORD           check_point_start;
 
        /* Initialize variables */
        status.dwWin32ExitCode = S_OK;
@@ -1184,7 +1440,7 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
 
        /* Start the postmaster */
        pgwin32_SetServiceStatus(SERVICE_START_PENDING);
-       if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi))
+       if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi, true))
        {
                pgwin32_SetServiceStatus(SERVICE_STOPPED);
                return;
@@ -1196,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... */
@@ -1252,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);
        }
 }
@@ -1264,15 +1514,17 @@ pgwin32_doRunAsService(void)
  * also load the couple of functions that *do* exist in minwg headers but not
  * on NT4. That way, we don't break on NT4.
  */
-typedef                BOOL(WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
-typedef                BOOL(WINAPI * __IsProcessInJob) (HANDLE, HANDLE, PBOOL);
-typedef                HANDLE(WINAPI * __CreateJobObject) (LPSECURITY_ATTRIBUTES, LPCTSTR);
-typedef                BOOL(WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD);
-typedef                BOOL(WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
-typedef                BOOL(WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
-
-/* Windows API define missing from MingW headers */
+typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
+typedef BOOL (WINAPI * __IsProcessInJob) (HANDLE, HANDLE, PBOOL);
+typedef HANDLE (WINAPI * __CreateJobObject) (LPSECURITY_ATTRIBUTES, LPCTSTR);
+typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD);
+typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
+typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
+
+/* 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
@@ -1287,7 +1539,7 @@ typedef           BOOL(WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS,
  * automatically destroyed when pg_ctl exits.
  */
 static int
-CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
+CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service)
 {
        int                     r;
        BOOL            b;
@@ -1322,7 +1574,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                 * 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);
@@ -1331,7 +1583,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
        /* 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;
        }
 
@@ -1344,7 +1596,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
        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;
        }
 
@@ -1363,10 +1615,14 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
 
        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;
        }
 
+#ifndef __CYGWIN__
+       AddUserToTokenDacl(restrictedToken);
+#endif
+
        r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, processInfo);
 
        Kernel32Handle = LoadLibrary("KERNEL32.DLL");
@@ -1397,7 +1653,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                         * 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
        {
@@ -1423,6 +1679,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                                        JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit;
                                        JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions;
                                        JOBOBJECT_SECURITY_LIMIT_INFORMATION securityLimit;
+                                       OSVERSIONINFO osv;
 
                                        ZeroMemory(&basicLimit, sizeof(basicLimit));
                                        ZeroMemory(&uiRestrictions, sizeof(uiRestrictions));
@@ -1433,8 +1690,25 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                                        _SetInformationJobObject(job, JobObjectBasicLimitInformation, &basicLimit, sizeof(basicLimit));
 
                                        uiRestrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_DESKTOP | JOB_OBJECT_UILIMIT_DISPLAYSETTINGS |
-                                               JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_HANDLES | JOB_OBJECT_UILIMIT_READCLIPBOARD |
+                                               JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_READCLIPBOARD |
                                                JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
+
+                                       if (as_service)
+                                       {
+                                               osv.dwOSVersionInfoSize = sizeof(osv);
+                                               if (!GetVersionEx(&osv) ||
+                                                       osv.dwMajorVersion < 6 ||
+                                               (osv.dwMajorVersion == 6 && osv.dwMinorVersion == 0))
+                                               {
+                                                       /*
+                                                        * On Windows 7 (and presumably later),
+                                                        * JOB_OBJECT_UILIMIT_HANDLES prevents us from
+                                                        * starting as a service. So we only enable it on
+                                                        * Vista and earlier (version <= 6.0)
+                                                        */
+                                                       uiRestrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
+                                               }
+                                       }
                                        _SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions));
 
                                        securityLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_NO_ADMIN | JOB_OBJECT_SECURITY_ONLY_TOKEN;
@@ -1447,6 +1721,7 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
                }
        }
 
+
        CloseHandle(restrictedToken);
 
        ResumeThread(processInfo->hThread);
@@ -1472,45 +1747,46 @@ 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);
        printf(_("  %s stop    [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname);
        printf(_("  %s restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"
-                "                 [-o \"OPTIONS\"]\n"), progname);
+                        "                 [-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"
-                "                    [-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"));
 
        printf(_("\nOptions for start or restart:\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)\n"));
-       printf(_("  -p PATH-TO-POSTGRES    normally not necessary\n"));
 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
        printf(_("  -c, --core-files       allow postgres to produce core files\n"));
 #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(_("  -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"));
@@ -1518,13 +1794,18 @@ 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"));
        printf(_("  -N SERVICENAME  service name with which to register PostgreSQL server\n"));
        printf(_("  -P PASSWORD     password of account to register PostgreSQL server\n"));
        printf(_("  -U USERNAME     user name of account to register PostgreSQL server\n"));
+       printf(_("  -S START-TYPE   service start type to register PostgreSQL server\n"));
+
+       printf(_("\nStart types are:\n"));
+       printf(_("  auto       start service automatically during system startup (default)\n"));
+       printf(_("  demand     start service on demand\n"));
 #endif
 
        printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
@@ -1563,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
        {
@@ -1589,9 +1869,86 @@ set_sig(char *signame)
                do_advice();
                exit(1);
        }
+}
+
 
+#if defined(WIN32) || defined(__CYGWIN__)
+static void
+set_starttype(char *starttypeopt)
+{
+       if (strcmp(starttypeopt, "a") == 0 || strcmp(starttypeopt, "auto") == 0)
+               pgctl_start_type = SERVICE_AUTO_START;
+       else if (strcmp(starttypeopt, "d") == 0 || strcmp(starttypeopt, "demand") == 0)
+               pgctl_start_type = SERVICE_DEMAND_START;
+       else
+       {
+               write_stderr(_("%s: unrecognized start type \"%s\"\n"), progname, starttypeopt);
+               do_advice();
+               exit(1);
+       }
 }
+#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
@@ -1618,7 +1975,8 @@ main(int argc, char **argv)
 #endif
 
        progname = get_progname(argv[0]);
-       set_pglocale_pgservice(argv[0], "pg_ctl");
+       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
@@ -1626,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)
@@ -1670,7 +2028,7 @@ main(int argc, char **argv)
        /* process command-line options */
        while (optind < argc)
        {
-               while ((c = getopt_long(argc, argv, "cD:l:m:N:o:p:P:st:U:wW", long_options, &option_index)) != -1)
+               while ((c = getopt_long(argc, argv, "cD:l:m:N:o:p:P:sS:t:U:wW", long_options, &option_index)) != -1)
                {
                        switch (c)
                        {
@@ -1709,7 +2067,7 @@ main(int argc, char **argv)
                                        post_opts = xstrdup(optarg);
                                        break;
                                case 'p':
-                                       postgres_path = xstrdup(optarg);
+                                       exec_path = xstrdup(optarg);
                                        break;
                                case 'P':
                                        register_password = xstrdup(optarg);
@@ -1717,6 +2075,15 @@ main(int argc, char **argv)
                                case 's':
                                        silent_mode = true;
                                        break;
+                               case 'S':
+#if defined(WIN32) || defined(__CYGWIN__)
+                                       set_starttype(optarg);
+#else
+                                       write_stderr(_("%s: -S option not supported on this platform\n"),
+                                                                progname);
+                                       exit(1);
+#endif
+                                       break;
                                case 't':
                                        wait_seconds = atoi(optarg);
                                        break;
@@ -1764,7 +2131,10 @@ main(int argc, char **argv)
                                exit(1);
                        }
 
-                       if (strcmp(argv[optind], "start") == 0)
+                       if (strcmp(argv[optind], "init") == 0
+                               || strcmp(argv[optind], "initdb") == 0)
+                               ctl_command = INIT_COMMAND;
+                       else if (strcmp(argv[optind], "start") == 0)
                                ctl_command = START_COMMAND;
                        else if (strcmp(argv[optind], "stop") == 0)
                                ctl_command = STOP_COMMAND;
@@ -1774,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)
@@ -1812,18 +2184,22 @@ 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"),
+               write_stderr(_("%s: no database directory specified and environment variable PGDATA unset\n"),
                                         progname);
                do_advice();
                exit(1);
@@ -1853,14 +2229,18 @@ main(int argc, char **argv)
 
        if (pg_data)
        {
-               snprintf(def_postopts_file, MAXPGPATH, "%s/postmaster.opts.default", pg_data);
                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)
        {
+               case INIT_COMMAND:
+                       do_init();
+                       break;
                case STATUS_COMMAND:
                        do_status();
                        break;
@@ -1876,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;