diff options
Diffstat (limited to 'src/backend/utils/error/elog.c')
-rw-r--r-- | src/backend/utils/error/elog.c | 187 |
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 |