failures.c revision 39454303866b345d010f024e86adbd2f0fa66692
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "lib.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "ioloop.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "str.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "hostpid.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "net.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "process-title.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "lib-signals.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "backtrace-string.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "printf-format-fix.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include "write-full.h"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include <unistd.h>
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include <fcntl.h>
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include <syslog.h>
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#include <time.h>
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen#define LOG_TYPE_FLAG_DISABLE_LOG_PREFIX 0x80
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenconst char *failure_log_type_prefixes[LOG_TYPE_COUNT] = {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "Debug: ",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "Info: ",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "Warning: ",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "Error: ",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "Fatal: ",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "Panic: "
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen};
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenconst char *failure_log_type_names[LOG_TYPE_COUNT] = {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen "debug", "info", "warning", "error", "fatal", "panic"
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen};
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen/* Initialize working defaults */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic failure_callback_t *fatal_handler ATTR_NORETURN =
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen default_fatal_handler;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic failure_callback_t *error_handler = default_error_handler;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic failure_callback_t *info_handler = default_error_handler;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic failure_callback_t *debug_handler = default_error_handler;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic void (*failure_exit_callback)(int *) = NULL;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic struct failure_context failure_ctx_debug = { .type = LOG_TYPE_DEBUG };
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic struct failure_context failure_ctx_info = { .type = LOG_TYPE_INFO };
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic struct failure_context failure_ctx_warning = { .type = LOG_TYPE_WARNING };
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic struct failure_context failure_ctx_error = { .type = LOG_TYPE_ERROR };
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic int log_fd = STDERR_FILENO, log_info_fd = STDERR_FILENO,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen log_debug_fd = STDERR_FILENO;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic char *log_prefix = NULL;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic char *log_stamp_format = NULL, *log_stamp_format_suffix = NULL;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic bool failure_ignore_errors = FALSE, log_prefix_sent = FALSE;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic bool coredump_on_error = FALSE;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic void ATTR_FORMAT(2, 0)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Siraineni_internal_error_handler(const struct failure_context *ctx,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const char *format, va_list args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen/* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic const char *
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenget_log_stamp_format(const char *format_arg, unsigned int timestamp_usecs)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ATTR_FORMAT_ARG(1);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic const char *get_log_stamp_format(const char *format_arg ATTR_UNUSED,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen unsigned int timestamp_usecs)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (log_stamp_format_suffix == NULL)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return log_stamp_format;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return t_strdup_printf("%s%06u%s", log_stamp_format,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen timestamp_usecs, log_stamp_format_suffix);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid failure_exit(int status)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen static bool recursed = FALSE;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (failure_exit_callback != NULL && !recursed) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen recursed = TRUE;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen failure_exit_callback(&status);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen exit(status);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic void log_prefix_add(const struct failure_context *ctx, string_t *str)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const struct tm *tm = ctx->timestamp;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen char buf[256];
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen struct timeval now;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (log_stamp_format != NULL) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (tm == NULL) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (gettimeofday(&now, NULL) < 0)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_panic("gettimeofday() failed: %m");
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen tm = localtime(&now.tv_sec);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen } else {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen now.tv_usec = ctx->timestamp_usecs;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (strftime(buf, sizeof(buf),
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen get_log_stamp_format("unused", now.tv_usec), tm) > 0)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen str_append(str, buf);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (ctx->log_prefix != NULL)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen str_append(str, ctx->log_prefix);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen else if (log_prefix != NULL)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen str_append(str, log_prefix);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic void log_fd_flush_stop(struct ioloop *ioloop)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen io_loop_stop(ioloop);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic int log_fd_write(int fd, const unsigned char *data, size_t len)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen struct ioloop *ioloop;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen struct io *io;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ssize_t ret;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen unsigned int prev_signal_term_counter = signal_term_counter;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen unsigned int terminal_eintr_count = 0;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen while ((ret = write(fd, data, len)) != (ssize_t)len) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (ret > 0) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* some was written, continue.. */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen data += ret;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen len -= ret;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen continue;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (ret == 0) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* out of disk space? */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen errno = ENOSPC;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return -1;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen switch (errno) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen case EAGAIN: {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* wait until we can write more. this can happen at
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen least when writing to terminal, even if fd is
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen blocking. Internal logging fd is also now
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen non-blocking, so we can show warnings about blocking
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen on a log write. */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const char *title, *old_title =
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen t_strdup(process_title_get());
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (old_title == NULL)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen title = "[blocking on log write]";
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen else
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen title = t_strdup_printf("%s - [blocking on log write]",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen old_title);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen process_title_set(title);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ioloop = io_loop_create();
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen io = io_add(fd, IO_WRITE, log_fd_flush_stop, ioloop);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen io_loop_run(ioloop);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen io_remove(&io);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen io_loop_destroy(&ioloop);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
0e94016c18197cb42d00be0085e6da4223a1c84eTimo Sirainen process_title_set(old_title);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen break;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen case EINTR:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (prev_signal_term_counter == signal_term_counter) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* non-terminal signal. ignore. */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen } else if (terminal_eintr_count++ == 0) {
0e94016c18197cb42d00be0085e6da4223a1c84eTimo Sirainen /* we'd rather not die in the middle of
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen writing to log. try again once more */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen } else {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* received two terminal signals.
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen someone wants us dead. */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return -1;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen break;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen default:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return -1;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen prev_signal_term_counter = signal_term_counter;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return 0;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic int ATTR_FORMAT(3, 0)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainendefault_handler(const struct failure_context *ctx, int fd,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const char *format, va_list args)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen static int recursed = 0;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen int ret;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (recursed >= 2) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* we're being called from some signal handler or we ran
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen out of memory */
0e94016c18197cb42d00be0085e6da4223a1c84eTimo Sirainen return -1;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
0e94016c18197cb42d00be0085e6da4223a1c84eTimo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen recursed++;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen T_BEGIN {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen string_t *str = t_str_new(256);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen log_prefix_add(ctx, str);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen str_append(str, failure_log_type_prefixes[ctx->type]);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* make sure there's no %n in there and fix %m */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen str_vprintfa(str, printf_format_fix(format), args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen str_append_c(str, '\n');
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ret = log_fd_write(fd, str_data(str), str_len(str));
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen } T_END;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (ret < 0 && failure_ignore_errors)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ret = 0;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen recursed--;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen return ret;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenstatic void ATTR_NORETURN
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainendefault_fatal_finish(enum log_type type, int status)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const char *backtrace;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen static int recursed = 0;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen recursed++;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if ((type == LOG_TYPE_PANIC || status == FATAL_OUTOFMEM) &&
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen recursed == 1) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (backtrace_get(&backtrace) == 0)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_error("Raw backtrace: %s", backtrace);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen recursed--;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (type == LOG_TYPE_PANIC || getenv("CORE_ERROR") != NULL ||
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen (status == FATAL_OUTOFMEM && getenv("CORE_OUTOFMEM") != NULL))
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen abort();
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen else
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen failure_exit(status);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid default_fatal_handler(const struct failure_context *ctx,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const char *format, va_list args)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen int status = ctx->exit_status;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (default_handler(ctx, log_fd, format, args) < 0 &&
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen status == FATAL_DEFAULT)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen status = FATAL_LOGWRITE;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen default_fatal_finish(ctx->type, status);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid default_error_handler(const struct failure_context *ctx,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen const char *format, va_list args)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen int fd;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen switch (ctx->type) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen case LOG_TYPE_DEBUG:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen fd = log_debug_fd;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen break;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen case LOG_TYPE_INFO:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen fd = log_info_fd;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen break;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen default:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen fd = log_fd;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (default_handler(ctx, fd, format, args) < 0) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (fd == log_fd)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen failure_exit(FATAL_LOGWRITE);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /* we failed to log to info/debug log, try to log the
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen write error to error log - maybe that'll work. */
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_fatal_status(FATAL_LOGWRITE, "write() failed to %s log: %m",
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen fd == log_info_fd ? "info" : "debug");
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen if (ctx->type == LOG_TYPE_ERROR && coredump_on_error)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen abort();
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid i_log_type(const struct failure_context *ctx, const char *format, ...)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_list args;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_start(args, format);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_log_typev(ctx, format, args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_end(args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid i_log_typev(const struct failure_context *ctx, const char *format,
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_list args)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen switch (ctx->type) {
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen case LOG_TYPE_DEBUG:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen debug_handler(ctx, format, args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen break;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen case LOG_TYPE_INFO:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen info_handler(ctx, format, args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen break;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen default:
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen error_handler(ctx, format, args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen }
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid i_panic(const char *format, ...)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen struct failure_context ctx;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_list args;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_zero(&ctx);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ctx.type = LOG_TYPE_PANIC;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_start(args, format);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen fatal_handler(&ctx, format, args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_unreached();
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen /*va_end(args);*/
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen}
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainenvoid i_fatal(const char *format, ...)
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen{
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen struct failure_context ctx;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_list args;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_zero(&ctx);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ctx.type = LOG_TYPE_FATAL;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen ctx.exit_status = FATAL_DEFAULT;
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen va_start(args, format);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen fatal_handler(&ctx, format, args);
b90c23a9862b91594959b918b035d73f7bc0b265Timo Sirainen i_unreached();
/*va_end(args);*/
}
void i_fatal_status(int status, const char *format, ...)
{
struct failure_context ctx;
va_list args;
i_zero(&ctx);
ctx.type = LOG_TYPE_FATAL;
ctx.exit_status = status;
va_start(args, format);
fatal_handler(&ctx, format, args);
i_unreached();
/*va_end(args);*/
}
void i_error(const char *format, ...)
{
int old_errno = errno;
va_list args;
va_start(args, format);
error_handler(&failure_ctx_error, format, args);
va_end(args);
errno = old_errno;
}
void i_warning(const char *format, ...)
{
int old_errno = errno;
va_list args;
va_start(args, format);
error_handler(&failure_ctx_warning, format, args);
va_end(args);
errno = old_errno;
}
void i_info(const char *format, ...)
{
int old_errno = errno;
va_list args;
va_start(args, format);
info_handler(&failure_ctx_info, format, args);
va_end(args);
errno = old_errno;
}
void i_debug(const char *format, ...)
{
int old_errno = errno;
va_list args;
va_start(args, format);
debug_handler(&failure_ctx_debug, format, args);
va_end(args);
errno = old_errno;
}
void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN)
{
fatal_handler = callback;
}
void i_set_error_handler(failure_callback_t *callback)
{
coredump_on_error = getenv("CORE_ERROR") != NULL;
error_handler = callback;
}
void i_set_info_handler(failure_callback_t *callback)
{
info_handler = callback;
}
void i_set_debug_handler(failure_callback_t *callback)
{
debug_handler = callback;
}
void i_get_failure_handlers(failure_callback_t **fatal_callback_r,
failure_callback_t **error_callback_r,
failure_callback_t **info_callback_r,
failure_callback_t **debug_callback_r)
{
*fatal_callback_r = fatal_handler;
*error_callback_r = error_handler;
*info_callback_r = info_handler;
*debug_callback_r = debug_handler;
}
static int ATTR_FORMAT(4, 0)
syslog_handler(const struct failure_context *ctx,
int level, enum log_type type, const char *format, va_list args)
{
static int recursed = 0;
if (recursed >= 2)
return -1;
recursed++;
/* syslogs don't generally bother to log the level in any way,
so make sure errors are shown clearly */
T_BEGIN {
const char *cur_log_prefix = ctx->log_prefix != NULL ?
ctx->log_prefix : log_prefix;
syslog(level, "%s%s%s",
cur_log_prefix == NULL ? "" : cur_log_prefix,
type != LOG_TYPE_INFO ?
failure_log_type_prefixes[type] : "",
t_strdup_vprintf(format, args));
} T_END;
recursed--;
return 0;
}
void i_syslog_fatal_handler(const struct failure_context *ctx,
const char *format, va_list args)
{
int status = ctx->exit_status;
if (syslog_handler(ctx, LOG_CRIT, ctx->type, format, args) < 0 &&
status == FATAL_DEFAULT)
status = FATAL_LOGERROR;
default_fatal_finish(ctx->type, status);
}
void i_syslog_error_handler(const struct failure_context *ctx,
const char *format, va_list args)
{
int level = LOG_ERR;
switch (ctx->type) {
case LOG_TYPE_DEBUG:
level = LOG_DEBUG;
break;
case LOG_TYPE_INFO:
level = LOG_INFO;
break;
case LOG_TYPE_WARNING:
level = LOG_WARNING;
break;
case LOG_TYPE_ERROR:
level = LOG_ERR;
break;
case LOG_TYPE_FATAL:
case LOG_TYPE_PANIC:
level = LOG_CRIT;
break;
case LOG_TYPE_COUNT:
case LOG_TYPE_OPTION:
i_unreached();
}
if (syslog_handler(ctx, level, ctx->type, format, args) < 0)
failure_exit(FATAL_LOGERROR);
}
void i_set_failure_syslog(const char *ident, int options, int facility)
{
openlog(ident, options, facility);
i_set_fatal_handler(i_syslog_fatal_handler);
i_set_error_handler(i_syslog_error_handler);
i_set_info_handler(i_syslog_error_handler);
i_set_debug_handler(i_syslog_error_handler);
}
static void open_log_file(int *fd, const char *path)
{
const char *str;
if (*fd != STDERR_FILENO) {
if (close(*fd) < 0) {
str = t_strdup_printf("close(%d) failed: %m\n", *fd);
(void)write_full(STDERR_FILENO, str, strlen(str));
}
}
if (path == NULL || strcmp(path, "/dev/stderr") == 0)
*fd = STDERR_FILENO;
else {
*fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600);
if (*fd == -1) {
*fd = STDERR_FILENO;
str = t_strdup_printf("Can't open log file %s: %m\n",
path);
(void)write_full(STDERR_FILENO, str, strlen(str));
if (fd == &log_fd)
failure_exit(FATAL_LOGOPEN);
else
i_fatal_status(FATAL_LOGOPEN, "%s", str);
}
fd_close_on_exec(*fd, TRUE);
}
}
void i_set_failure_file(const char *path, const char *prefix)
{
i_set_failure_prefix("%s", prefix);
if (log_info_fd != STDERR_FILENO && log_info_fd != log_fd) {
if (close(log_info_fd) < 0)
i_error("close(%d) failed: %m", log_info_fd);
}
if (log_debug_fd != STDERR_FILENO && log_debug_fd != log_info_fd &&
log_debug_fd != log_fd) {
if (close(log_debug_fd) < 0)
i_error("close(%d) failed: %m", log_debug_fd);
}
open_log_file(&log_fd, path);
/* if info/debug logs are elsewhere, i_set_info/debug_file()
overrides these later. */
log_info_fd = log_fd;
log_debug_fd = log_fd;
i_set_fatal_handler(default_fatal_handler);
i_set_error_handler(default_error_handler);
i_set_info_handler(default_error_handler);
i_set_debug_handler(default_error_handler);
}
static void i_failure_send_option(const char *key, const char *value)
{
const char *str;
if (error_handler != i_internal_error_handler)
return;
str = t_strdup_printf("\001%c%s %s=%s\n", LOG_TYPE_OPTION+1,
my_pid, key, value);
(void)write_full(STDERR_FILENO, str, strlen(str));
}
void i_set_failure_prefix(const char *prefix_fmt, ...)
{
va_list args;
va_start(args, prefix_fmt);
i_free(log_prefix);
log_prefix = i_strdup_vprintf(prefix_fmt, args);
va_end(args);
log_prefix_sent = FALSE;
}
void i_unset_failure_prefix(void)
{
i_free(log_prefix);
log_prefix = i_strdup("");
log_prefix_sent = FALSE;
}
const char *i_get_failure_prefix(void)
{
return log_prefix != NULL ? log_prefix : "";
}
static int internal_send_split(string_t *full_str, size_t prefix_len)
{
string_t *str;
size_t max_text_len, pos = prefix_len;
str = t_str_new(PIPE_BUF);
str_append_n(str, str_c(full_str), prefix_len);
max_text_len = PIPE_BUF - prefix_len - 1;
while (pos < str_len(full_str)) {
str_truncate(str, prefix_len);
str_append_n(str, str_c(full_str) + pos, max_text_len);
str_append_c(str, '\n');
if (log_fd_write(STDERR_FILENO,
str_data(str), str_len(str)) < 0)
return -1;
pos += max_text_len;
}
return 0;
}
static int ATTR_FORMAT(2, 0)
internal_handler(const struct failure_context *ctx,
const char *format, va_list args)
{
static int recursed = 0;
int ret;
if (recursed >= 2) {
/* we're being called from some signal handler or we ran
out of memory */
return -1;
}
recursed++;
T_BEGIN {
string_t *str;
size_t prefix_len;
unsigned char log_type = ctx->type + 1;
if (ctx->log_prefix != NULL) {
log_type |= LOG_TYPE_FLAG_DISABLE_LOG_PREFIX;
} else if (!log_prefix_sent && log_prefix != NULL) {
log_prefix_sent = TRUE;
i_failure_send_option("prefix", log_prefix);
}
str = t_str_new(128);
str_printfa(str, "\001%c%s ", log_type, my_pid);
if (ctx->log_prefix != NULL)
str_append(str, ctx->log_prefix);
prefix_len = str_len(str);
str_vprintfa(str, format, args);
if (str_len(str)+1 <= PIPE_BUF) {
str_append_c(str, '\n');
ret = log_fd_write(STDERR_FILENO,
str_data(str), str_len(str));
} else {
ret = internal_send_split(str, prefix_len);
}
} T_END;
if (ret < 0 && failure_ignore_errors)
ret = 0;
recursed--;
return ret;
}
static bool line_parse_prefix(const char *line, enum log_type *log_type_r,
bool *replace_prefix_r)
{
if (*line != 1)
return FALSE;
unsigned char log_type = (line[1] & 0x7f);
if (log_type == '\0') {
i_warning("Broken log line follows (type=NUL)");
return FALSE;
}
log_type--;
if (log_type > LOG_TYPE_OPTION) {
i_warning("Broken log line follows (type=%d)", log_type);
return FALSE;
}
*log_type_r = log_type;
*replace_prefix_r = (line[1] & LOG_TYPE_FLAG_DISABLE_LOG_PREFIX) != 0;
return TRUE;
}
void i_failure_parse_line(const char *line, struct failure_line *failure)
{
i_zero(failure);
if (!line_parse_prefix(line, &failure->log_type,
&failure->disable_log_prefix)) {
failure->log_type = LOG_TYPE_ERROR;
failure->text = line;
return;
}
line += 2;
failure->text = line;
while (*line >= '0' && *line <= '9') {
failure->pid = failure->pid*10 + (*line - '0');
line++;
}
if (*line != ' ') {
/* some old protocol? */
failure->pid = 0;
return;
}
failure->text = line + 1;
}
static void ATTR_NORETURN ATTR_FORMAT(2, 0)
i_internal_fatal_handler(const struct failure_context *ctx,
const char *format, va_list args)
{
int status = ctx->exit_status;
if (internal_handler(ctx, format, args) < 0 &&
status == FATAL_DEFAULT)
status = FATAL_LOGERROR;
default_fatal_finish(ctx->type, status);
}
static void
i_internal_error_handler(const struct failure_context *ctx,
const char *format, va_list args)
{
if (internal_handler(ctx, format, args) < 0)
failure_exit(FATAL_LOGERROR);
}
void i_set_failure_internal(void)
{
fd_set_nonblock(STDERR_FILENO, TRUE);
i_set_fatal_handler(i_internal_fatal_handler);
i_set_error_handler(i_internal_error_handler);
i_set_info_handler(i_internal_error_handler);
i_set_debug_handler(i_internal_error_handler);
}
void i_set_failure_ignore_errors(bool ignore)
{
failure_ignore_errors = ignore;
}
void i_set_info_file(const char *path)
{
if (log_info_fd == log_fd)
log_info_fd = STDERR_FILENO;
open_log_file(&log_info_fd, path);
info_handler = default_error_handler;
/* write debug-level messages to the info_log_path,
until i_set_debug_file() was called */
log_debug_fd = log_info_fd;
i_set_debug_handler(default_error_handler);
}
void i_set_debug_file(const char *path)
{
if (log_debug_fd == log_fd || log_debug_fd == log_info_fd)
log_debug_fd = STDERR_FILENO;
open_log_file(&log_debug_fd, path);
debug_handler = default_error_handler;
}
void i_set_failure_timestamp_format(const char *fmt)
{
const char *p;
i_free(log_stamp_format);
i_free_and_null(log_stamp_format_suffix);
p = strstr(fmt, "%{usecs}");
if (p == NULL)
log_stamp_format = i_strdup(fmt);
else {
log_stamp_format = i_strdup_until(fmt, p);
log_stamp_format_suffix = i_strdup(p + 8);
}
}
void i_set_failure_send_ip(const struct ip_addr *ip)
{
i_failure_send_option("ip", net_ip2addr(ip));
}
void i_set_failure_send_prefix(const char *prefix)
{
i_failure_send_option("prefix", prefix);
}
void i_set_failure_exit_callback(void (*callback)(int *status))
{
failure_exit_callback = callback;
}
void failures_deinit(void)
{
if (log_debug_fd == log_info_fd || log_debug_fd == log_fd)
log_debug_fd = STDERR_FILENO;
if (log_info_fd == log_fd)
log_info_fd = STDERR_FILENO;
if (log_fd != STDERR_FILENO) {
i_close_fd(&log_fd);
log_fd = STDERR_FILENO;
}
if (log_info_fd != STDERR_FILENO) {
i_close_fd(&log_info_fd);
log_info_fd = STDERR_FILENO;
}
if (log_debug_fd != STDERR_FILENO) {
i_close_fd(&log_debug_fd);
log_debug_fd = STDERR_FILENO;
}
i_free_and_null(log_prefix);
i_free_and_null(log_stamp_format);
i_free_and_null(log_stamp_format_suffix);
}