Skip to content

Commit 744d01c

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 9919f4f commit 744d01c

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
@@ -149,6 +149,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
149149
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
150150
static void pgwin32_doRunAsService(void);
151151
static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
152+
static bool pgwin32_get_dynamic_tokeninfo(HANDLE token,
153+
TOKEN_INFORMATION_CLASS class,
154+
char **InfoBuffer, char *errbuf, int errsize);
155+
static int pgwin32_is_service(void);
152156
#endif
153157

154158
static pgpid_t get_pgpid(bool is_status_request);
@@ -216,7 +220,7 @@ write_stderr(const char *fmt,...)
216220
* On Win32, we print to stderr if running on a console, or write to
217221
* eventlog if running as a service
218222
*/
219-
if (!isatty(fileno(stderr))) /* Running as a service */
223+
if (!pgwin32_is_service()) /* Running as a service */
220224
{
221225
char errbuf[2048]; /* Arbitrary size? */
222226

@@ -1689,6 +1693,160 @@ pgwin32_doRunAsService(void)
16891693
}
16901694
}
16911695

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

16931851
/*
16941852
* 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