* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.184 2004/06/06 00:41:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.185 2004/06/24 21:02:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
if (!potential_DataDir)
{
- fprintf(stderr,
- gettext("%s does not know where to find the database system data.\n"
- "You must specify the directory that contains the database system\n"
- "either by specifying the -D invocation option or by setting the\n"
- "PGDATA environment variable.\n"),
- argv[0]);
+ write_stderr("%s does not know where to find the database system data.\n"
+ "You must specify the directory that contains the database system\n"
+ "either by specifying the -D invocation option or by setting the\n"
+ "PGDATA environment variable.\n",
+ argv[0]);
proc_exit(1);
}
SetDataDir(potential_DataDir);
static void
usage(void)
{
- fprintf(stderr,
- gettext("Usage:\n"
+ write_stderr("Usage:\n"
" postgres -boot [OPTION]... DBNAME\n"
" -c NAME=VALUE set run-time parameter\n"
" -d 1-5 debug level\n"
" -D datadir data directory\n"
" -F turn off fsync\n"
" -o file send debug output to file\n"
- " -x num internal use\n"));
+ " -x num internal use\n");
proc_exit(1);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/main/main.c,v 1.86 2004/06/03 00:07:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/main/main.c,v 1.87 2004/06/24 21:02:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#if defined(__alpha) /* no __alpha__ ? */
if (setsysinfo(SSI_NVPAIRS, buffer, 1, (caddr_t) NULL,
(unsigned long) NULL) < 0)
- fprintf(stderr, gettext("%s: setsysinfo failed: %s\n"),
- argv[0], strerror(errno));
+ write_stderr("%s: setsysinfo failed: %s\n",
+ argv[0], strerror(errno));
#endif
#endif /* NOFIXADE || NOPRINTADE */
err = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (err != 0)
{
- fprintf(stderr, "%s: WSAStartup failed: %d\n",
+ write_stderr("%s: WSAStartup failed: %d\n",
argv[0], err);
exit(1);
}
*/
if (geteuid() == 0)
{
- fprintf(stderr,
- gettext("\"root\" execution of the PostgreSQL server is not permitted.\n"
- "The server must be started under an unprivileged user ID to prevent\n"
- "possible system security compromise. See the documentation for\n"
- "more information on how to properly start the server.\n"
- ));
+ write_stderr("\"root\" execution of the PostgreSQL server is not permitted.\n"
+ "The server must be started under an unprivileged user ID to prevent\n"
+ "possible system security compromise. See the documentation for\n"
+ "more information on how to properly start the server.\n");
exit(1);
}
#endif /* !__BEOS__ */
*/
if (getuid() != geteuid())
{
- fprintf(stderr,
- gettext("%s: real and effective user IDs must match\n"),
- argv[0]);
+ write_stderr("%s: real and effective user IDs must match\n",
+ argv[0]);
+ exit(1);
+ }
+#else /* WIN32 */
+ if (pgwin32_is_admin())
+ {
+ write_stderr("execution of PostgreSQL by a user with administrative permissions is not permitted.\n"
+ "The server must be started under an unprivileged user ID to prevent\n"
+ "possible system security compromise. See the documentation for\n"
+ "more information on how to properly start the server.\n");
exit(1);
}
#endif /* !WIN32 */
pw = getpwuid(geteuid());
if (pw == NULL)
{
- fprintf(stderr, gettext("%s: invalid effective UID: %d\n"),
- argv[0], (int) geteuid());
+ write_stderr("%s: invalid effective UID: %d\n",
+ argv[0], (int) geteuid());
exit(1);
}
/* Allocate new memory because later getpwuid() calls can overwrite it */
pw_name_persist = malloc(namesize);
if (!GetUserName(pw_name_persist, &namesize))
{
- fprintf(stderr, gettext("%s: could not determine user name (GetUserName failed)\n"),
+ write_stderr("%s: could not determine user name (GetUserName failed)\n",
argv[0]);
exit(1);
}
-# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.12 2004/06/10 17:10:24 petere Exp $
+# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.13 2004/06/24 21:02:40 tgl Exp $
CATALOG_NAME := postgres
AVAIL_LANGUAGES := af cs de es hr hu it nb pt_BR ru sv tr zh_CN zh_TW
GETTEXT_FILES := + gettext-files
# you can add "elog:2" and "errmsg_internal" to this list if you want to
# include internal messages in the translation list.
-GETTEXT_TRIGGERS:= errmsg errdetail errhint errcontext postmaster_error yyerror
+GETTEXT_TRIGGERS:= errmsg errdetail errhint errcontext write_stderr yyerror
gettext-files: distprep
find $(srcdir)/ -name '*.c' -print >$@
# Makefile for port/win32
#
# IDENTIFICATION
-# $PostgreSQL: pgsql/src/backend/port/win32/Makefile,v 1.4 2004/04/12 16:19:18 momjian Exp $
+# $PostgreSQL: pgsql/src/backend/port/win32/Makefile,v 1.5 2004/06/24 21:02:42 tgl Exp $
#
#-------------------------------------------------------------------------
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = sema.o shmem.o timer.o socket.o signal.o
+OBJS = sema.o shmem.o timer.o socket.o signal.o security.o
all: SUBSYS.o
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * security.c
+ * Microsoft Windows Win32 Security Support Functions
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/port/win32/security.c,v 1.1 2004/06/24 21:02:42 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+
+/*
+ * Returns nonzero if the current user has administrative privileges,
+ * or zero if not.
+ *
+ * Note: this cannot use ereport() because it's called too early during
+ * startup.
+ */
+int
+pgwin32_is_admin(void)
+{
+ HANDLE AccessToken;
+ UCHAR InfoBuffer[1024];
+ PTOKEN_GROUPS Groups = (PTOKEN_GROUPS)InfoBuffer;
+ DWORD InfoBufferSize;
+ PSID AdministratorsSid;
+ PSID PowerUsersSid;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = { SECURITY_NT_AUTHORITY };
+ UINT x;
+ BOOL success;
+
+ if(!OpenProcessToken(GetCurrentProcess(),TOKEN_READ,&AccessToken))
+ {
+ write_stderr("failed to open process token: %d\n",
+ (int)GetLastError());
+ exit(1);
+ }
+
+ if (!GetTokenInformation(AccessToken,TokenGroups,InfoBuffer,
+ 1024, &InfoBufferSize))
+ {
+ write_stderr("failed to get token information: %d\n",
+ (int)GetLastError());
+ exit(1);
+ }
+
+ CloseHandle(AccessToken);
+
+ if(!AllocateAndInitializeSid(&NtAuthority, 2,
+ SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
+ 0,&AdministratorsSid))
+ {
+ write_stderr("failed to get SID for Administrators group: %d\n",
+ (int)GetLastError());
+ exit(1);
+ }
+
+ if (!AllocateAndInitializeSid(&NtAuthority, 2,
+ SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
+ 0, &PowerUsersSid))
+ {
+ write_stderr("failed to get SID for PowerUsers group: %d\n",
+ (int)GetLastError());
+ exit(1);
+ }
+
+ success = FALSE;
+
+ for (x=0; x<Groups->GroupCount; x++)
+ {
+ if (EqualSid(AdministratorsSid, Groups->Groups[x].Sid) ||
+ EqualSid(PowerUsersSid, Groups->Groups[x].Sid))
+ {
+ success = TRUE;
+ break;
+ }
+ }
+
+ FreeSid(AdministratorsSid);
+ FreeSid(PowerUsersSid);
+ return success;
+}
+
+/*
+ * We consider ourselves running as a service if one of the following is
+ * true:
+ *
+ * 1) We are running as Local System (only used by services)
+ * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
+ * process token by the SCM when starting a service)
+ *
+ * Return values:
+ * 0 = Not service
+ * 1 = Service
+ * -1 = Error
+ *
+ * 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
+ * writing directly on stderr, which sucks, but we have few alternatives.
+ */
+int
+pgwin32_is_service(void)
+{
+ static int _is_service = -1;
+ HANDLE AccessToken;
+ UCHAR InfoBuffer[1024];
+ PTOKEN_GROUPS Groups = (PTOKEN_GROUPS)InfoBuffer;
+ PTOKEN_USER User = (PTOKEN_USER)InfoBuffer;
+ DWORD InfoBufferSize;
+ PSID ServiceSid;
+ PSID LocalSystemSid;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = { SECURITY_NT_AUTHORITY };
+ UINT x;
+
+ /* Only check the first time */
+ if (_is_service != -1)
+ return _is_service;
+
+ if (!OpenProcessToken(GetCurrentProcess(),TOKEN_READ,&AccessToken)) {
+ fprintf(stderr,"failed to open process token: %d\n",
+ (int)GetLastError());
+ return -1;
+ }
+
+ /* First check for local system */
+ if (!GetTokenInformation(AccessToken,TokenUser,InfoBuffer,1024,&InfoBufferSize)) {
+ fprintf(stderr,"failed to get token information: %d\n",
+ (int)GetLastError());
+ return -1;
+ }
+
+ if (!AllocateAndInitializeSid(&NtAuthority,1,
+ SECURITY_LOCAL_SYSTEM_RID,0,0,0,0,0,0,0,
+ &LocalSystemSid)) {
+ fprintf(stderr,"failed to get SID for local system account\n");
+ CloseHandle(AccessToken);
+ return -1;
+ }
+
+ if (EqualSid(LocalSystemSid, User->User.Sid)) {
+ FreeSid(LocalSystemSid);
+ CloseHandle(AccessToken);
+ _is_service = 1;
+ return _is_service;
+ }
+
+ FreeSid(LocalSystemSid);
+
+ /* Now check for group SID */
+ if (!GetTokenInformation(AccessToken,TokenGroups,InfoBuffer,1024,&InfoBufferSize)) {
+ fprintf(stderr,"failed to get token information: %d\n",
+ (int)GetLastError());
+ return -1;
+ }
+
+ if (!AllocateAndInitializeSid(&NtAuthority,1,
+ SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0,
+ &ServiceSid)) {
+ fprintf(stderr,"failed to get SID for service group\n");
+ CloseHandle(AccessToken);
+ return -1;
+ }
+
+ _is_service = 0;
+ for (x = 0; x < Groups->GroupCount; x++)
+ {
+ if (EqualSid(ServiceSid, Groups->Groups[x].Sid))
+ {
+ _is_service = 1;
+ break;
+ }
+ }
+
+ FreeSid(ServiceSid);
+
+ CloseHandle(AccessToken);
+
+ return _is_service;
+}
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.3 2004/05/27 14:39:29 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.4 2004/06/24 21:02:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
pgwin32_signal_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (pgwin32_signal_event == NULL)
ereport(FATAL,
- (errmsg_internal("Failed to create signal event: %i!",(int)GetLastError())));
+ (errmsg_internal("failed to create signal event: %d", (int)GetLastError())));
/* Create thread for handling signals */
signal_thread_handle = CreateThread(NULL, 0, pg_signal_thread, NULL, 0, NULL);
if (signal_thread_handle == NULL)
ereport(FATAL,
- (errmsg_internal("Failed to create signal handler thread!")));
+ (errmsg_internal("failed to create signal handler thread")));
/* Create console control handle to pick up Ctrl-C etc */
if (!SetConsoleCtrlHandler(pg_console_handler, TRUE))
ereport(FATAL,
- (errmsg_internal("Failed to set console control handler!")));
+ (errmsg_internal("failed to set console control handler")));
}
char pipename[128];
HANDLE pipe = INVALID_HANDLE_VALUE;
- wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%i", GetCurrentProcessId());
+ wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%d", GetCurrentProcessId());
for (;;)
{
PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
if (pipe == INVALID_HANDLE_VALUE)
{
- fprintf(stderr, gettext("Failed to create signal listener pipe: %i. Retrying.\n"), (int) GetLastError());
+ write_stderr("failed to create signal listener pipe: %d. Retrying.\n", (int) GetLastError());
SleepEx(500, FALSE);
continue;
}
(LPTHREAD_START_ROUTINE) pg_signal_dispatch_thread,
(LPVOID) pipe, 0, NULL);
if (hThread == INVALID_HANDLE_VALUE)
- fprintf(stderr, gettext("Failed to create signal dispatch thread: %i\n"), (int) GetLastError());
+ write_stderr("failed to create signal dispatch thread: %d\n",
+ (int) GetLastError());
else
CloseHandle(hThread);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.404 2004/06/14 18:08:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.405 2004/06/24 21:02:55 tgl Exp $
*
* NOTES
*
* The Postmaster cleans up after backends if they have an emergency
* exit and/or core dump.
*
+ * Error Reporting:
+ * Use write_stderr() only for reporting "interactive" errors
+ * (essentially, bogus arguments on the command line). Once the
+ * postmaster is launched, use ereport(). In particular, don't use
+ * write_stderr() for anything that occurs after pmdaemonize.
+ *
*-------------------------------------------------------------------------
*/
static int CountChildren(void);
static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
static pid_t StartChildProcess(int xlop);
-static void
-postmaster_error(const char *fmt,...)
-/* This lets gcc check the format string for consistency. */
-__attribute__((format(printf, 1, 2)));
#ifdef EXEC_BACKEND
#ifdef USE_ASSERT_CHECKING
SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
#else
- postmaster_error("assert checking is not compiled in");
+ write_stderr("%s: assert checking is not compiled in\n", progname);
#endif
break;
case 'a':
}
default:
- fprintf(stderr,
- gettext("Try \"%s --help\" for more information.\n"),
- progname);
+ write_stderr("Try \"%s --help\" for more information.\n",
+ progname);
ExitPostmaster(1);
}
}
*/
if (optind < argc)
{
- postmaster_error("invalid argument: \"%s\"", argv[optind]);
- fprintf(stderr,
- gettext("Try \"%s --help\" for more information.\n"),
- progname);
+ write_stderr("%s: invalid argument: \"%s\"\n",
+ progname, argv[optind]);
+ write_stderr("Try \"%s --help\" for more information.\n",
+ progname);
ExitPostmaster(1);
}
* for lack of buffers. The specific choices here are somewhat
* arbitrary.
*/
- postmaster_error("the number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16");
+ write_stderr("%s: the number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16\n", progname);
ExitPostmaster(1);
}
if (ReservedBackends >= MaxBackends)
{
- postmaster_error("superuser_reserved_connections must be less than max_connections");
+ write_stderr("%s: superuser_reserved_connections must be less than max_connections\n", progname);
ExitPostmaster(1);
}
*/
if (!CheckDateTokenTables())
{
- postmaster_error("invalid datetoken tables, please fix");
+ write_stderr("%s: invalid datetoken tables, please fix\n", progname);
ExitPostmaster(1);
}
if (checkdir == NULL)
{
- fprintf(stderr,
- gettext("%s does not know where to find the database system data.\n"
- "You must specify the directory that contains the database system\n"
- "either by specifying the -D invocation option or by setting the\n"
- "PGDATA environment variable.\n"),
- progname);
+ write_stderr("%s does not know where to find the database system data.\n"
+ "You must specify the directory that contains the database system\n"
+ "either by specifying the -D invocation option or by setting the\n"
+ "PGDATA environment variable.\n",
+ progname);
ExitPostmaster(2);
}
fp = AllocateFile(path, PG_BINARY_R);
if (fp == NULL)
{
- fprintf(stderr,
- gettext("%s: could not find the database system\n"
- "Expected to find it in the directory \"%s\",\n"
- "but could not open file \"%s\": %s\n"),
- progname, checkdir, path, strerror(errno));
+ write_stderr("%s: could not find the database system\n"
+ "Expected to find it in the directory \"%s\",\n"
+ "but could not open file \"%s\": %s\n",
+ progname, checkdir, path, strerror(errno));
ExitPostmaster(2);
}
FreeFile(fp);
pid = fork();
if (pid == (pid_t) -1)
{
- postmaster_error("could not fork background process: %s",
- strerror(errno));
+ write_stderr("%s: could not fork background process: %s\n",
+ progname, strerror(errno));
ExitPostmaster(1);
}
else if (pid)
#ifdef HAVE_SETSID
if (setsid() < 0)
{
- postmaster_error("could not dissociate from controlling TTY: %s",
- strerror(errno));
+ write_stderr("%s: could not dissociate from controlling TTY: %s\n",
+ progname, strerror(errno));
ExitPostmaster(1);
}
#endif
return true;
}
-/*
- * This should be used only for reporting "interactive" errors (essentially,
- * bogus arguments on the command line). Once the postmaster is launched,
- * use ereport. In particular, don't use this for anything that occurs
- * after pmdaemonize.
- */
-static void
-postmaster_error(const char *fmt,...)
-{
- va_list ap;
-
- fprintf(stderr, "%s: ", progname);
- va_start(ap, fmt);
- vfprintf(stderr, gettext(fmt), ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-
#ifdef EXEC_BACKEND
if (r == WAIT_OBJECT_0)
pg_queue_signal(SIGCHLD);
else
- fprintf(stderr, "ERROR: failed to wait on child process handle: %d\n",
+ write_stderr("ERROR: failed to wait on child process handle: %d\n",
(int) GetLastError());
CloseHandle(procHandle);
return 0;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.420 2004/06/11 01:09:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.421 2004/06/24 21:03:08 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
{
if (!potential_DataDir)
{
- fprintf(stderr,
- gettext("%s does not know where to find the database system data.\n"
- "You must specify the directory that contains the database system\n"
- "either by specifying the -D invocation option or by setting the\n"
- "PGDATA environment variable.\n"),
- argv[0]);
+ write_stderr("%s does not know where to find the database system data.\n"
+ "You must specify the directory that contains the database system\n"
+ "either by specifying the -D invocation option or by setting the\n"
+ "PGDATA environment variable.\n",
+ argv[0]);
proc_exit(1);
}
SetDataDir(potential_DataDir);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/error/assert.c,v 1.26 2004/04/19 17:42:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/error/assert.c,v 1.27 2004/06/24 21:03:13 tgl Exp $
*
* NOTE
* This should eventually work with elog()
if (!PointerIsValid(conditionName)
|| !PointerIsValid(fileName)
|| !PointerIsValid(errorType))
- fprintf(stderr, "TRAP: ExceptionalCondition: bad arguments\n");
+ write_stderr("TRAP: ExceptionalCondition: bad arguments\n");
else
{
- fprintf(stderr, "TRAP: %s(\"%s\", File: \"%s\", Line: %d)\n",
+ write_stderr("TRAP: %s(\"%s\", File: \"%s\", Line: %d)\n",
errorType, conditionName,
fileName, lineNumber);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.141 2004/06/21 14:12:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.142 2004/06/24 21:03:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
appendStringInfoCharMacro(buf, '\t');
}
}
+
+
+/*
+ * Write errors to stderr (or by equal means when stderr is
+ * not available). Used before ereport/elog can be used
+ * safely (memory context, GUC load etc)
+ */
+void
+write_stderr(const char *fmt,...)
+{
+ va_list ap;
+
+ fmt = gettext(fmt);
+
+ va_start(ap, fmt);
+#ifndef WIN32
+ /* On Unix, we just fprintf to stderr */
+ vfprintf(stderr, fmt, ap);
+#else
+ /* On Win32, we print to stderr if running on a console, or write to
+ * eventlog if running as a service */
+ if (pgwin32_is_service()) /* Running as a service */
+ {
+ char errbuf[2048]; /* Arbitrary size? */
+
+ vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
+
+ write_eventlog(EVENTLOG_ERROR_TYPE, errbuf);
+ }
+ else /* Not running as service, write to stderr */
+ vfprintf(stderr, fmt, ap);
+#endif
+ va_end(ap);
+}
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/help_config.c,v 1.11 2004/06/02 18:09:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/help_config.c,v 1.12 2004/06/24 21:03:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
break;
default:
- fprintf(stderr, "internal error: unrecognized run-time parameter type\n");
+ write_stderr("internal error: unrecognized run-time parameter type\n");
break;
}
-/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.25 2004/05/27 14:39:33 momjian Exp $ */
+/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.26 2004/06/24 21:03:33 tgl Exp $ */
/* undefine and redefine after #include */
#undef mkdir
int pgwin32_send(SOCKET s, char* buf, int len, int flags);
const char *pgwin32_socket_strerror(int err);
+
+/* in backend/port/win32/security.c */
+extern int pgwin32_is_admin(void);
+extern int pgwin32_is_service(void);
#endif
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.68 2004/04/05 03:02:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.69 2004/06/24 21:03:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Other exported functions */
extern void DebugFileOpen(void);
+/*
+ * Write errors to stderr (or by equal means when stderr is
+ * not available). Used before ereport/elog can be used
+ * safely (memory context, GUC load etc)
+ */
+extern void write_stderr(const char *fmt,...)
+/* This extension allows gcc to check the format string for consistency with
+ the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
#endif /* ELOG_H */