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:
Alvaro Herrera 2016-01-07 11:59:08 -03:00
parent dad08994b2
commit a967613911
5 changed files with 63 additions and 28 deletions

View File

@ -12,7 +12,7 @@ subdir = src/backend/port/win32
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = timer.o socket.o signal.o security.o mingwcompat.o OBJS = timer.o socket.o signal.o mingwcompat.o
ifeq ($(have_win32_dbghelp), yes) ifeq ($(have_win32_dbghelp), yes)
OBJS += crashdump.o OBJS += crashdump.o
endif endif

View File

@ -216,7 +216,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? */

View File

@ -382,9 +382,6 @@ int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);
extern int pgwin32_noblock; extern int pgwin32_noblock;
/* in backend/port/win32/security.c */
extern int pgwin32_is_admin(void);
extern int pgwin32_is_service(void);
#endif #endif
/* in backend/port/win32_shmem.c */ /* in backend/port/win32_shmem.c */
@ -400,6 +397,10 @@ extern void _dosmaperr(unsigned long);
extern int pgwin32_putenv(const char *); extern int pgwin32_putenv(const char *);
extern void pgwin32_unsetenv(const char *); extern void pgwin32_unsetenv(const char *);
/* in port/win32security.c */
extern int pgwin32_is_service(void);
extern int pgwin32_is_admin(void);
#define putenv(x) pgwin32_putenv(x) #define putenv(x) pgwin32_putenv(x)
#define unsetenv(x) pgwin32_unsetenv(x) #define unsetenv(x) pgwin32_unsetenv(x)

View File

@ -1,22 +1,47 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* security.c * win32security.c
* Microsoft Windows Win32 Security Support Functions * Microsoft Windows Win32 Security Support Functions
* *
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* src/backend/port/win32/security.c * src/port/win32security.c
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef FRONTEND
#include "postgres.h" #include "postgres.h"
#else
#include "postgres_fe.h"
#endif
static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token, static BOOL pgwin32_get_dynamic_tokeninfo(HANDLE token,
TOKEN_INFORMATION_CLASS class, char **InfoBuffer, TOKEN_INFORMATION_CLASS class,
char *errbuf, int errsize); char **InfoBuffer, char *errbuf, int errsize);
/*
* Utility wrapper for frontend and backend when reporting an error
* message.
*/
static
pg_attribute_printf(1, 2)
void
log_error(const char *fmt,...)
{
va_list ap;
va_start(fmt, ap);
#ifndef FRONTEND
write_stderr(fmt, ap);
#else
fprintf(stderr, fmt, ap);
#endif
va_end(ap);
}
/* /*
* Returns nonzero if the current user has administrative privileges, * Returns nonzero if the current user has administrative privileges,
@ -40,7 +65,7 @@ pgwin32_is_admin(void)
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken)) if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &AccessToken))
{ {
write_stderr("could not open process token: error code %lu\n", log_error("could not open process token: error code %lu\n",
GetLastError()); GetLastError());
exit(1); exit(1);
} }
@ -48,7 +73,7 @@ pgwin32_is_admin(void)
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups,
&InfoBuffer, errbuf, sizeof(errbuf))) &InfoBuffer, errbuf, sizeof(errbuf)))
{ {
write_stderr("%s", errbuf); log_error("%s", errbuf);
exit(1); exit(1);
} }
@ -57,19 +82,21 @@ pgwin32_is_admin(void)
CloseHandle(AccessToken); CloseHandle(AccessToken);
if (!AllocateAndInitializeSid(&NtAuthority, 2, if (!AllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
0, &AdministratorsSid)) 0, &AdministratorsSid))
{ {
write_stderr("could not get SID for Administrators group: error code %lu\n", log_error("could not get SID for Administrators group: error code %lu\n",
GetLastError()); GetLastError());
exit(1); exit(1);
} }
if (!AllocateAndInitializeSid(&NtAuthority, 2, if (!AllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
0, &PowerUsersSid)) 0, &PowerUsersSid))
{ {
write_stderr("could not get SID for PowerUsers group: error code %lu\n", log_error("could not get SID for PowerUsers group: error code %lu\n",
GetLastError()); GetLastError());
exit(1); exit(1);
} }
@ -78,8 +105,10 @@ pgwin32_is_admin(void)
for (x = 0; x < Groups->GroupCount; x++) for (x = 0; x < Groups->GroupCount; x++)
{ {
if ((EqualSid(AdministratorsSid, Groups->Groups[x].Sid) && (Groups->Groups[x].Attributes & SE_GROUP_ENABLED)) || if ((EqualSid(AdministratorsSid, Groups->Groups[x].Sid) &&
(EqualSid(PowerUsersSid, Groups->Groups[x].Sid) && (Groups->Groups[x].Attributes & SE_GROUP_ENABLED))) (Groups->Groups[x].Attributes & SE_GROUP_ENABLED)) ||
(EqualSid(PowerUsersSid, Groups->Groups[x].Sid) &&
(Groups->Groups[x].Attributes & SE_GROUP_ENABLED)))
{ {
success = TRUE; success = TRUE;
break; break;
@ -105,9 +134,10 @@ pgwin32_is_admin(void)
* 1 = Service * 1 = Service
* -1 = Error * -1 = Error
* *
* Note: we can't report errors via either ereport (we're called too early) * Note: we can't report errors via either ereport (we're called too early
* or write_stderr (because that calls this). We are therefore reduced to * in the backend) or write_stderr (because that calls this). We are
* writing directly on stderr, which sucks, but we have few alternatives. * therefore reduced to writing directly on stderr, which sucks, but we
* have few alternatives.
*/ */
int int
pgwin32_is_service(void) pgwin32_is_service(void)
@ -217,13 +247,15 @@ pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize)) if (GetTokenInformation(token, class, NULL, 0, &InfoBufferSize))
{ {
snprintf(errbuf, errsize, "could not get token information: got zero size\n"); snprintf(errbuf, errsize,
"could not get token information: got zero size\n");
return FALSE; return FALSE;
} }
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{ {
snprintf(errbuf, errsize, "could not get token information: error code %lu\n", snprintf(errbuf, errsize,
"could not get token information: error code %lu\n",
GetLastError()); GetLastError());
return FALSE; return FALSE;
} }
@ -231,7 +263,8 @@ pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
*InfoBuffer = malloc(InfoBufferSize); *InfoBuffer = malloc(InfoBufferSize);
if (*InfoBuffer == NULL) if (*InfoBuffer == NULL)
{ {
snprintf(errbuf, errsize, "could not allocate %d bytes for token information\n", snprintf(errbuf, errsize,
"could not allocate %d bytes for token information\n",
(int) InfoBufferSize); (int) InfoBufferSize);
return FALSE; return FALSE;
} }
@ -239,7 +272,8 @@ pgwin32_get_dynamic_tokeninfo(HANDLE token, TOKEN_INFORMATION_CLASS class,
if (!GetTokenInformation(token, class, *InfoBuffer, if (!GetTokenInformation(token, class, *InfoBuffer,
InfoBufferSize, &InfoBufferSize)) InfoBufferSize, &InfoBufferSize))
{ {
snprintf(errbuf, errsize, "could not get token information: error code %lu\n", snprintf(errbuf, errsize,
"could not get token information: error code %lu\n",
GetLastError()); GetLastError());
return FALSE; return FALSE;
} }

View File

@ -90,7 +90,7 @@ sub mkvcbuild
pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c
mkdtemp.c qsort.c qsort_arg.c quotes.c system.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
win32env.c win32error.c win32setlocale.c); win32env.c win32error.c win32security.c win32setlocale.c);
push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00'); push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');