Skip to content
Snippets Groups Projects
elog.c 36 KiB
Newer Older
/*-------------------------------------------------------------------------
 *
 *	  error logging and reporting
 *
 * Some notes about recursion and errors during error processing:
 *
 * We need to be robust about recursive-error scenarios --- for example,
 * if we run out of memory, it's important to be able to report that fact.
 * There are a number of considerations that go into this.
 *
Bruce Momjian's avatar
Bruce Momjian committed
 * First, distinguish between re-entrant use and actual recursion.	It
 * is possible for an error or warning message to be emitted while the
Bruce Momjian's avatar
Bruce Momjian committed
 * parameters for an error message are being computed.	In this case
 * errstart has been called for the outer message, and some field values
Bruce Momjian's avatar
Bruce Momjian committed
 * may have already been saved, but we are not actually recursing.	We handle
 * this by providing a (small) stack of ErrorData records.	The inner message
 * can be computed and sent without disturbing the state of the outer message.
 * (If the inner message is actually an error, this isn't very interesting
 * because control won't come back to the outer message generator ... but
 * if the inner message is only debug or log data, this is critical.)
 *
 * Second, actual recursion will occur if an error is reported by one of
Bruce Momjian's avatar
Bruce Momjian committed
 * the elog.c routines or something they call.	By far the most probable
 * scenario of this sort is "out of memory"; and it's also the nastiest
 * to handle because we'd likely also run out of memory while trying to
 * report this error!  Our escape hatch for this condition is to force any
 * such messages up to ERROR level if they aren't already (so that we will
 * not need to return to the outer elog.c call), and to reset the ErrorContext
 * to empty before trying to process the inner message.  Since ErrorContext
 * is guaranteed to have at least 8K of space in it (see mcxt.c), we should
 * be able to process an "out of memory" message successfully.
 *
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
Bruce Momjian's avatar
Bruce Momjian committed
 * Portions Copyright (c) 1994, Regents of the University of California
 *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.121 2003/08/27 00:33:34 petere Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <ctype.h>
#ifdef HAVE_SYSLOG
Marc G. Fournier's avatar
 
Marc G. Fournier committed
#endif

#include "libpq/libpq.h"
#include "libpq/pqformat.h"
Marc G. Fournier's avatar
 
Marc G. Fournier committed


/* Global variables */
ErrorContextCallback *error_context_stack = NULL;

/* GUC parameters */
PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
Bruce Momjian's avatar
Bruce Momjian committed
bool		Log_timestamp = false;		/* show timestamps in stderr
										 * output */
bool		Log_pid = false;	/* show PIDs in stderr output */
#ifdef HAVE_SYSLOG
Marc G. Fournier's avatar
 
Marc G. Fournier committed
/*
 * 0 = only stdout/stderr
 * 1 = stdout+stderr and syslog
 * 2 = syslog only
 * ... in theory anyway
Marc G. Fournier's avatar
 
Marc G. Fournier committed
 */
int			Use_syslog = 0;
char	   *Syslog_facility;	/* openlog() parameters */
char	   *Syslog_ident;

static void write_syslog(int level, const char *line);

#else
#define Use_syslog 0
Bruce Momjian's avatar
Bruce Momjian committed
#endif   /* HAVE_SYSLOG */
/*
 * ErrorData holds the data accumulated during any one ereport() cycle.
 * Any non-NULL pointers must point to palloc'd data in ErrorContext.
 * (The const pointers are an exception; we assume they point at non-freeable
 * constant strings.)
 */
