summaryrefslogtreecommitdiff
path: root/src/backend/utils/error/elog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/error/elog.c')
-rw-r--r--src/backend/utils/error/elog.c187
1 files changed, 180 insertions, 7 deletions
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index cb3c2898898..96c694da8fd 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -79,9 +79,10 @@
#include "storage/ipc.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
-#include "utils/guc.h"
+#include "utils/guc_hooks.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
+#include "utils/varlena.h"
/* In this module, access gettext() via err_gettext() */
@@ -113,6 +114,9 @@ char *Log_destination_string = NULL;
bool syslog_sequence_numbers = true;
bool syslog_split_messages = true;
+/* Processed form of backtrace_symbols GUC */
+static char *backtrace_symbol_list;
+
#ifdef HAVE_SYSLOG
/*
@@ -1957,14 +1961,159 @@ DebugFileOpen(void)
}
+/*
+ * GUC check_hook for backtrace_functions
+ *
+ * We split the input string, where commas separate function names
+ * and certain whitespace chars are ignored, into a \0-separated (and
+ * \0\0-terminated) list of function names. This formulation allows
+ * easy scanning when an error is thrown while avoiding the use of
+ * non-reentrant strtok(), as well as keeping the output data in a
+ * single palloc() chunk.
+ */
+bool
+check_backtrace_functions(char **newval, void **extra, GucSource source)
+{
+ int newvallen = strlen(*newval);
+ char *someval;
+ int validlen;
+ int i;
+ int j;
+
+ /*
+ * Allow characters that can be C identifiers and commas as separators, as
+ * well as some whitespace for readability.
+ */
+ validlen = strspn(*newval,
+ "0123456789_"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ ", \n\t");
+ if (validlen != newvallen)
+ {
+ GUC_check_errdetail("invalid character");
+ return false;
+ }
+
+ if (*newval[0] == '\0')
+ {
+ *extra = NULL;
+ return true;
+ }
+
+ /*
+ * Allocate space for the output and create the copy. We could discount
+ * whitespace chars to save some memory, but it doesn't seem worth the
+ * trouble.
+ */
+ someval = guc_malloc(ERROR, newvallen + 1 + 1);
+ for (i = 0, j = 0; i < newvallen; i++)
+ {
+ if ((*newval)[i] == ',')
+ someval[j++] = '\0'; /* next item */
+ else if ((*newval)[i] == ' ' ||
+ (*newval)[i] == '\n' ||
+ (*newval)[i] == '\t')
+ ; /* ignore these */
+ else
+ someval[j++] = (*newval)[i]; /* copy anything else */
+ }
+
+ /* two \0s end the setting */
+ someval[j] = '\0';
+ someval[j + 1] = '\0';
+
+ *extra = someval;
+ return true;
+}
+
+/*
+ * GUC assign_hook for backtrace_functions
+ */
+void
+assign_backtrace_functions(const char *newval, void *extra)
+{
+ backtrace_symbol_list = (char *) extra;
+}
+
+/*
+ * GUC check_hook for log_destination
+ */
+bool
+check_log_destination(char **newval, void **extra, GucSource source)
+{
+ char *rawstring;
+ List *elemlist;
+ ListCell *l;
+ int newlogdest = 0;
+ int *myextra;
+
+ /* Need a modifiable copy of string */
+ rawstring = pstrdup(*newval);
+
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawstring, ',', &elemlist))
+ {
+ /* syntax error in list */
+ GUC_check_errdetail("List syntax is invalid.");
+ pfree(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+
+ foreach(l, elemlist)
+ {
+ char *tok = (char *) lfirst(l);
+
+ if (pg_strcasecmp(tok, "stderr") == 0)
+ newlogdest |= LOG_DESTINATION_STDERR;
+ else if (pg_strcasecmp(tok, "csvlog") == 0)
+ newlogdest |= LOG_DESTINATION_CSVLOG;
+ else if (pg_strcasecmp(tok, "jsonlog") == 0)
+ newlogdest |= LOG_DESTINATION_JSONLOG;
#ifdef HAVE_SYSLOG
+ else if (pg_strcasecmp(tok, "syslog") == 0)
+ newlogdest |= LOG_DESTINATION_SYSLOG;
+#endif
+#ifdef WIN32
+ else if (pg_strcasecmp(tok, "eventlog") == 0)
+ newlogdest |= LOG_DESTINATION_EVENTLOG;
+#endif
+ else
+ {
+ GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
+ pfree(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+ }
+
+ pfree(rawstring);
+ list_free(elemlist);
+
+ myextra = (int *) guc_malloc(ERROR, sizeof(int));
+ *myextra = newlogdest;
+ *extra = (void *) myextra;
+
+ return true;
+}
+
+/*
+ * GUC assign_hook for log_destination
+ */
+void
+assign_log_destination(const char *newval, void *extra)
+{
+ Log_destination = *((int *) extra);
+}
/*
- * Set or update the parameters for syslog logging
+ * GUC assign_hook for syslog_ident
*/
void
-set_syslog_parameters(const char *ident, int facility)
+assign_syslog_ident(const char *newval, void *extra)
{
+#ifdef HAVE_SYSLOG
/*
* guc.c is likely to call us repeatedly with same parameters, so don't
* thrash the syslog connection unnecessarily. Also, we do not re-open
@@ -1975,8 +2124,7 @@ set_syslog_parameters(const char *ident, int facility)
* on guc.c's. This may be overly paranoid, but it ensures that we cannot
* accidentally free a string that syslog is still using.
*/
- if (syslog_ident == NULL || strcmp(syslog_ident, ident) != 0 ||
- syslog_facility != facility)
+ if (syslog_ident == NULL || strcmp(syslog_ident, newval) != 0)
{
if (openlog_done)
{
@@ -1984,12 +2132,37 @@ set_syslog_parameters(const char *ident, int facility)
openlog_done = false;
}
free(syslog_ident);
- syslog_ident = strdup(ident);
+ syslog_ident = strdup(newval);
/* if the strdup fails, we will cope in write_syslog() */
- syslog_facility = facility;
}
+#endif
+ /* Without syslog support, just ignore it */
+}
+
+/*
+ * GUC assign_hook for syslog_facility
+ */
+void
+assign_syslog_facility(int newval, void *extra)
+{
+#ifdef HAVE_SYSLOG
+ /*
+ * As above, don't thrash the syslog connection unnecessarily.
+ */
+ if (syslog_facility != newval)
+ {
+ if (openlog_done)
+ {
+ closelog();
+ openlog_done = false;
+ }
+ syslog_facility = newval;
+ }
+#endif
+ /* Without syslog support, just ignore it */
}
+#ifdef HAVE_SYSLOG
/*
* Write a message line to syslog