pg_ctl.patch
text/x-diff
Filename: pg_ctl.patch
Type: text/x-diff
Part: 0
Patch
Format: format-patch
Subject: [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: https://www.postgresql.org/message-id/flat/16922.1520722108%40sss.pgh.pa.us
| File | + | − |
|---|---|---|
| src/backend/utils/init/miscinit.c | 15 | 3 |
| src/bin/pg_ctl/pg_ctl.c | 135 | 0 |
From 7db244f6224d772a0c0fb26e6dfc38b2bad7a93e Mon Sep 17 00:00:00 2001
From: Roman Zharkov <r.zharkov@postgrespro.ru>
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:
https://www.postgresql.org/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