mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Windows: Make pg_ctl reliably detect service status
pg_ctl is using isatty() to verify whether the process is running in a terminal, and if not it sends its output to Windows' Event Log ... which does the wrong thing when the output has been redirected to a pipe, as reported in bug #13592. To fix, make pg_ctl use the code we already have to detect service-ness: in the master branch, move src/backend/port/win32/security.c to src/port (with suitable tweaks so that it runs properly in backend and frontend environments); pg_ctl already has access to pgport so it Just Works. In older branches, that's likely to cause trouble, so instead duplicate the required code in pg_ctl.c. Author: Michael Paquier Bug report and diagnosis: Egon Kocjan Backpatch: all supported branches
This commit is contained in:
		@@ -146,6 +146,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
 | 
				
			|||||||
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
 | 
					static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
 | 
				
			||||||
static void pgwin32_doRunAsService(void);
 | 
					static void pgwin32_doRunAsService(void);
 | 
				
			||||||
static int	CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
 | 
					static int	CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
 | 
				
			||||||
 | 
					static bool pgwin32_get_dynamic_tokeninfo(HANDLE token,
 | 
				
			||||||
 | 
												  TOKEN_INFORMATION_CLASS class,
 | 
				
			||||||
 | 
												  char **InfoBuffer, char *errbuf, int errsize);
 | 
				
			||||||
 | 
					static int pgwin32_is_service(void);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static pgpid_t get_pgpid(void);
 | 
					static pgpid_t get_pgpid(void);
 | 
				
			||||||
@@ -211,7 +215,7 @@ write_stderr(const char *fmt,...)
 | 
				
			|||||||
	 * On Win32, we print to stderr if running on a console, or write to
 | 
						 * On Win32, we print to stderr if running on a console, or write to
 | 
				
			||||||
	 * eventlog if running as a service
 | 
						 * eventlog if running as a service
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!isatty(fileno(stderr)))	/* Running as a service */
 | 
						if (!pgwin32_is_service())	/* Running as a service */
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		char		errbuf[2048];		/* Arbitrary size? */
 | 
							char		errbuf[2048];		/* Arbitrary size? */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1594,6 +1598,160 @@ pgwin32_doRunAsService(void)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Call GetTokenInformation() on a token and return a dynamically sized
 | 
				
			||||||
 | 
					 * buffer with the information in it. This buffer must be free():d by
 | 
				
			||||||
 | 
					 * the calling function!
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
 | 
				
			||||||
 | 
												  char **InfoBuffer, char *errbuf, int errsize)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DWORD		InfoBufferSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							snprintf(errbuf, errsize, "could not get token information: got zero size\n");
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							snprintf(errbuf, errsize, "could not get token information: error code %lu\n",
 | 
				
			||||||
 | 
									 GetLastError());
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*InfoBuffer = malloc(InfoBufferSize);
 | 
				
			||||||
 | 
						if (*InfoBuffer == NULL)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							snprintf(errbuf, errsize, "could not allocate %d bytes for token information\n",
 | 
				
			||||||
 | 
									 (int) InfoBufferSize);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!GetTokenInformation(token, class, *InfoBuffer,
 | 
				
			||||||
 | 
												 InfoBufferSize, &InfoBufferSize))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							snprintf(errbuf, errsize, "could not get token information: error code %lu\n",
 | 
				
			||||||
 | 
									 GetLastError());
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * We consider ourselves running as a service if one of the following is
 | 
				
			||||||
 | 
					 * true:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 1) We are running as Local System (only used by services)
 | 
				
			||||||
 | 
					 * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
 | 
				
			||||||
 | 
					 *	  process token by the SCM when starting a service)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return values:
 | 
				
			||||||
 | 
					 *	 0 = Not service
 | 
				
			||||||
 | 
					 *	 1 = Service
 | 
				
			||||||
 | 
					 *	-1 = Error
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: we can't report errors via write_stderr (because that calls this)
 | 
				
			||||||
 | 
					 * We are therefore reduced to writing directly on stderr, which sucks, but
 | 
				
			||||||
 | 
					 * we have few alternatives.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					pgwin32_is_service(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static int	_is_service = -1;
 | 
				
			||||||
 | 
						HANDLE		AccessToken;
 | 
				
			||||||
 | 
						char	   *InfoBuffer = NULL;
 | 
				
			||||||
 | 
						char		errbuf[256];
 | 
				
			||||||
 | 
						PTOKEN_GROUPS Groups;
 | 
				
			||||||
 | 
						PTOKEN_USER User;
 | 
				
			||||||
 | 
						PSID		ServiceSid;
 | 
				
			||||||
 | 
						PSID		LocalSystemSid;
 | 
				
			||||||
 | 
						SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
 | 
				
			||||||
 | 
						UINT		x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Only check the first time */
 | 
				
			||||||
 | 
						if (_is_service != -1)
 | 
				
			||||||
 | 
							return _is_service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "could not open process token: error code %lu\n",
 | 
				
			||||||
 | 
									GetLastError());
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* First check for local system */
 | 
				
			||||||
 | 
						if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer,
 | 
				
			||||||
 | 
														   errbuf, sizeof(errbuf)))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "%s", errbuf);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						User = (PTOKEN_USER) InfoBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!AllocateAndInitializeSid(&NtAuthority, 1,
 | 
				
			||||||
 | 
												  SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
													  &LocalSystemSid))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "could not get SID for local system account\n");
 | 
				
			||||||
 | 
							CloseHandle(AccessToken);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (EqualSid(LocalSystemSid, User->User.Sid))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							FreeSid(LocalSystemSid);
 | 
				
			||||||
 | 
							free(InfoBuffer);
 | 
				
			||||||
 | 
							CloseHandle(AccessToken);
 | 
				
			||||||
 | 
							_is_service = 1;
 | 
				
			||||||
 | 
							return _is_service;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						FreeSid(LocalSystemSid);
 | 
				
			||||||
 | 
						free(InfoBuffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Now check for group SID */
 | 
				
			||||||
 | 
						if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
 | 
				
			||||||
 | 
														   errbuf, sizeof(errbuf)))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "%s", errbuf);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Groups = (PTOKEN_GROUPS) InfoBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!AllocateAndInitializeSid(&NtAuthority, 1,
 | 
				
			||||||
 | 
													  SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
													  &ServiceSid))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "could not get SID for service group\n");
 | 
				
			||||||
 | 
							free(InfoBuffer);
 | 
				
			||||||
 | 
							CloseHandle(AccessToken);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_is_service = 0;
 | 
				
			||||||
 | 
						for (x = 0; x < Groups->GroupCount; x++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (EqualSid(ServiceSid, Groups->Groups[x].Sid))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								_is_service = 1;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free(InfoBuffer);
 | 
				
			||||||
 | 
						FreeSid(ServiceSid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CloseHandle(AccessToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return _is_service;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Mingw headers are incomplete, and so are the libraries. So we have to load
 | 
					 * Mingw headers are incomplete, and so are the libraries. So we have to load
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user