mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-07 19:47:50 +08: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:
parent
6d899f098c
commit
74d4009b8c
@ -151,6 +151,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);
|
||||||
@ -216,7 +220,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? */
|
||||||
|
|
||||||
@ -1604,6 +1608,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
|
||||||
|
Loading…
Reference in New Issue
Block a user