- Fix the -w (wait) option to work in Windows service mode, per bug #3382.
authorMagnus Hagander
Mon, 2 Jul 2007 21:58:31 +0000 (21:58 +0000)
committerMagnus Hagander
Mon, 2 Jul 2007 21:58:31 +0000 (21:58 +0000)
- Prevent the -w option being passed to the postmaster.
- Read the postmaster options file when starting as a Windows service.

Dave Page

src/bin/pg_ctl/pg_ctl.c

index 3b819c44649fee01d521edd71e14c5f7c44b3b11..4cfc3941d9c494fdaacd1ab432227fb249ea0faa 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.80 2007/05/31 15:13:04 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.81 2007/07/02 21:58:31 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -126,11 +126,22 @@ 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]
 #endif
+
 static pgpid_t get_pgpid(void);
 static char **readfile(const char *path);
-static int start_postmaster(void);
-static bool test_postmaster_connection(void);
+static int start_postmaster(void);
+static void read_post_opts(void);
+
+static bool test_postmaster_connection(bool);
 static bool postmaster_is_alive(pid_t pid);
 
 static char def_postopts_file[MAXPGPATH];
@@ -391,15 +402,20 @@ start_postmaster(void)
 
 
 
-/* Find the pgport and try a connection */
+/*
+ * 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
-test_postmaster_connection(void)
+test_postmaster_connection(bool do_checkpoint)
 {
    PGconn     *conn;
    bool        success = false;
    int         i;
    char        portstr[32];
    char       *p;
+   char        connstr[128]; /* Should be way more than enough! */
 
    *portstr = '\0';
 
@@ -464,10 +480,12 @@ test_postmaster_connection(void)
    if (!*portstr)
        snprintf(portstr, sizeof(portstr), "%d", DEF_PGPORT);
 
+   /* 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);
+
    for (i = 0; i < wait_seconds; i++)
    {
-       if ((conn = PQsetdbLogin(NULL, portstr, NULL, NULL,
-                                "postgres", NULL, NULL)) != NULL &&
+       if ((conn = PQconnectdb(connstr)) != NULL &&
            (PQstatus(conn) == CONNECTION_OK ||
             (strcmp(PQerrorMessage(conn),
                     PQnoPasswordSupplied) == 0)))
@@ -479,7 +497,25 @@ test_postmaster_connection(void)
        else
        {
            PQfinish(conn);
-           print_msg(".");
+
+#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 */
        }
    }
@@ -508,24 +544,10 @@ unlimit_core_size(void)
 }
 #endif
 
-
-
 static void
-do_start(void)
+read_post_opts(void)
 {
-   pgpid_t     pid;
-   pgpid_t     old_pid = 0;
    char       *optline = NULL;
-   int         exitcode;
-
-   if (ctl_command != RESTART_COMMAND)
-   {
-       old_pid = get_pgpid();
-       if (old_pid != 0)
-           write_stderr(_("%s: another server might be running; "
-                          "trying to start server anyway\n"),
-                        progname);
-   }
 
    if (post_opts == NULL)
    {
@@ -536,7 +558,7 @@ do_start(void)
                            postopts_file : def_postopts_file);
        if (optlines == NULL)
        {
-           if (ctl_command == START_COMMAND)
+           if (ctl_command == START_COMMAND || ctl_command == RUN_AS_SERVICE_COMMAND)
                post_opts = "";
            else
            {
@@ -576,6 +598,25 @@ do_start(void)
                post_opts = optline;
        }
    }
+}
+
+static void
+do_start(void)
+{
+   pgpid_t     pid;
+   pgpid_t     old_pid = 0;
+   int         exitcode;
+
+   if (ctl_command != RESTART_COMMAND)
+   {
+       old_pid = get_pgpid();
+       if (old_pid != 0)
+           write_stderr(_("%s: another server might be running; "
+                          "trying to start server anyway\n"),
+                        progname);
+   }
+
+   read_post_opts();
 
    /* No -D or -D already added during server start */
    if (ctl_command == RESTART_COMMAND || pgdata_opt == NULL)
@@ -642,7 +683,7 @@ do_start(void)
    {
        print_msg(_("waiting for server to start..."));
 
-       if (test_postmaster_connection() == false)
+       if (test_postmaster_connection(false) == false)
        {
            printf(_("could not start server\n"));
            exit(1);
@@ -982,7 +1023,7 @@ pgwin32_CommandLine(bool registration)
        strcat(cmdLine, "\"");
    }
 
-   if (do_wait)
+   if (registration && do_wait)
        strcat(cmdLine, " -w");
 
    if (post_opts)
@@ -1065,15 +1106,6 @@ pgwin32_doUnregister(void)
    CloseServiceHandle(hSCM);
 }
 
-
-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 void
 pgwin32_SetServiceStatus(DWORD currentState)
 {
@@ -1118,6 +1150,7 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
 {
    PROCESS_INFORMATION pi;
    DWORD       ret;
+   DWORD       check_point_start;
 
    /* Initialize variables */
    status.dwWin32ExitCode = S_OK;
@@ -1130,6 +1163,8 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
 
    memset(&pi, 0, sizeof(pi));
 
+        read_post_opts();
+
    /* Register the control request handler */
    if ((hStatus = RegisterServiceCtrlHandler(register_servicename, pgwin32_ServiceHandler)) == (SERVICE_STATUS_HANDLE) 0)
        return;
@@ -1147,10 +1182,27 @@ pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
    postmasterPID = pi.dwProcessId;
    postmasterProcess = pi.hProcess;
    CloseHandle(pi.hThread);
+
+   if (do_wait)
+   {
+       write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Waiting for server startup...\n"));
+       if (test_postmaster_connection(true) == false)
+       {
+                   write_eventlog(EVENTLOG_INFORMATION_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... */
    ret = WaitForMultipleObjects(2, shutdownHandles, FALSE, INFINITE);
+
    pgwin32_SetServiceStatus(SERVICE_STOP_PENDING);
    switch (ret)
    {