typedef struct ErrorData
{
	int			elevel;			/* error level */
Bruce Momjian's avatar
Bruce Momjian committed
	bool		output_to_server;		/* will report to server log? */
	bool		output_to_client;		/* will report to client? */
	bool		show_funcname;	/* true to force funcname inclusion */
	const char *filename;		/* __FILE__ of ereport() call */
	int			lineno;			/* __LINE__ of ereport() call */
	const char *funcname;		/* __func__ of ereport() call */
	int			sqlerrcode;		/* encoded ERRSTATE */
	char	   *message;		/* primary error message */
	char	   *detail;			/* detail error message */
	char	   *hint;			/* hint message */
	char	   *context;		/* context message */
	int			cursorpos;		/* cursor index into query string */
	int			saved_errno;	/* errno at entry */

/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE  5

static ErrorData errordata[ERRORDATA_STACK_SIZE];

static int	errordata_stack_depth = -1; /* index of topmost active frame */

Bruce Momjian's avatar
Bruce Momjian committed
static int	recursion_depth = 0;	/* to detect actual recursion */


/* Macro for checking errordata_stack_depth is reasonable */
#define CHECK_STACK_DEPTH() \
	do { \
		if (errordata_stack_depth < 0) \
		{ \
			errordata_stack_depth = -1; \
			ereport(ERROR, (errmsg_internal("errstart was not called"))); \
		} \
	} while (0)


static void send_message_to_server_log(ErrorData *edata);
static void send_message_to_frontend(ErrorData *edata);
static char *expand_fmt_string(const char *fmt, ErrorData *edata);
static const char *useful_strerror(int errnum);
static const char *error_severity(int elevel);
static const char *print_timestamp(void);
static const char *print_pid(void);
/*
 * errstart --- begin an error-reporting cycle
 * Create a stack entry and store the given parameters in it.  Subsequently,
 * errmsg() and perhaps other routines will be called to further populate
 * the stack entry.  Finally, errfinish() will be called to actually process
 * the error report.
 * Returns TRUE in normal case.  Returns FALSE to short-circuit the error
 * report (if it's a warning or lower and not to be reported anywhere).
bool
errstart(int elevel, const char *filename, int lineno,
		 const char *funcname)
	bool		output_to_server = false;
	bool		output_to_client = false;
Bruce Momjian's avatar
Bruce Momjian committed
	 * First decide whether we need to process this report at all; if it's
	 * warning or less and not enabled for logging, just return FALSE
	 * without starting up any error logging machinery.
	 * Convert initialization errors into fatal errors. This is probably
	 * redundant, because Warn_restart_ready won't be set anyway.
	if (elevel == ERROR && IsInitProcessingMode())
		elevel = FATAL;
	 * If we are inside a critical section, all errors become PANIC
	/* Determine whether message is enabled for server log output */
		/* Complicated because LOG is sorted out-of-order for this purpose */
		if (elevel == LOG || elevel == COMMERROR)
			if (log_min_messages == LOG)
				output_to_server = true;
			else if (log_min_messages < FATAL)
		else
		{
			/* elevel != LOG */
			if (log_min_messages == LOG)
			{
				if (elevel >= FATAL)
					output_to_server = true;
			}
			/* Neither is LOG */
			else if (elevel >= log_min_messages)
				output_to_server = true;
		}
	}
	else
	{
		/* In bootstrap/standalone case, do not sort LOG out-of-order */
		output_to_server = (elevel >= log_min_messages);
	}

	/* Determine whether message is enabled for client output */
	if (whereToSendOutput == Remote && elevel != COMMERROR)
	{
		/*
		 * client_min_messages is honored only after we complete the
		 * authentication handshake.  This is required both for security
		 * reasons and because many clients can't handle NOTICE messages
		 * during authentication.
		 */
		if (ClientAuthInProgress)
			output_to_client = (elevel >= ERROR);
			output_to_client = (elevel >= client_min_messages ||
								elevel == INFO);
	/* Skip processing effort if non-error message will not be output */
	if (elevel < ERROR && !output_to_server && !output_to_client)
		return false;
	 * Okay, crank up a stack entry to store the info in.
	if (recursion_depth++ > 0)
Bruce Momjian's avatar
Bruce Momjian committed
		 * Ooops, error during error processing.  Clear ErrorContext and
		 * force level up to ERROR or greater, as discussed at top of
		 * file.  Adjust output decisions too.
		 */
		MemoryContextReset(ErrorContext);
		output_to_server = true;
		if (whereToSendOutput == Remote && elevel != COMMERROR)
			output_to_client = true;
		elevel = Max(elevel, ERROR);
Bruce Momjian's avatar
Bruce Momjian committed

		/*
		 * If we recurse more than once, the problem might be something
		 * broken in a context traceback routine.  Abandon them too.
		if (recursion_depth > 2)
			error_context_stack = NULL;
	if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
		/* Wups, stack not big enough */
Bruce Momjian's avatar
Bruce Momjian committed
		int			i;
		elevel = Max(elevel, ERROR);
Bruce Momjian's avatar
Bruce Momjian committed

		/*
		 * Don't forget any FATAL/PANIC status on the stack (see comments
		 * in errfinish)
		 */
		for (i = 0; i < errordata_stack_depth; i++)
			elevel = Max(elevel, errordata[i].elevel);
		/* Clear the stack and try again */
		errordata_stack_depth = -1;
		ereport(elevel, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
	/* Initialize data for this error frame */
	edata = &errordata[errordata_stack_depth];
	MemSet(edata, 0, sizeof(ErrorData));
	edata->elevel = elevel;
	edata->output_to_server = output_to_server;
	edata->output_to_client = output_to_client;
	edata->filename = filename;
	edata->lineno = lineno;
	edata->funcname = funcname;
	/* Select default errcode based on elevel */
	if (elevel >= ERROR)
		edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
	else if (elevel == WARNING)
		edata->sqlerrcode = ERRCODE_WARNING;
	else
		edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
	/* errno is saved here so that error parameter eval can't change it */
	edata->saved_errno = errno;

	recursion_depth--;
	return true;
}
/*
 * errfinish --- end an error-reporting cycle
 *
 * Produce the appropriate error report(s) and pop the error stack.
 *
 * If elevel is ERROR or worse, control does not return to the caller.
 * See elog.h for the error level definitions.
 */
void
Bruce Momjian's avatar
Bruce Momjian committed
errfinish(int dummy,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	int			elevel = edata->elevel;
	MemoryContext oldcontext;
	ErrorContextCallback *econtext;
Bruce Momjian's avatar
Bruce Momjian committed
	 * Do processing in ErrorContext, which we hope has enough reserved
	 * space to report an error.
	 */
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	/*
	 * Call any context callback functions.  Errors occurring in callback
	 * functions will be treated as recursive errors --- this ensures we
	 * will avoid infinite recursion (see errstart).
	 */
	for (econtext = error_context_stack;
		 econtext != NULL;
		 econtext = econtext->previous)
		(*econtext->callback) (econtext->arg);
Bruce Momjian's avatar
Bruce Momjian committed

	/* Send to server log, if enabled */
	if (edata->output_to_server)
		send_message_to_server_log(edata);
	 * Abort any old-style COPY OUT in progress when an error is detected.
	 * This hack is necessary because of poor design of old-style copy
	 * protocol.  Note we must do this even if client is fool enough to
	 * have set client_min_messages above ERROR, so don't look at
	 * output_to_client.
	if (elevel >= ERROR && whereToSendOutput == Remote)
		pq_endcopyout(true);
Marc G. Fournier's avatar
 
Marc G. Fournier committed

	/* Send to client, if enabled */
	if (edata->output_to_client)
		send_message_to_frontend(edata);
	/* Now free up subsidiary data attached to stack entry, and release it */
	if (edata->message)
		pfree(edata->message);
	if (edata->detail)
		pfree(edata->detail);
	if (edata->hint)
		pfree(edata->hint);
	if (edata->context)
		pfree(edata->context);
Marc G. Fournier's avatar
 
Marc G. Fournier committed

	MemoryContextSwitchTo(oldcontext);
	errordata_stack_depth--;
	recursion_depth--;
	/*
	 * If the error level is ERROR or more, we are not going to return to
Bruce Momjian's avatar
Bruce Momjian committed
	 * caller; therefore, if there is any stacked error already in
	 * progress it will be lost.  This is more or less okay, except we do
	 * not want to have a FATAL or PANIC error downgraded because the
	 * reporting process was interrupted by a lower-grade error.  So check
	 * the stack and make sure we panic if panic is warranted.
Bruce Momjian's avatar
Bruce Momjian committed
		int			i;

		for (i = 0; i <= errordata_stack_depth; i++)
			elevel = Max(elevel, errordata[i].elevel);
		 * Also, be sure to reset the stack to empty.  We do not clear
		 * ErrorContext here, though; PostgresMain does that later on.
		errordata_stack_depth = -1;
		recursion_depth = 0;
		error_context_stack = NULL;
	 * Perform error recovery action as specified by elevel.
	if (elevel == ERROR || elevel == FATAL)
		/* Prevent immediate interrupt while entering error recovery */
		ImmediateInterruptOK = false;
		/*
		 * If we just reported a startup failure, the client will
Bruce Momjian's avatar
Bruce Momjian committed
		 * disconnect on receiving it, so don't send any more to the
		 * client.
		 */
		if (!Warn_restart_ready && whereToSendOutput == Remote)
			whereToSendOutput = None;

		 * For a FATAL error, we let proc_exit clean up and exit.
		 *
Bruce Momjian's avatar
Bruce Momjian committed
		 * There are several other cases in which we treat ERROR as FATAL and
		 * go directly to proc_exit:
		 *
		 * 1. ExitOnAnyError mode switch is set (initdb uses this).
Bruce Momjian's avatar
Bruce Momjian committed
		 *
		 * 2. we have not yet entered the main backend loop (ie, we are in
Bruce Momjian's avatar
Bruce Momjian committed
		 * the postmaster or in backend startup); we have noplace to
		 * recover.
Bruce Momjian's avatar
Bruce Momjian committed
		 * 3. the error occurred after proc_exit has begun to run.	(It's
		 * proc_exit's responsibility to see that this doesn't turn into
		 * infinite recursion!)
		 *
		 * In the last case, we exit with nonzero exit code to indicate that
Bruce Momjian's avatar
Bruce Momjian committed
		 * something's pretty wrong.  We also want to exit with nonzero
		 * exit code if not running under the postmaster (for example, if
		 * we are being run from the initdb script, we'd better return an
		 * error status).
		if (elevel == FATAL ||
			ExitOnAnyError ||
			!Warn_restart_ready ||
			proc_exit_inprogress)
			/*
			 * fflush here is just to improve the odds that we get to see
			 * the error message, in case things are so hosed that
			 * proc_exit crashes.  Any other code you might be tempted to
			 * add here should probably be in an on_proc_exit callback
			 * instead.
			proc_exit(proc_exit_inprogress || !IsUnderPostmaster);
		 * Guard against infinite loop from errors during error recovery.
			ereport(PANIC, (errmsg("error during error recovery, giving up")));
		 * Otherwise we can return to the main loop in postgres.c.
		siglongjmp(Warn_restart, 1);
		 * Serious crash time. Postmaster will observe nonzero process
		 * exit status and kill the other backends too.
Bruce Momjian's avatar
Bruce Momjian committed
		 * XXX: what if we are *in* the postmaster?  abort() won't kill our
		 * children...
		ImmediateInterruptOK = false;
		fflush(stdout);
		fflush(stderr);
	/* We reach here if elevel <= WARNING. OK to return to caller. */
}


/*
 * errcode --- add SQLSTATE error code to the current error
 *
 * The code is expected to be represented as per MAKE_SQLSTATE().
 */
int
errcode(int sqlerrcode)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];

	/* we don't bother incrementing recursion_depth */
	CHECK_STACK_DEPTH();

	edata->sqlerrcode = sqlerrcode;

	return 0;					/* return value does not matter */
}


