+++ /dev/null
-#include "debug.h"
-
-#include <execinfo.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "log.h"
-
-/**
- * Important: -rdynamic needs to be enabled, otherwise this does not print
- * function names. See LDFLAGS_DEBUG in Makefile.am.
- * Also: Only non-static functions will be labeled.
- *
- * During a segfault, the first three printed entries are usually not
- * meaningful. Outside of a segfault, the first entry is not meaningful.
- */
-void print_stack_trace(void)
-{
-#define STACK_SIZE 64
- void *array[STACK_SIZE];
- size_t size;
- char **strings;
- size_t i;
-
- size = backtrace(array, STACK_SIZE);
- strings = backtrace_symbols(array, size);
-
- pr_op_err("Stack trace:");
- for (i = 0; i < size; i++)
- pr_op_err(" %s", strings[i]);
- pr_op_err("(Stack size was %zu.)", size);
-
- free(strings);
-}
-
-static void
-segfault_handler(int thingy)
-{
- pr_op_err("Segmentation Fault.");
- print_stack_trace();
- exit(1);
-}
-
-/**
- * If you get a Segmentation Fault after calling this, the stack trace will be
- * automatically printed in standard error.
- * Remember to enable -rdynamic (See print_stack_trace()).
- */
-void
-print_stack_trace_on_segfault(void)
-{
- struct sigaction handler;
-
- handler.sa_handler = segfault_handler;
- sigemptyset(&handler.sa_mask);
- handler.sa_flags = 0;
- sigaction(SIGSEGV, &handler, NULL);
-}
#include "log.h"
+#include <execinfo.h>
#include <openssl/bio.h>
#include <openssl/err.h>
+#include <signal.h>
#include <syslog.h>
#include <time.h>
#include "config.h"
-#include "debug.h"
#include "thread_var.h"
struct level {
/* Configuration for the validation logs. */
static struct log_config val_config;
+/*
+ * Note: Normally, fprintf and syslog would have separate locks.
+ *
+ * However, fprintf and syslog are rarely enabled at the same time, so I don't
+ * think it's worth it. So I'm reusing the lock.
+ */
+static pthread_mutex_t lock;
+
+/**
+ * Important: -rdynamic needs to be enabled, otherwise this does not print
+ * function names. See LDFLAGS_DEBUG in Makefile.am.
+ * Also: Only non-static functions will be labeled.
+ *
+ * During a segfault, the first three printed entries are usually not
+ * meaningful. Outside of a segfault, the first entry is not meaningful.
+ * (But I'm printing everything anyway due to paranoia.)
+ *
+ * @title is allowed to be NULL. If you need locking, do it outside. (And be
+ * aware that pthread_mutex_lock() can return error codes, which shouldn't
+ * prevent critical stack traces from printing.)
+ */
+void
+print_stack_trace(char const *title)
+{
+ /*
+ * Keep this as low-level as possible. Do not employ helper functions,
+ * even if this causes small inconsistencies.
+ * Helper functions (ie. any functions declared by Fort) might end up
+ * attempting to write another stack trace and cause infinite recursion.
+ */
+
+#define STACK_SIZE 64
+
+ void *array[STACK_SIZE];
+ size_t size;
+ char **strings;
+ size_t i;
+ int fp;
+
+ size = backtrace(array, STACK_SIZE);
+ strings = backtrace_symbols(array, size);
+
+ if (op_config.fprintf_enabled) {
+ if (title != NULL)
+ fprintf(ERR.stream, "%s\n", title);
+ fprintf(ERR.stream, "Stack trace:\n");
+ for (i = 0; i < size; i++)
+ fprintf(ERR.stream, " %s\n", strings[i]);
+ fprintf(ERR.stream, "(End of stack trace)\n");
+ }
+
+ if (op_config.syslog_enabled) {
+ fp = LOG_ERR | op_config.facility;
+ if (title != NULL)
+ syslog(fp, "%s", title);
+ syslog(fp, "Stack trace:");
+ for (i = 0; i < size; i++)
+ syslog(fp, " %s", strings[i]);
+ syslog(fp, "(End of stack trace)");
+ }
+
+ free(strings);
+}
+
+static void
+segfault_handler(int thingy)
+{
+ print_stack_trace("Segmentation Fault.");
+ exit(1);
+}
+
static void init_config(struct log_config *cfg)
{
cfg->fprintf_enabled = true;
cfg->facility = LOG_DAEMON;
}
-void
+int
log_setup(void)
{
+ /*
+ * Remember not to use any actual logging functions until logging has
+ * been properly initialized.
+ */
+
+ struct sigaction handler;
+ int error;
+
DBG.stream = stdout;
INF.stream = stdout;
WRN.stream = stderr;
init_config(&op_config);
init_config(&val_config);
+
+ error = pthread_mutex_init(&lock, NULL);
+ if (error) {
+ fprintf(ERR.stream, "pthread_mutex_init() returned %d: %s\n",
+ error, strerror(abs(error)));
+ syslog(LOG_ERR | op_config.facility,
+ "pthread_mutex_init() returned %d: %s",
+ error, strerror(abs(error)));
+ return error;
+ }
+
+ /*
+ * Register stack trace printer on segmentation fault listener.
+ * Remember to enable -rdynamic (See print_stack_trace()).
+ */
+ handler.sa_handler = segfault_handler;
+ sigemptyset(&handler.sa_mask);
+ handler.sa_flags = 0;
+ sigaction(SIGSEGV, &handler, NULL);
+
+ return 0;
}
static void
log_teardown(void)
{
log_disable_syslog();
+ pthread_mutex_destroy(&lock);
}
void
return &UNK;
}
+static void
+lock_mutex(void)
+{
+ int error;
+
+ error = pthread_mutex_lock(&lock);
+ if (error) {
+ /*
+ * Despite being supposed to be impossible, failing to lock the
+ * mutex is not fatal; it just means we might log some mixed
+ * messages, which is better than dying.
+ *
+ * Furthermore, this might have been called while logging
+ * another critical. We must absolutely not get in the way of
+ * that critical's print.
+ */
+ print_stack_trace(strerror(error));
+ }
+}
+
+static void
+unlock_mutex(void)
+{
+ int error;
+
+ error = pthread_mutex_unlock(&lock);
+ if (error)
+ print_stack_trace(strerror(error)); /* Same as above. */
+}
+
static void
__vfprintf(int level, struct log_config *cfg, char const *format, va_list args)
{
lvl = level2struct(level);
+ lock_mutex();
+
if (cfg->color)
fprintf(lvl->stream, "%s", lvl->color);
/* Force flush */
if (lvl->stream == stdout)
fflush(lvl->stream);
+
+ unlock_mutex();
}
-#define MSG_LEN 512
+#define MSG_LEN 1024
static void
__syslog(int level, struct log_config *cfg, const char *format, va_list args)
{
+ static char msg[MSG_LEN];
char const *file_name;
struct level const *lvl;
- char msg[MSG_LEN];
file_name = fnstack_peek();
lvl = level2struct(level);
+ lock_mutex();
+
/* Can't use vsyslog(); it's not portable. */
vsnprintf(msg, MSG_LEN, format, args);
if (file_name != NULL) {
syslog(level | cfg->facility, "%s: %s",
lvl->label, msg);
}
+
+ unlock_mutex();
}
#define PR_SIMPLE(lvl, config) \
arg.error_fn = error_fn;
ERR_print_errors_cb(log_crypto_error, &arg);
if (arg.stack_size == 0)
- error_fn(0, " <Empty>");
+ error_fn(0, " <Empty>");
+ else
+ error_fn(0, "End of libcrypto stack.");
return -EINVAL;
}
return crypto_err(&val_config, __pr_val_err);
}
-/**
- * This is an operation log
- **/
int
pr_enomem(void)
{
- pr_op_err("Out of memory.");
- print_stack_trace();
- exit(ENOMEM);
+ pr_crit("Out of memory.");
}
-/**
- * This is an operation log
- **/
__dead void
pr_crit(const char *format, ...)
{
- PR_SIMPLE(LOG_CRIT, op_config);
- print_stack_trace();
+ PR_SIMPLE(LOG_ERR, op_config);
+ print_stack_trace(NULL);
exit(-1);
}
#define CHECK_FORMAT(str, args) /* Nothing */
#endif
-/* Only call this group of functions when you know there's only one thread. */
-void log_setup(void);
+/*
+ * Only call this group of functions when you know there's only one thread.
+ *
+ * log_setup() is an incomplete initialization meant to be called when the
+ * program starts. Logging can be performed after log_setup(), but it will use
+ * default values.
+ * log_init() finishes initialization by loading the user's intended config.
+ * log_teardown() reverts initialization.
+ */
+int log_setup(void);
void log_start(void);
void log_teardown(void);
/* Call to flush the stdout/stderr streams */
void log_flush(void);
-/*
- * Please note: The log message (excluding pr_errno's strerror and libcrypto's
- * error stack) cannot exceed 512 bytes at present.
- */
-
/*
* Check if corresponding logging is enabled. You can use these to short-circuit
* out of heavy logging code.
void pr_op_info(const char *, ...) CHECK_FORMAT(1, 2);
/* Non-errors that suggest a problem. */
int pr_op_warn(const char *, ...) CHECK_FORMAT(1, 2);
-/* Problematic situations that prevent Fort from doing its job. */
+/* Do not use this; see pr_op_err() and pr_op_errno(). */
int __pr_op_err(int, const char *, ...) CHECK_FORMAT(2, 3);
+/*
+ * Problematic situations that prevent Fort from doing its job.
+ * (Always returns -EINVAL.)
+ */
+#define pr_op_err(fmt, ...) __pr_op_err(-EINVAL, fmt, ##__VA_ARGS__)
+/*
+ * Like pr_op_err(), but also prints strerror(error).
+ * (Always returns error).
+ */
+#define pr_op_errno(error, fmt, ...) \
+ __pr_op_err(error, fmt ": %s", ##__VA_ARGS__, strerror(abs(error)))
/* Like pr_op_err(), except it prints libcrypto's error stack as well. */
int op_crypto_err(const char *, ...) CHECK_FORMAT(1, 2);
-#define pr_op_err(fmt, ...) \
- __pr_op_err(-EINVAL, fmt, ##__VA_ARGS__)
-#define pr_op_errno(error, fmt, ...) \
- __pr_op_err(error, fmt ": %s", ##__VA_ARGS__, strerror(abs(error)))
/* == Validation logs == */
void pr_val_info(const char *, ...) CHECK_FORMAT(1, 2);
/* Issues that did not trigger RPKI object rejection. */
int pr_val_warn(const char *, ...) CHECK_FORMAT(1, 2);
-/* Problems that trigger RPKI object rejection. */
+/* Do not use this; see pr_val_err() and pr_val_errno(). */
int __pr_val_err(int, const char *, ...) CHECK_FORMAT(2, 3);
-/* Like pr_val_err(), except it prints libcrypto's error stack as well. */
-int val_crypto_err(const char *, ...) CHECK_FORMAT(1, 2);
-
-#define pr_val_err(fmt, ...) \
- __pr_val_err(-EINVAL, fmt, ##__VA_ARGS__)
+/* Problems that trigger RPKI object rejection. */
+#define pr_val_err(fmt, ...) __pr_val_err(-EINVAL, fmt, ##__VA_ARGS__)
+/*
+ * Like pr_val_err(), but also prints strerror(error).
+ * (Always returns error).
+ */
#define pr_val_errno(error, fmt, ...) \
__pr_val_err(error, fmt ": %s", ##__VA_ARGS__, strerror(abs(error)))
+/* Like pr_val_err(), except it prints libcrypto's error stack as well. */
+int val_crypto_err(const char *, ...) CHECK_FORMAT(1, 2);
/* Like pr_*_err(), specific to out-of-memory situations. */
int pr_enomem(void);