10000 Windows: Make pg_ctl reliably detect service status · ccneo/postgres@5c4cbd5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5c4cbd5

Browse files
committed
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
1 parent 9b2eacb commit 5c4cbd5

File tree

1 file changed

+159
-1
lines changed

1 file changed

+159
-1
lines changed

src/bin/pg_ctl/pg_ctl.c

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
148148
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
149149
static void pgwin32_doRunAsService(void);
150150
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
151+
static bool pgwin32_get_dynamic_tokeninfo(HANDLE token,
152+
TOKEN_INFORMATION_CLASS class,
153+
char **InfoBuffer, char *errbuf, int errsize);
154+
static int pgwin32_is_service(void);
151155
#endif
152156

153157
static pgpid_t get_pgpid(void);
@@ -213,7 +217,7 @@ write_stderr(const char *fmt,...)
213217
* On Win32, we print to stderr if running on a console, or write to
214218
* eventlog if running as a service
215219
*/
216-
if (!isatty(fileno(stderr))) /* Running as a service */
220+
if (!pgwin32_is_service()) /* Running as a service */
217221
{
218222
char errbuf[2048]; /* Arbitrary size? */
219223

@@ -1601,6 +1605,160 @@ pgwin32_doRunAsService(void)
16011605
}
16021606
}
16031607

1608+
/*
1609+
* Call GetTokenInformation() on a token and return a dynamically sized
1610+
* buffer with the information in it. This buffer must be free():d by
1611+
* the calling function!
1612+
*/
1613+
static bool
1614+
pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
1615+
char **InfoBuffer, char *errbuf, int errsize)
1616+
{
1617+
DWORD InfoBufferSize;
1618+
1619+
if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize))
1620+
{
1621+
snprintf(errbuf, errsize, "could not get token information: got zero size\n");
1622+
return false;
1623+
}
1624+
1625+
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1626+
{
1627+
snprintf(errbuf, errsize, "could not get token information: error code %lu\n",
1628+
GetLastError());
1629+
return false;
1630+
}
1631+
1632+
*InfoBuffer = malloc(InfoBufferSize);
1633+
if (*InfoBuffer == NULL)
1634+
{
1635+
snprintf(errbuf, errsize, "could not allocate %d bytes for token information\n",
1636+
(int) InfoBufferSize);
1637+
return false;
1638+
}
1639+
1640+
if (!GetTokenInformation(token, class, *InfoBuffer,
1641+
InfoBufferSize, &InfoBufferSize))
1642+
{
1643+
snprintf(errbuf, errsize, "could not get token information: error code %lu\n",
1644+
GetLastError());
1645+
return false;
1646+
}
1647+
1648+
return true;
1649+
}
1650+
1651+
/*
1652+
* We consider ourselves running as a service if one of the following is
1653+
* true:
1654+
*
1655+
* 1) We are running as Local System (only used by services)
1656+
* 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
1657+
* process token by the SCM when starting a service)
1658+
*
1659+
* Return values:
1660+
* 0 = Not service
1661+
* 1 = Service
1662+
* -1 = Error
1663+
*
1664+
* Note: we can't report errors via write_stderr (because that calls this)
1665+
* We are therefore reduced to writing directly on stderr, which sucks, but
1666+
* we have few alternatives.
1667+
*/
1668+
int
1669+
pgwin32_is_service(void)
1670+
{
1671+
static int _is_service = -1;
1672+
HANDLE AccessToken;
1673+
char *InfoBuffer = NULL;
1674+
char errbuf[256];
1675+
PTOKEN_GROUPS Groups;
1676+
PTOKEN_USER User;
1677+
PSID ServiceSid;
1678+
PSID LocalSystemSid;
1679+
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
1680+
UINT x;
1681+
1682+
/* Only check the first time */
1683+
if (_is_service != -1)
1684+
return _is_service;
1685+
1686+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
1687+
{
1688+
fprintf(stderr, "could not open process token: error code %lu\n",
1689+
GetLastError());
1690+
return -1;
1691+
}
1692+
1693+
/* First check for local system */
1694+
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer,
1695+
errbuf, sizeof(errbuf)))
1696+
{
1697+
fprintf(stderr, "%s", errbuf);
1698+
return -1;
1699+
}
1700+
1701+
User = (PTOKEN_USER) InfoBuffer;
1702+
1703+
if (!AllocateAndInitializeSid(&NtAuthority, 1,
1704+
SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
1705+
&LocalSystemSid))
1706+
{
1707+
fprintf(stderr, "could not get SID for local system account\n");
1708+
CloseHandle(AccessToken);
1709+
return -1;
1710+
}
1711+
1712+
if (EqualSid(LocalSystemSid, User->User.Sid))
1713+
{
1714+
FreeSid(LocalSystemSid);
1715+
free(InfoBuffer);
1716+
CloseHandle(AccessToken);
1717+
_is_service = 1;
1718+
return _is_service;
1719+
}
1720+
1721+
FreeSid(LocalSystemSid);
1722+
free(InfoBuffer);
1723+
1724+
/* Now check for group SID */
1725+
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
1726+
errbuf, sizeof(errbuf)))
1727+
{
1728+
fprintf(stderr, "%s", errbuf);
1729+
return -1;
1730+
}
1731+
1732+
Groups = (PTOKEN_GROUPS) InfoBuffer;
1733+
1734+
if (!AllocateAndInitializeSid(&NtAuthority, 1,
1735+
SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
1736+
&ServiceSid))
1737+
{
1738+
fprintf(stderr, "could not get SID for service group\n");
1739+
free(InfoBuffer);
1740+
CloseHandle(AccessToken);
1741+
return -1;
1742+
}
1743+
1744+
_is_service = 0;
1745+
for (x = 0; x < Groups->GroupCount; x++)
1746+
{
1747+
if (EqualSid(ServiceSid, Groups->Groups[x].Sid))
1748+
{
1749+
_is_service = 1;
1750+
break;
1751+
}
1752+
}
1753+
1754+
free(InfoBuffer);
1755+
FreeSid(ServiceSid);
1756+
1757+
CloseHandle(AccessToken);
1758+
1759+
return _is_service;
1760+
}
1761+
16041762

16051763
/*
16061764
* Mingw headers are incomplete, and so are the libraries. So we have to load

0 commit comments

Comments
 (0)
0