From 7db244f6224d772a0c0fb26e6dfc38b2bad7a93e Mon Sep 17 00:00:00 2001 From: Roman Zharkov Date: Fri, 12 Jul 2019 13:01:58 +0700 Subject: [PATCH] [refer #PGPRO-2846] Avoid share locks on log_file between old and new cmd.exe when the server restarts. pg_ctl now opens the postmaster.pid file using pgwin32_open() function to correctly handle share locks. On Windows systems we cannot handle ERROR_DELETE_PENDING because GetLastError() returns ERROR_ACCESS_DENIED instead. So we rename the lock files before delete them. Discussion: /message-id/flat/16922.1520722108%40sss.pgh.pa.us --- src/backend/utils/init/miscinit.c | 18 ++++- src/bin/pg_ctl/pg_ctl.c | 135 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 3 deletions(-) diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index ce929d8806..32a3e9520f 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -840,9 +840,21 @@ UnlinkLockFiles(int status, Datum arg) foreach(l, lock_files) { - char *curfile = (char *) lfirst(l); - - unlink(curfile); + /* + * On Windows systems we cannot handle ERROR_DELETE_PENDING + * because GetLastError() returns ERROR_ACCESS_DENIED instead. + * So rename the file first. + */ + char *tmpfile; + char *curfile = (char *)lfirst(l); + tmpfile = psprintf("%s.deleted", curfile); + if (rename(curfile, tmpfile) == 0) { + unlink(tmpfile); + } + else { + unlink(curfile); + } + pfree(tmpfile); /* Should we complain if the unlink fails? */ } /* Since we're about to exit, no need to reclaim storage */ diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index ed2396aa6c..6b6705e0f2 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -31,6 +31,8 @@ #ifdef WIN32 /* on Unix, we don't need libpq */ #include "pqexpbuffer.h" +#include "windows.h" +#include "tlhelp32.h" #endif /* PID can be negative for standalone backend */ @@ -75,6 +77,11 @@ typedef enum static bool do_wait = true; static int wait_seconds = DEFAULT_WAIT; +#ifdef WIN32 +static int windows_shell_wait_seconds = 1; +#define open(a,b,c) pgwin32_open(a,b,c) /* It is not necessary to include whole port.h */ +#define fopen(a,b) pgwin32_fopen(a,b) +#endif static bool wait_seconds_arg = false; static bool silent_mode = false; static ShutdownMode shutdown_mode = FAST_MODE; @@ -110,6 +117,8 @@ static pid_t postmasterPID = -1; #define shutdownEvent shutdownHandles[0] #define postmasterProcess shutdownHandles[1] +static pgpid_t pgwin32_getppid(pgpid_t pid); +static bool pgwin32_parent_shell_is_alive(pid_t pid); #endif @@ -867,6 +876,12 @@ do_stop(void) pid = get_pgpid(false); +#ifdef WIN32 + /* On Windows systems we must additionaly check status of the processes after server stops */ + pgpid_t postmasters_shell_pid; + postmasters_shell_pid = pgwin32_getppid(pid); +#endif + if (pid == 0) /* no pid file */ { write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file); @@ -934,6 +949,32 @@ do_stop(void) "waiting for session-initiated disconnection.\n")); exit(1); } + +#ifdef WIN32 + /* + * On Windows systems postmaster's shell may still run after postmaster.pid file has deleted. + * This may lead to share locks on log_file between old and new cmd.exe when the server restarts. + * The share locks do not allow the new cmd.exe to run. + */ + if (postmasters_shell_pid > 0 && pgwin32_parent_shell_is_alive(postmasters_shell_pid)) + { + print_msg(_("\npostmaster's shell is still alive. waiting for server to shut down again...")); + /* wait for stop */ + for (cnt = 0; cnt < windows_shell_wait_seconds * WAITS_PER_SEC; cnt++) + { + if (pgwin32_parent_shell_is_alive(postmasters_shell_pid)) + { + if (cnt % WAITS_PER_SEC == 0) + print_msg("."); + pg_usleep(USEC_PER_SEC / WAITS_PER_SEC); + } + else + break; + } + /* Do not fail if the parent process is still running. */ + } +#endif + print_msg(_(" done\n")); print_msg(_("server stopped\n")); @@ -954,6 +995,12 @@ do_restart(void) pid = get_pgpid(false); +#ifdef WIN32 + /* On Windows systems we must additionaly check status of the processes after server stops */ + pgpid_t postmasters_shell_pid; + postmasters_shell_pid = pgwin32_getppid(pid); +#endif + if (pid == 0) /* no pid file */ { write_stderr(_("%s: PID file \"%s\" does not exist\n"), @@ -1026,6 +1073,31 @@ do_restart(void) exit(1); } +#ifdef WIN32 + /* + * On Windows systems postmaster's shell may still run after postmaster.pid file has deleted. + * This may lead to share locks on log_file between old and new cmd.exe when the server restarts. + * The share locks do not allow the new cmd.exe to run. + */ + if (postmasters_shell_pid > 0 && pgwin32_parent_shell_is_alive(postmasters_shell_pid)) + { + print_msg(_("\npostmaster's shell is still alive. waiting for server to shut down again...")); + /* wait for stop */ + for (cnt = 0; cnt < windows_shell_wait_seconds * WAITS_PER_SEC; cnt++) + { + if (pgwin32_parent_shell_is_alive(postmasters_shell_pid)) + { + if (cnt % WAITS_PER_SEC == 0) + print_msg("."); + pg_usleep(USEC_PER_SEC / WAITS_PER_SEC); + } + else + break; + } + /* Do not fail if the parent process is still running. */ + } +#endif + print_msg(_(" done\n")); print_msg(_("server stopped\n")); } @@ -1176,6 +1248,69 @@ do_promote(void) * utility routines */ +#ifdef WIN32 +static pgpid_t +pgwin32_getppid(pgpid_t pid) +{ + /* Finds parent process id. Returns 0 by default. */ + HANDLE hSnapshot; + PROCESSENTRY32 pe32; + pgpid_t ppid = 0; + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, (DWORD)pid); + __try{ + if (hSnapshot == INVALID_HANDLE_VALUE) __leave; + + ZeroMemory(&pe32, sizeof(pe32)); + pe32.dwSize = sizeof(pe32); + if (!Process32First(hSnapshot, &pe32)) __leave; + + do{ + if (pe32.th32ProcessID == pid){ + ppid = (pgpid_t)pe32.th32ParentProcessID; + break; + } + } while (Process32Next(hSnapshot, &pe32)); + + } + __finally{ + if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); + } + return ppid; +} + +static bool +pgwin32_parent_shell_is_alive(pid_t pid) +{ + { + /* Checks the parent cmd.exe with specified pid is running */ + HANDLE hSnapshot; + PROCESSENTRY32 pe32; + bool res = false; + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, (DWORD)pid); + __try{ + if (hSnapshot == INVALID_HANDLE_VALUE) __leave; + + ZeroMemory(&pe32, sizeof(pe32)); + pe32.dwSize = sizeof(pe32); + if (!Process32First(hSnapshot, &pe32)) __leave; + + do{ + if (pe32.th32ProcessID == pid && strcmp(pe32.szExeFile, "cmd.exe") == 0){ + res = true; + } + } while (Process32Next(hSnapshot, &pe32)); + + } + __finally{ + if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); + } + return res; + } +} +#endif + static bool postmaster_is_alive(pid_t pid) { -- 2.14.1.windows.1