@@ -132,6 +132,7 @@ static void adjust_data_dir(void);
132
132
133
133
#ifdef WIN32
134
134
#include <versionhelpers.h>
135
+ #include <tlhelp32.h>
135
136
static bool pgwin32_IsInstalled (SC_HANDLE );
136
137
static char * pgwin32_CommandLine (bool );
137
138
static void pgwin32_doRegister (void );
@@ -142,6 +143,7 @@ static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
142
143
static void pgwin32_doRunAsService (void );
143
144
static int CreateRestrictedProcess (char * cmd , PROCESS_INFORMATION * processInfo , bool as_service );
144
145
static PTOKEN_PRIVILEGES GetPrivilegesToDelete (HANDLE hToken );
146
+ static pid_t pgwin32_find_postmaster_pid (pid_t shell_pid );
145
147
#endif
146
148
147
149
static pid_t get_pgpid (bool is_status_request );
@@ -609,7 +611,11 @@ wait_for_postmaster_start(pid_t pm_pid, bool do_checkpoint)
609
611
/* File is complete enough for us, parse it */
610
612
pid_t pmpid ;
611
613
time_t pmstart ;
612
-
614
+ #ifndef WIN32
615
+ pid_t wait_pid = pm_pid ;
616
+ #else
617
+ pid_t wait_pid = pgwin32_find_postmaster_pid (pm_pid );
618
+ #endif
613
619
/*
614
620
* Make sanity checks. If it's for the wrong PID, or the recorded
615
621
* start time is before pg_ctl started, then either we are looking
@@ -619,14 +625,8 @@ wait_for_postmaster_start(pid_t pm_pid, bool do_checkpoint)
619
625
*/
620
626
pmpid = atol (optlines [LOCK_FILE_LINE_PID - 1 ]);
621
627
pmstart = atol (optlines [LOCK_FILE_LINE_START_TIME - 1 ]);
622
- if (pmstart >= start_time - 2 &&
623
- #ifndef WIN32
624
- pmpid == pm_pid
625
- #else
626
- /* Windows can only reject standalone-backend PIDs */
627
- pmpid > 0
628
- #endif
629
- )
628
+
629
+ if (pmstart >= start_time - 2 && pmpid == wait_pid )
630
630
{
631
631
/*
632
632
* OK, seems to be a valid pidfile from our child. Check the
@@ -1959,6 +1959,93 @@ GetPrivilegesToDelete(HANDLE hToken)
1959
1959
1960
1960
return tokenPrivs ;
1961
1961
}
1962
+
1963
+ /*
1964
+ * Find the PID of the launched postmaster.
1965
+ *
1966
+ * On Windows, the cmd.exe doesn't support the exec command. As a result, we
1967
+ * don't directly get the postmaster's PID. This function identifies the PID of
1968
+ * the postmaster started by the child cmd.exe.
1969
+ *
1970
+ * Returns the postmaster's PID. If the shell is alive but the postmaster is
1971
+ * missing, returns 0. Otherwise terminates this command with an error.
1972
+ *
1973
+ * This function uses PID 0 as an invalid value, assuming the system idle
1974
+ * process occupies it and it won't be a PID for a shell or postmaster.
1975
+ */
1976
+ static pid_t
1977
+ pgwin32_find_postmaster_pid (pid_t shell_pid )
1978
+ {
1979
+ HANDLE hSnapshot ;
1980
+ PROCESSENTRY32 ppe ;
1981
+ pid_t pm_pid = 0 ; /* abusing 0 as an invalid value */
1982
+ bool multiple_children = false;
1983
+ DWORD last_error ;
1984
+
1985
+ /* create a process snapshot */
1986
+ hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS , 0 );
1987
+ if (hSnapshot == INVALID_HANDLE_VALUE )
1988
+ {
1989
+ write_stderr (_ ("%s: CreateToolhelp32Snapshot failed: error code %lu\n" ),
1990
+ progname , (unsigned long ) GetLastError ());
1991
+ exit (1 );
1992
+ }
1993
+
1994
+ /* start iterating on the snapshot */
1995
+ ppe .dwSize = sizeof (PROCESSENTRY32 );
1996
+ if (!Process32First (hSnapshot , & ppe ))
1997
+ {
1998
+ write_stderr (_ ("%s: Process32First failed: error code %lu\n" ),
1999
+ progname , (unsigned long ) GetLastError ());
2000
+ exit (1 );
2001
+ }
2002
+
2003
+ /*
2004
+ * Iterate over the snapshot
2005
+ *
2006
+ * Check for duplicate processes to ensure reliability.
2007
+ *
2008
+ * The ppe entry to be examined is identified by th32ParentProcessID, which
2009
+ * should correspond to the cmd.exe process that executes the postgres.exe
2010
+ * binary. Additionally, th32ProcessID in the same entry should be the PID
2011
+ * of the launched postgres.exe. However, even though we have launched the
2012
+ * parent cmd.exe with the /D option specified, it is sometimes observed
2013
+ * that another cmd.exe is launched for unknown reasons. Therefore, it is
2014
+ * crucial to verify the program file name to avoid returning the wrong
2015
+ * PID.
2016
+ */
2017
+ do
2018
+ {
2019
+ if (ppe .th32ParentProcessID == shell_pid &&
2020
+ strcmp ("postgres.exe" , ppe .szExeFile ) == 0 )
2021
+ {
2022
+ if (pm_pid != ppe .th32ProcessID && pm_pid != 0 )
2023
+ multiple_children = true;
2024
+ pm_pid = ppe .th32ProcessID ;
2025
+ }
2026
+ }
2027
+ while (Process32Next (hSnapshot , & ppe ));
2028
+
2029
+ /* avoid multiple calls primary for clarity, not out of necessity */
2030
+ last_error = GetLastError ();
2031
+ if (last_error != ERROR_NO_MORE_FILES )
2032
+ {
2033
+ write_stderr (_ ("%s: Process32Next failed: error code %lu\n" ),
2034
+ progname , (unsigned long ) last_error );
2035
+ exit (1 );
2036
+ }
2037
+ CloseHandle (hSnapshot );
2038
+
2039
+ /* assuming the launching shell executes a single process */
2040
+ if (multiple_children )
2041
+ {
2042
+ write_stderr (_ ("%s: multiple postmasters found\n" ),
2043
+ progname );
2044
+ exit (1 );
2045
+ }
2046
+
2047
+ return pm_pid ;
2048
+ }
1962
2049
#endif /* WIN32 */
1963
2050
1964
2051
static void
0 commit comments