From a1587e41aecf2626c62f96cf07acbe5bca5871a3 Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Mon, 2 Jul 2007 21:58:31 +0000 Subject: [PATCH] - Fix the -w (wait) option to work in Windows service mode, per bug #3382. - 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 | 122 ++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 35 deletions(-) diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 3b819c4464..4cfc3941d9 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -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) {