/*
 * errcode_for_file_access --- add SQLSTATE error code to the current error
 *
Bruce Momjian's avatar
Bruce Momjian committed
 * The SQLSTATE code is chosen based on the saved errno value.	We assume
 * that the failing operation was some type of disk file access.
 *
 * NOTE: the primary error message string should generally include %m
 * when this is used.
 */
int
errcode_for_file_access(void)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];

	/* we don't bother incrementing recursion_depth */
	CHECK_STACK_DEPTH();

	switch (edata->saved_errno)
	{
Bruce Momjian's avatar
Bruce Momjian committed
			/* Permission-denied failures */
		case EPERM:				/* Not super-user */
		case EACCES:			/* Permission denied */
#ifdef EROFS
		case EROFS:				/* Read only file system */
#endif
			edata->sqlerrcode = ERRCODE_INSUFFICIENT_PRIVILEGE;
			break;

		case ENOENT:			/* No such file or directory */
			edata->sqlerrcode = ERRCODE_UNDEFINED_FILE;
		case EEXIST:			/* File exists */
			edata->sqlerrcode = ERRCODE_DUPLICATE_FILE;
Bruce Momjian's avatar
Bruce Momjian committed
			/* Wrong object type or state */
		case ENOTDIR:			/* Not a directory */
		case EISDIR:			/* Is a directory */
		case ENOTEMPTY:			/* Directory not empty */
			edata->sqlerrcode = ERRCODE_WRONG_OBJECT_TYPE;
			break;

Bruce Momjian's avatar
Bruce Momjian committed
			/* Insufficient resources */
		case ENOSPC:			/* No space left on device */
			edata->sqlerrcode = ERRCODE_DISK_FULL;
			break;

		case ENFILE:			/* File table overflow */
		case EMFILE:			/* Too many open files */
			edata->sqlerrcode = ERRCODE_INSUFFICIENT_RESOURCES;
			break;

Bruce Momjian's avatar
Bruce Momjian committed
			/* Hardware failure */
		case EIO:				/* I/O error */
			edata->sqlerrcode = ERRCODE_IO_ERROR;
			break;

Bruce Momjian's avatar
Bruce Momjian committed
			/* All else is classified as internal errors */
		default:
			edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
			break;
	}

	return 0;					/* return value does not matter */
}

/*
 * errcode_for_socket_access --- add SQLSTATE error code to the current error
 *
Bruce Momjian's avatar
Bruce Momjian committed
 * The SQLSTATE code is chosen based on the saved errno value.	We assume
 * that the failing operation was some type of socket access.
 *
 * NOTE: the primary error message string should generally include %m
 * when this is used.
 */
int
errcode_for_socket_access(void)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];

	/* we don't bother incrementing recursion_depth */
	CHECK_STACK_DEPTH();

	switch (edata->saved_errno)
	{
Bruce Momjian's avatar
Bruce Momjian committed
			/* Loss of connection */
		case EPIPE:
#ifdef ECONNRESET
		case ECONNRESET:
#endif
			edata->sqlerrcode = ERRCODE_CONNECTION_FAILURE;
			break;

Bruce Momjian's avatar
Bruce Momjian committed
			/* All else is classified as internal errors */
		default:
			edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
			break;
	}

	return 0;					/* return value does not matter */
}

/*
 * This macro handles expansion of a format string and associated parameters;
 * it's common code for errmsg(), errdetail(), etc.  Must be called inside
 * a routine that is declared like "const char *fmt, ..." and has an edata
Bruce Momjian's avatar
Bruce Momjian committed
 * pointer set up.	The message is assigned to edata->targetfield, or
 * appended to it if appendval is true.
 *
 * Note: we pstrdup the buffer rather than just transferring its storage
 * to the edata field because the buffer might be considerably larger than
 * really necessary.
 */
#define EVALUATE_MESSAGE(targetfield, appendval)  \
	{ \
		char		   *fmtbuf; \
		StringInfoData	buf; \
		/* Internationalize the error format string */ \
		fmt = gettext(fmt); \
		/* Expand %m in format string */ \
		fmtbuf = expand_fmt_string(fmt, edata); \
		initStringInfo(&buf); \
		if ((appendval) && edata->targetfield) \
			appendStringInfo(&buf, "%s\n", edata->targetfield); \
		/* Generate actual output --- have to use appendStringInfoVA */ \
		for (;;) \
		{ \
			va_list		args; \
			bool		success; \
			va_start(args, fmt); \
			success = appendStringInfoVA(&buf, fmtbuf, args); \
			va_end(args); \
			if (success) \
				break; \
			enlargeStringInfo(&buf, buf.maxlen); \
		} \
		/* Done with expanded fmt */ \
		pfree(fmtbuf); \
		/* Save the completed message into the stack item */ \
		if (edata->targetfield) \
			pfree(edata->targetfield); \
		edata->targetfield = pstrdup(buf.data); \
		pfree(buf.data); \
	}


/*
 * errmsg --- add a primary error message text to the current error
 *
 * In addition to the usual %-escapes recognized by printf, "%m" in
 * fmt is replaced by the error message for the caller's value of errno.
 *
 * Note: no newline is needed at the end of the fmt string, since
 * ereport will provide one for the output methods that need it.
 */
int
Bruce Momjian's avatar
Bruce Momjian committed
errmsg(const char *fmt,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	recursion_depth++;
	CHECK_STACK_DEPTH();
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	EVALUATE_MESSAGE(message, false);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;
	return 0;					/* return value does not matter */
}


/*
 * errmsg_internal --- add a primary error message text to the current error
 *
 * This is exactly like errmsg() except that strings passed to errmsg_internal
 * are customarily left out of the internationalization message dictionary.
 * This should be used for "can't happen" cases that are probably not worth
 * spending translation effort on.
 */
int
Bruce Momjian's avatar
Bruce Momjian committed
errmsg_internal(const char *fmt,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	recursion_depth++;
	CHECK_STACK_DEPTH();
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	EVALUATE_MESSAGE(message, false);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;
	return 0;					/* return value does not matter */
}


/*
 * errdetail --- add a detail error message text to the current error
 */
int
Bruce Momjian's avatar
Bruce Momjian committed
errdetail(const char *fmt,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	recursion_depth++;
	CHECK_STACK_DEPTH();
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	EVALUATE_MESSAGE(detail, false);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;
	return 0;					/* return value does not matter */
/*
 * errhint --- add a hint error message text to the current error
 */
Bruce Momjian's avatar
Bruce Momjian committed
errhint(const char *fmt,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	recursion_depth++;
	CHECK_STACK_DEPTH();
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	EVALUATE_MESSAGE(hint, false);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;
	return 0;					/* return value does not matter */
}


/*
 * errcontext --- add a context error message text to the current error
 *
 * Unlike other cases, multiple calls are allowed to build up a stack of
 * context information.  We assume earlier calls represent more-closely-nested
 * states.
 */
int
Bruce Momjian's avatar
Bruce Momjian committed
errcontext(const char *fmt,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	recursion_depth++;
	CHECK_STACK_DEPTH();
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	EVALUATE_MESSAGE(context, true);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;
	return 0;					/* return value does not matter */
}


/*
 * errfunction --- add reporting function name to the current error
 *
 * This is used when backwards compatibility demands that the function
 * name appear in messages sent to old-protocol clients.  Note that the
 * passed string is expected to be a non-freeable constant string.
 */
int
errfunction(const char *funcname)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];

	/* we don't bother incrementing recursion_depth */
	CHECK_STACK_DEPTH();

	edata->funcname = funcname;
	edata->show_funcname = true;

	return 0;					/* return value does not matter */
}

/*
 * errposition --- add cursor position to the current error
 */
int
errposition(int cursorpos)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];

	/* we don't bother incrementing recursion_depth */
	CHECK_STACK_DEPTH();

	edata->cursorpos = cursorpos;

	return 0;					/* return value does not matter */
}


/*
 * elog_finish --- finish up for old-style API
 *
 * The elog() macro already called errstart, but with ERROR rather than
 * the true elevel.
 */
void
Bruce Momjian's avatar
Bruce Momjian committed
elog_finish(int elevel, const char *fmt,...)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	CHECK_STACK_DEPTH();

	/*
	 * We need to redo errstart() because the elog macro had to call it
	 * with bogus elevel.
	 */
	errordata_stack_depth--;
	errno = edata->saved_errno;
	if (!errstart(elevel, edata->filename, edata->lineno, edata->funcname))
		return;					/* nothing to do */

	/*
	 * Format error message just like errmsg().
	 */
	recursion_depth++;
	oldcontext = MemoryContextSwitchTo(ErrorContext);

	EVALUATE_MESSAGE(message, false);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;

	/*
	 * And let errfinish() finish up.
	 */
	errfinish(0);
}


/*
 * Initialization of error output file
 */
void
		/*
		 * A debug-output file name was given.
		 *
		 * Make sure we can write the file, and find out if it's a tty.
		 */
		if ((fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY,
					   0666)) < 0)
					(errcode_for_file_access(),
Bruce Momjian's avatar
Bruce Momjian committed
				   errmsg("failed to open \"%s\": %m", OutputFileName)));
		/*
		 * Redirect our stderr to the debug output file.
		 */
		if (!freopen(OutputFileName, "a", stderr))
					(errcode_for_file_access(),
					 errmsg("failed to reopen \"%s\" as stderr: %m",
		/*
		 * If the file is a tty and we're running under the postmaster,
		 * try to send stdout there as well (if it isn't a tty then stderr
		 * will block out stdout, so we may as well let stdout go wherever
		 * it was going before).
		 */
		if (istty && IsUnderPostmaster)
			if (!freopen(OutputFileName, "a", stdout))
						(errcode_for_file_access(),
						 errmsg("failed to reopen \"%s\" as stdout: %m",
#ifdef HAVE_SYSLOG
#define PG_SYSLOG_LIMIT 128
/*
 * Write a message line to syslog if the syslog option is set.
 *
 * Our problem here is that many syslog implementations don't handle
 * long messages in an acceptable manner. While this function doesn't
 * help that fact, it does work around by splitting up messages into
 * smaller pieces.
 */
static void
write_syslog(int level, const char *line)
{
	static bool openlog_done = false;
	static unsigned long seq = 0;
	static int	syslog_fac = LOG_LOCAL0;
	int			len = strlen(line);

	if (Use_syslog == 0)
		return;

	if (!openlog_done)
	{
		if (strcasecmp(Syslog_facility, "LOCAL0") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL1") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL2") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL3") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL4") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL5") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL6") == 0)
		if (strcasecmp(Syslog_facility, "LOCAL7") == 0)
		openlog(Syslog_ident, LOG_PID | LOG_NDELAY, syslog_fac);
		openlog_done = true;
	}

	/*
	 * We add a sequence number to each log message to suppress "same"
	 * messages.
	 */
	seq++;

	/* divide into multiple syslog() calls if message is too long */
	/* or if the message contains embedded NewLine(s) '\n' */
	if (len > PG_SYSLOG_LIMIT || strchr(line, '\n') != NULL)
		int			chunk_nr = 0;
			char		buf[PG_SYSLOG_LIMIT + 1];
			int			buflen;
			int			i;
			/* if we start at a newline, move ahead one char */
			if (line[0] == '\n')
			{
				line++;
				len--;

			strncpy(buf, line, PG_SYSLOG_LIMIT);
			buf[PG_SYSLOG_LIMIT] = '\0';
			if (strchr(buf, '\n') != NULL)
				*strchr(buf, '\n') = '\0';
			/* trim to multibyte letter boundary */
			buflen = pg_mbcliplen(buf, buflen, buflen);
			/* already word boundary? */
			if (!isspace((unsigned char) line[buflen]) &&
				line[buflen] != '\0')
				/* try to divide at word boundary */
				while (i > 0 && !isspace((unsigned char) buf[i]))
				if (i > 0)		/* else couldn't divide word boundary */