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)