#include "postgres.h"
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#ifdef HAVE_SYSLOG
#include <syslog.h>
#endif
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#include "access/transam.h"
#include "access/xact.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "postmaster/bgworker.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#undef _
#define _(x) err_gettext(x)
__thread ErrorContextCallback *error_context_stack = NULL;
__thread sigjmp_buf *PG_exception_stack = NULL;
extern bool redirection_done;
__thread emit_log_hook_type emit_log_hook = NULL;
#ifdef HAVE_SYSLOG
#ifndef PG_SYSLOG_LIMIT
#define PG_SYSLOG_LIMIT 900
#endif
static void write_syslog(int level, const char *line);
#endif
#ifdef WIN32
extern char *event_source;
static void write_eventlog(int level, const char *line, int len);
#endif
#define ERRORDATA_STACK_SIZE 5
static __thread ErrorData errordata[ERRORDATA_STACK_SIZE];
static __thread int errordata_stack_depth = -1;
static __thread int recursion_depth = 0;
#define FORMATTED_TS_LEN 128
#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 const char *err_gettext(const char *str) pg_attribute_format_arg(1);
static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
static void write_console(const char *line, int len);
static void setup_formatted_log_time(void);
static void setup_formatted_start_time(void);
static const char *process_log_prefix_padding(const char *p, int *padding);
static void log_line_prefix(StringInfo buf, ErrorData *edata);
static void write_csvlog(ErrorData *edata);
static void send_message_to_server_log(ErrorData *edata);
static void write_pipe_chunks(char *data, int len, int dest);
static void send_message_to_frontend(ErrorData *edata);
static const char *error_severity(int elevel);
static void append_with_tabs(StringInfo buf, const char *str);
static bool is_log_level_output(int elevel, int log_min_level);
bool
in_error_recursion_trouble(void)
{
return (recursion_depth > 2);
}
static inline const char *
err_gettext(const char *str)
{
#ifdef ENABLE_NLS
if (in_error_recursion_trouble())
return str;
else
return gettext(str);
#else
return str;
#endif
}
bool
errstart(int elevel, const char *domain)
{
ErrorData *edata;
bool output_to_server;
bool output_to_client = false;
int i;
if (elevel >= ERROR)
{
if (CritSectionCount > 0)
elevel = PANIC;
if (elevel == ERROR)
{
if (PG_exception_stack == NULL ||
ExitOnAnyError ||
proc_exit_inprogress)
elevel = FATAL;
}
for (i = 0; i <= errordata_stack_depth; i++)
elevel = Max(elevel, errordata[i].elevel);
}
output_to_server = is_log_level_output(elevel, log_min_messages);
if (whereToSendOutput == DestRemote && elevel != LOG_SERVER_ONLY)
{
if (ClientAuthInProgress)
output_to_client = (elevel >= ERROR);
else
output_to_client = (elevel >= client_min_messages ||
elevel == INFO);
}
if (elevel < ERROR && !output_to_server && !output_to_client)
return false;
if (ErrorContext == NULL)
{
write_stderr("error occurred before error message processing is available\n");
exit(2);
}
if (recursion_depth++ > 0 && elevel >= ERROR)
{
MemoryContextReset(ErrorContext);
if (in_error_recursion_trouble())
{
error_context_stack = NULL;
debug_query_string = NULL;
}
}
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
{
errordata_stack_depth = -1;
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
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->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
edata->context_domain = edata->domain;
if (elevel >= ERROR)
edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
else if (elevel == WARNING)
edata->sqlerrcode = ERRCODE_WARNING;
else
edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
edata->saved_errno = errno;
edata->assoc_context = ErrorContext;
recursion_depth--;
return true;
}
static bool
matches_backtrace_functions(const char *funcname)
{
char *p;
if (!backtrace_symbol_list || funcname == NULL || funcname[0] == '\0')
return false;
p = backtrace_symbol_list;
for (;;)
{
if (*p == '\0')
break;
if (strcmp(funcname, p) == 0)
return true;
p += strlen(p) + 1;
}
return false;
}
void
errfinish(const char *filename, int lineno, const char *funcname)
{
ErrorData *edata = &errordata[errordata_stack_depth];
int elevel;
MemoryContext oldcontext;
ErrorContextCallback *econtext;
recursion_depth++;
CHECK_STACK_DEPTH();
if (filename)
{
const char *slash;
slash = strrchr(filename, '/');
if (slash)
filename = slash + 1;
}
edata->filename = filename;
edata->lineno = lineno;
edata->funcname = funcname;
elevel = edata->elevel;
oldcontext = MemoryContextSwitchTo(ErrorContext);
if (!edata->backtrace &&
edata->funcname &&
backtrace_functions &&
matches_backtrace_functions(edata->funcname))
set_backtrace(edata, 2);
for (econtext = error_context_stack;
econtext != NULL;
econtext = econtext->previous)
econtext->callback(econtext->arg);
if (elevel == ERROR)
{
InterruptHoldoffCount = 0;
QueryCancelHoldoffCount = 0;
CritSectionCount = 0;
recursion_depth--;
PG_RE_THROW();
}
if (elevel >= FATAL && whereToSendOutput == DestRemote)
pq_endcopyout(true);
EmitErrorReport();
if (edata->message)
pfree(edata->message);
if (edata->detail)
pfree(edata->detail);
if (edata->detail_log)
pfree(edata->detail_log);
if (edata->hint)
pfree(edata->hint);
if (edata->context)
pfree(edata->context);
if (edata->backtrace)
pfree(edata->backtrace);
if (edata->schema_name)
pfree(edata->schema_name);
if (edata->table_name)
pfree(edata->table_name);
if (edata->column_name)
pfree(edata->column_name);
if (edata->datatype_name)
pfree(edata->datatype_name);
if (edata->constraint_name)
pfree(edata->constraint_name);
if (edata->internalquery)
pfree(edata->internalquery);
errordata_stack_depth--;
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
if (elevel == FATAL)
{
if (PG_exception_stack == NULL && whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
fflush(stdout);
fflush(stderr);
proc_exit(1);
}
if (elevel >= PANIC)
{
fflush(stdout);
fflush(stderr);
abort();
}
CHECK_FOR_INTERRUPTS();
}
int
errcode(int sqlerrcode)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
edata->sqlerrcode = sqlerrcode;
return 0;
}
#ifdef EROFS
#endif
#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST)
#endif
#ifdef ECONNRESET
#endif
#define EVALUATE_MESSAGE(domain, targetfield, appendval, translateit) \
{ \
StringInfoData buf; \
\
if ((translateit) && !in_error_recursion_trouble()) \
fmt = dgettext((domain), fmt); \
initStringInfo(&buf); \
if ((appendval) && edata->targetfield) { \
appendStringInfoString(&buf, edata->targetfield); \
appendStringInfoChar(&buf, '\n'); \
} \
\
for (;;) \
{ \
va_list args; \
int needed; \
errno = edata->saved_errno; \
va_start(args, fmt); \
needed = appendStringInfoVA(&buf, fmt, args); \
va_end(args); \
if (needed == 0) \
break; \
enlargeStringInfo(&buf, needed); \
} \
\
if (edata->targetfield) \
pfree(edata->targetfield); \
edata->targetfield = pstrdup(buf.data); \
pfree(buf.data); \
}
#define EVALUATE_MESSAGE_PLURAL(domain, targetfield, appendval) \
{ \
const char *fmt; \
StringInfoData buf; \
\
if (!in_error_recursion_trouble()) \
fmt = dngettext((domain), fmt_singular, fmt_plural, n); \
else \
fmt = (n == 1 ? fmt_singular : fmt_plural); \
initStringInfo(&buf); \
if ((appendval) && edata->targetfield) { \
appendStringInfoString(&buf, edata->targetfield); \
appendStringInfoChar(&buf, '\n'); \
} \
\
for (;;) \
{ \
va_list args; \
int needed; \
errno = edata->saved_errno; \
va_start(args, n); \
needed = appendStringInfoVA(&buf, fmt, args); \
va_end(args); \
if (needed == 0) \
break; \
enlargeStringInfo(&buf, needed); \
} \
\
if (edata->targetfield) \
pfree(edata->targetfield); \
edata->targetfield = pstrdup(buf.data); \
pfree(buf.data); \
}
int
errmsg(const char *fmt,...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
edata->message_id = fmt;
EVALUATE_MESSAGE(edata->domain, message, false, true);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0;
}
static void
set_backtrace(ErrorData *edata, int num_skip)
{
StringInfoData errtrace;
initStringInfo(&errtrace);
#ifdef HAVE_BACKTRACE_SYMBOLS
{
void *buf[100];
int nframes;
char **strfrms;
nframes = backtrace(buf, lengthof(buf));
strfrms = backtrace_symbols(buf, nframes);
if (strfrms == NULL)
return;
for (int i = num_skip; i < nframes; i++)
appendStringInfo(&errtrace, "\n%s", strfrms[i]);
free(strfrms);
}
#else
appendStringInfoString(&errtrace,
"backtrace generation is not supported by this installation");
#endif
edata->backtrace = errtrace.data;
}
int
errmsg_internal(const char *fmt,...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
edata->message_id = fmt;
EVALUATE_MESSAGE(edata->domain, message, false, false);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0;
}
int
errdetail(const char *fmt,...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, detail, false, true);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0;
}
int
errhint(const char *fmt,...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, hint, false, true);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0;
}
int
errcontext_msg(const char *fmt,...)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->context_domain, context, true, true);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
return 0;
}
int
set_errcontext_domain(const char *domain)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
edata->context_domain = domain ? domain : PG_TEXTDOMAIN("postgres");
return 0;
}
int
errposition(int cursorpos)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
edata->cursorpos = cursorpos;
return 0;
}
int
internalerrposition(int cursorpos)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
edata->internalpos = cursorpos;
return 0;
}
int
internalerrquery(const char *query)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
if (edata->internalquery)
{
pfree(edata->internalquery);
edata->internalquery = NULL;
}
if (query)
edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);
return 0;
}
int
geterrcode(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
return edata->sqlerrcode;
}
int
geterrposition(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
return edata->cursorpos;
}
int
getinternalerrposition(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
CHECK_STACK_DEPTH();
return edata->internalpos;
}
void
EmitErrorReport(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
MemoryContext oldcontext;
recursion_depth++;
CHECK_STACK_DEPTH();
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
if (edata->output_to_server && emit_log_hook)
(*emit_log_hook) (edata);
if (edata->output_to_server)
send_message_to_server_log(edata);
if (edata->output_to_client)
send_message_to_frontend(edata);
MemoryContextSwitchTo(oldcontext);
recursion_depth--;
}
ErrorData *
CopyErrorData(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
ErrorData *newedata;
CHECK_STACK_DEPTH();
Assert(CurrentMemoryContext != ErrorContext);
newedata = (ErrorData *) palloc(sizeof(ErrorData));
memcpy(newedata, edata, sizeof(ErrorData));
if (newedata->message)
newedata->message = pstrdup(newedata->message);
if (newedata->detail)
newedata->detail = pstrdup(newedata->detail);
if (newedata->detail_log)
newedata->detail_log = pstrdup(newedata->detail_log);
if (newedata->hint)
newedata->hint = pstrdup(newedata->hint);
if (newedata->context)
newedata->context = pstrdup(newedata->context);
if (newedata->backtrace)
newedata->backtrace = pstrdup(newedata->backtrace);
if (newedata->schema_name)
newedata->schema_name = pstrdup(newedata->schema_name);
if (newedata->table_name)
newedata->table_name = pstrdup(newedata->table_name);
if (newedata->column_name)
newedata->column_name = pstrdup(newedata->column_name);
if (newedata->datatype_name)
newedata->datatype_name = pstrdup(newedata->datatype_name);
if (newedata->constraint_name)
newedata->constraint_name = pstrdup(newedata->constraint_name);
if (newedata->internalquery)
newedata->internalquery = pstrdup(newedata->internalquery);
newedata->assoc_context = CurrentMemoryContext;
return newedata;
}
void
FlushErrorState(void)
{
errordata_stack_depth = -1;
recursion_depth = 0;
MemoryContextResetAndDeleteChildren(ErrorContext);
}
void
pg_re_throw(void)
{
if (PG_exception_stack != NULL)
siglongjmp(*PG_exception_stack, 1);
else
{
ErrorData *edata = &errordata[errordata_stack_depth];
Assert(errordata_stack_depth >= 0);
Assert(edata->elevel == ERROR);
edata->elevel = FATAL;
if (IsPostmasterEnvironment)
edata->output_to_server = is_log_level_output(FATAL,
log_min_messages);
else
edata->output_to_server = (FATAL >= log_min_messages);
if (whereToSendOutput == DestRemote)
edata->output_to_client = true;
error_context_stack = NULL;
errfinish(edata->filename, edata->lineno, edata->funcname);
}
ExceptionalCondition("pg_re_throw tried to return", "FailedAssertion",
__FILE__, __LINE__);
}
#ifdef HAVE_SYSLOG
#endif
#ifdef WIN32
static int
GetACPEncoding(void)
{
static int encoding = -2;
if (encoding == -2)
encoding = pg_codepage_to_encoding(GetACP());
return encoding;
}
static void
write_eventlog(int level, const char *line, int len)
{
WCHAR *utf16;
int eventlevel = EVENTLOG_ERROR_TYPE;
static HANDLE evtHandle = INVALID_HANDLE_VALUE;
if (evtHandle == INVALID_HANDLE_VALUE)
{
evtHandle = RegisterEventSource(NULL,
event_source ? event_source : DEFAULT_EVENT_SOURCE);
if (evtHandle == NULL)
{
evtHandle = INVALID_HANDLE_VALUE;
return;
}
}
switch (level)
{
case DEBUG5:
case DEBUG4:
case DEBUG3:
case DEBUG2:
case DEBUG1:
case LOG:
case LOG_SERVER_ONLY:
case INFO:
case NOTICE:
eventlevel = EVENTLOG_INFORMATION_TYPE;
break;
case WARNING:
eventlevel = EVENTLOG_WARNING_TYPE;
break;
case ERROR:
case FATAL:
case PANIC:
default:
eventlevel = EVENTLOG_ERROR_TYPE;
break;
}
if (!in_error_recursion_trouble() &&
CurrentMemoryContext != NULL &&
GetMessageEncoding() != GetACPEncoding())
{
utf16 = pgwin32_message_to_UTF16(line, len, NULL);
if (utf16)
{
ReportEventW(evtHandle,
eventlevel,
0,
0,
NULL,
1,
0,
(LPCWSTR *) &utf16,
NULL);
pfree(utf16);
return;
}
}
ReportEventA(evtHandle,
eventlevel,
0,
0,
NULL,
1,
0,
&line,
NULL);
}
#endif
#ifdef WIN32
#else
#endif
static void send_message_to_server_log(ErrorData *edata) {}
static void send_message_to_frontend(ErrorData *edata) {}
void
write_stderr(const char *fmt,...)
{
va_list ap;
#ifdef WIN32
char errbuf[2048];
#endif
fmt = _(fmt);
va_start(ap, fmt);
#ifndef WIN32
vfprintf(stderr, fmt, ap);
fflush(stderr);
#else
vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
if (pgwin32_is_service())
{
write_eventlog(ERROR, errbuf, strlen(errbuf));
}
else
{
write_console(errbuf, strlen(errbuf));
fflush(stderr);
}
#endif
va_end(ap);
}
static bool
is_log_level_output(int elevel, int log_min_level)
{
if (elevel == LOG || elevel == LOG_SERVER_ONLY)
{
if (log_min_level == LOG || log_min_level <= ERROR)
return true;
}
else if (log_min_level == LOG)
{
if (elevel >= FATAL)
return true;
}
else if (elevel >= log_min_level)
return true;
return false;
}