Skip to content

Commit c7aca3d

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 8c558b2 commit c7aca3d

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
@@ -152,6 +152,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
152152
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
153153
static void pgwin32_doRunAsService(void);
154154
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
155+
static bool pgwin32_get_dynamic_tokeninfo(HANDLE token,
156+
TOKEN_INFORMATION_CLASS class,
157+
char **InfoBuffer, char *errbuf, int errsize);
158+
static int pgwin32_is_service(void);
155159
#endif
156160

157161
static pgpid_t get_pgpid(bool is_status_request);
@@ -218,7 +222,7 @@ write_stderr(const char *fmt,...)
218222
* On Win32, we print to stderr if running on a console, or write to
219223
* eventlog if running as a service
220224
*/
221-
if (!isatty(fileno(stderr))) /* Running as a service */
225+
if (!pgwin32_is_service()) /* Running as a service */
222226
{
223227
char errbuf[2048]; /* Arbitrary size? */
224228

@@ -1681,6 +1685,160 @@ pgwin32_doRunAsService(void)
16811685
}
16821686
}
16831687

1688+
/*
1689+
* Call GetTokenInformation() on a token and return a dynamically sized
1690+
* buffer with the information in it. This buffer must be free():d by
1691+
* the calling function!
1692+
*/
1693+
static bool
1694+
pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
1695+
char **InfoBuffer, char *errbuf, int errsize)
1696+
{
1697+
DWORD InfoBufferSize;
1698+
1699+
if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize))
1700+
{
1701+
snprintf(errbuf, errsize, "could not get token information: got zero size\n");
1702+
return false;
1703+
}
1704+
1705+
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1706+
{
1707+
snprintf(errbuf, errsize, "could not get token information: error code %lu\n",
1708+
GetLastError());
1709+
return false;
1710+
}
1711+
1712+
*InfoBuffer = malloc(InfoBufferSize);
1713+
if (*InfoBuffer == NULL)
1714+
{
1715+
snprintf(errbuf, errsize, "could not allocate %d bytes for token information\n",
1716+
(int) InfoBufferSize);
1717+
return false;
1718+
}
1719+
1720+
if (!GetTokenInformation(token, class, *InfoBuffer,
1721+
InfoBufferSize, &InfoBufferSize))
1722+
{
1723+
snprintf(errbuf, errsize, "could not get token information: error code %lu\n",
1724+
GetLastError());
1725+
return false;
1726+
}
1727+
1728+
return true;
1729+
}
1730+
1731+
/*
1732+
* We consider ourselves running as a service if one of the following is
1733+
* true:
1734+
*
1735+
* 1) We are running as Local System (only used by services)
1736+
* 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
1737+
* process token by the SCM when starting a service)
1738+
*
1739+
* Return values:
1740+
* 0 = Not service
1741+
* 1 = Service
1742+
* -1 = Error
1743+
*
1744+
* Note: we can't report errors via write_stderr (because that calls this)
1745+
* We are therefore reduced to writing directly on stderr, which sucks, but
1746+
* we have few alternatives.
1747+
*/
1748+
int
1749+
pgwin32_is_service(void)
1750+
{
1751+
static int _is_service = -1;
1752+
HANDLE AccessToken;
1753+
char *InfoBuffer = NULL;
1754+
char errbuf[256];
1755+
PTOKEN_GROUPS Groups;
1756+
PTOKEN_USER User;
1757+
PSID ServiceSid;
1758+
PSID LocalSystemSid;
1759+
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
1760+
UINT x;
1761+
1762+
/* Only check the first time */
1763+
if (_is_service != -1)
1764+
return _is_service;
1765+
1766+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
1767+
{
1768+
fprintf(stderr, "could not open process token: error code %lu\n",
1769+
GetLastError());
1770+
return -1;
1771+
}
1772+
1773+
/* First check for local system */
1774+
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer,
1775+
errbuf, sizeof(errbuf)))
1776+
{
1777+
fprintf(stderr, "%s", errbuf);
1778+
return -1;
1779+
}
1780+
1781+
User = (PTOKEN_USER) InfoBuffer;
1782+
1783+
if (!AllocateAndInitializeSid(&NtAuthority, 1,
1784+
SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
1785+
&LocalSystemSid))
1786+
{
1787+
fprintf(stderr, "could not get SID for local system account\n");
1788+
CloseHandle(AccessToken);
1789+
return -1;
1790+
}
1791+
1792+
if (EqualSid(LocalSystemSid, User->User.Sid))
1793+
{
1794+
FreeSid(LocalSystemSid);
1795+
free(InfoBuffer);
1796+
CloseHandle(AccessToken);
1797+
_is_service = 1;
1798+
return _is_service;
1799+
}
1800+
1801+
FreeSid(LocalSystemSid);
1802+
free(InfoBuffer);
1803+
1804+
/* Now check for group SID */
1805+
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
1806+
errbuf, sizeof(errbuf)))
1807+
{
1808+
fprintf(stderr, "%s", errbuf);
1809+
return -1;
1810+
}
1811+
1812+
Groups = (PTOKEN_GROUPS) InfoBuffer;
1813+
1814+
if (!AllocateAndInitializeSid(&NtAuthority, 1,
1815+
SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
1816+
&ServiceSid))
1817+
{
1818+
fprintf(stderr, "could not get SID for service group\n");
1819+
free(InfoBuffer);
1820+
CloseHandle(AccessToken);
1821+
return -1;
1822+
}
1823+
1824+
_is_service = 0;
1825+
for (x = 0; x < Groups->GroupCount; x++)
1826+
{
1827+
if (EqualSid(ServiceSid, Groups->Groups[x].Sid))
1828+
{
1829+
_is_service = 1;
1830+
break;
1831+
}
1832+
}
1833+
1834+
free(InfoBuffer);
1835+
FreeSid(ServiceSid);
1836+
1837+
CloseHandle(AccessToken);
1838+
1839+
return _is_service;
1840+
}
1841+
16841842

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

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy