failures.c revision bb3d09ab39d75ca5269723237f4e0eeec916b534
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "lib.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "ioloop.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "str.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "backtrace-string.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "printf-format-fix.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "write-full.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include "fd-close-on-exec.h"
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include <stdlib.h>
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include <unistd.h>
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include <fcntl.h>
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include <syslog.h>
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi#include <time.h>
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomiconst char *failure_log_type_prefixes[] = {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi "Info: ",
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi "Warning: ",
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi "Error: ",
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi "Fatal: ",
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi "Panic: "
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi};
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic char log_type_internal_chars[] = {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi 'I', 'W', 'E', 'F', 'P'
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi};
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi/* Initialize working defaults */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic fatal_failure_callback_t *fatal_handler ATTR_NORETURN =
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi default_fatal_handler;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic failure_callback_t *error_handler = default_error_handler;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic failure_callback_t *info_handler = default_error_handler;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic void (*failure_exit_callback)(int *) = NULL;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic int log_fd = STDERR_FILENO, log_info_fd = STDERR_FILENO;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic char *log_prefix = NULL, *log_stamp_format = NULL;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi/* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic const char *get_log_stamp_format(const char *unused)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi ATTR_FORMAT_ARG(1);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic const char *get_log_stamp_format(const char *unused ATTR_UNUSED)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return log_stamp_format;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid failure_exit(int status)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (failure_exit_callback != NULL)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi failure_exit_callback(&status);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi exit(status);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic void log_prefix_add(string_t *str)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi struct tm *tm;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi char buf[256];
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi time_t now;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (log_prefix != NULL)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi str_append(str, log_prefix);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (log_stamp_format != NULL) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi now = time(NULL);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi tm = localtime(&now);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (strftime(buf, sizeof(buf),
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi get_log_stamp_format("unused"), tm) > 0)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi str_append(str, buf);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic void log_fd_flush_stop(struct ioloop *ioloop)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi io_loop_stop(ioloop);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic int log_fd_write(int fd, const unsigned char *data, unsigned int len)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi struct ioloop *ioloop;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi struct io *io;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi ssize_t ret;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi while ((ret = write(fd, data, len)) != (ssize_t)len) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (ret > 0) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* some was written, continue.. */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi data += ret;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi len -= ret;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi continue;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (ret == 0) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* out of disk space? */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi errno = ENOSPC;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return -1;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (errno != EAGAIN)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return -1;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* wait until we can write more. this can happen at least
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi when writing to terminal, even if fd is blocking. */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi ioloop = io_loop_create();
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi io = io_add(fd, IO_WRITE, log_fd_flush_stop, ioloop);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi io_loop_run(ioloop);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi io_remove(&io);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi io_loop_destroy(&ioloop);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return 0;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic int ATTR_FORMAT(3, 0)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomidefault_handler(const char *prefix, int fd, const char *format, va_list args)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi static int recursed = 0;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi int ret;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (recursed >= 2) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* we're being called from some signal handler or we ran
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi out of memory */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return -1;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi recursed++;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi T_BEGIN {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi string_t *str = t_str_new(256);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi log_prefix_add(str);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi str_append(str, prefix);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* make sure there's no %n in there and fix %m */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi str_vprintfa(str, printf_format_fix(format), args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi str_append_c(str, '\n');
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi ret = log_fd_write(fd, str_data(str), str_len(str));
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi } T_END;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi recursed--;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return ret;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic void ATTR_NORETURN
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomidefault_fatal_finish(enum log_type type, int status)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi const char *backtrace;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (type == LOG_TYPE_PANIC || status == FATAL_OUTOFMEM) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (backtrace_get(&backtrace) == 0)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi i_error("Raw backtrace: %s", backtrace);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (type == LOG_TYPE_PANIC)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi abort();
9e38412ec12ae8dc4f67ade5b18c8b976de59bcbAki Tuomi else
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi failure_exit(status);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid default_fatal_handler(enum log_type type, int status,
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi const char *format, va_list args)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (default_handler(failure_log_type_prefixes[type], log_fd, format,
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi args) < 0 && status == FATAL_DEFAULT)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi status = FATAL_LOGWRITE;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi default_fatal_finish(type, status);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid default_error_handler(enum log_type type, const char *format, va_list args)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi int fd = type == LOG_TYPE_INFO ? log_info_fd : log_fd;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (default_handler(failure_log_type_prefixes[type],
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi fd, format, args) < 0) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (fd == log_fd)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi failure_exit(FATAL_LOGWRITE);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* we failed to log to info log, try to log the write error
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi to error log - maybe that'll work. */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi i_fatal_status(FATAL_LOGWRITE,
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi "write() failed to info log: %m");
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_log_type(enum log_type type, const char *format, ...)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (type == LOG_TYPE_INFO)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi info_handler(type, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi else
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi error_handler(type, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_panic(const char *format, ...)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi fatal_handler(LOG_TYPE_PANIC, 0, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
9e38412ec12ae8dc4f67ade5b18c8b976de59bcbAki Tuomivoid i_fatal(const char *format, ...)
9e38412ec12ae8dc4f67ade5b18c8b976de59bcbAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi fatal_handler(LOG_TYPE_FATAL, FATAL_DEFAULT, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
9e38412ec12ae8dc4f67ade5b18c8b976de59bcbAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_fatal_status(int status, const char *format, ...)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi fatal_handler(LOG_TYPE_FATAL, status, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_error(const char *format, ...)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi int old_errno = errno;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi error_handler(LOG_TYPE_ERROR, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi errno = old_errno;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_warning(const char *format, ...)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi int old_errno = errno;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi error_handler(LOG_TYPE_WARNING, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi errno = old_errno;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_info(const char *format, ...)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi int old_errno = errno;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_list args;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_start(args, format);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi info_handler(LOG_TYPE_INFO, format, args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi va_end(args);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi errno = old_errno;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_set_fatal_handler(fatal_failure_callback_t *callback ATTR_NORETURN)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (callback == NULL)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi callback = default_fatal_handler;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi fatal_handler = callback;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_set_error_handler(failure_callback_t *callback)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (callback == NULL)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi callback = default_error_handler;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi error_handler = callback;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_set_info_handler(failure_callback_t *callback)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (callback == NULL)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi callback = default_error_handler;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi info_handler = callback;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic int ATTR_FORMAT(3, 0)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomisyslog_handler(int level, enum log_type type, const char *format, va_list args)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi static int recursed = 0;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (recursed >= 2)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return -1;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi recursed++;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi /* syslogs don't generatelly bother to log the level in any way,
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi so make sure fatals and panics are shown clearly */
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi T_BEGIN {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi syslog(level, "%s%s%s",
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi log_prefix == NULL ? "" : log_prefix,
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi type == LOG_TYPE_FATAL || type == LOG_TYPE_PANIC ?
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi failure_log_type_prefixes[type] : "",
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi t_strdup_vprintf(format, args));
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi } T_END;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi recursed--;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi return 0;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_syslog_fatal_handler(enum log_type type, int status,
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi const char *fmt, va_list args)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (syslog_handler(LOG_CRIT, type, fmt, args) < 0 &&
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi status == FATAL_DEFAULT)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi status = FATAL_LOGERROR;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi default_fatal_finish(type, status);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_syslog_error_handler(enum log_type type, const char *fmt, va_list args)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi int level = LOG_ERR;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi switch (type) {
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi case LOG_TYPE_INFO:
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi level = LOG_INFO;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi break;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi case LOG_TYPE_WARNING:
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi level = LOG_WARNING;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi break;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi case LOG_TYPE_ERROR:
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi level = LOG_ERR;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi break;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi case LOG_TYPE_FATAL:
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi case LOG_TYPE_PANIC:
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi level = LOG_CRIT;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi break;
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi }
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (syslog_handler(level, type, fmt, args) < 0)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi failure_exit(FATAL_LOGERROR);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomivoid i_set_failure_syslog(const char *ident, int options, int facility)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi openlog(ident, options, facility);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi i_set_fatal_handler(i_syslog_fatal_handler);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi i_set_error_handler(i_syslog_error_handler);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi i_set_info_handler(i_syslog_error_handler);
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi}
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomistatic void open_log_file(int *fd, const char *path)
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi{
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi char buf[PATH_MAX];
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi
dbc351d8a8dad8cfbc0c1f5d957d23873ab5e26cAki Tuomi if (*fd != STDERR_FILENO) {
if (close(*fd) < 0) {
i_snprintf(buf, sizeof(buf),
"close(%d) failed: %m", *fd);
(void)write_full(STDERR_FILENO, buf, strlen(buf));
}
}
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;
i_snprintf(buf, sizeof(buf),
"Can't open log file %s: %m\n", path);
(void)write_full(STDERR_FILENO, buf, strlen(buf));
failure_exit(FATAL_LOGOPEN);
}
fd_close_on_exec(*fd, TRUE);
}
}
void i_set_failure_file(const char *path, const char *prefix)
{
i_set_failure_prefix(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);
}
open_log_file(&log_fd, path);
log_info_fd = log_fd;
i_set_fatal_handler(NULL);
i_set_error_handler(NULL);
i_set_info_handler(NULL);
}
void i_set_failure_prefix(const char *prefix)
{
i_free(log_prefix);
log_prefix = i_strdup(prefix);
}
static int ATTR_FORMAT(2, 0)
internal_handler(char log_type, 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;
str = t_str_new(512);
str_append_c(str, 1);
str_append_c(str, log_type);
str_vprintfa(str, format, args);
str_append_c(str, '\n');
ret = write_full(2, str_data(str), str_len(str));
} T_END;
recursed--;
return ret;
}
static void ATTR_NORETURN ATTR_FORMAT(3, 0)
i_internal_fatal_handler(enum log_type type, int status,
const char *fmt, va_list args)
{
if (internal_handler(log_type_internal_chars[type], fmt, args) < 0 &&
status == FATAL_DEFAULT)
status = FATAL_LOGERROR;
default_fatal_finish(type, status);
}
static void ATTR_FORMAT(2, 0)
i_internal_error_handler(enum log_type type, const char *fmt, va_list args)
{
if (internal_handler(log_type_internal_chars[type], fmt, args) < 0)
failure_exit(FATAL_LOGERROR);
}
void i_set_failure_internal(void)
{
i_set_fatal_handler(i_internal_fatal_handler);
i_set_error_handler(i_internal_error_handler);
i_set_info_handler(i_internal_error_handler);
}
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;
}
void i_set_failure_timestamp_format(const char *fmt)
{
i_free(log_stamp_format);
log_stamp_format = i_strdup(fmt);
}
void i_set_failure_exit_callback(void (*callback)(int *status))
{
failure_exit_callback = callback;
}
void failures_deinit(void)
{
if (log_info_fd == log_fd)
log_info_fd = STDERR_FILENO;
if (log_fd != STDERR_FILENO) {
(void)close(log_fd);
log_fd = STDERR_FILENO;
}
if (log_info_fd != STDERR_FILENO) {
(void)close(log_info_fd);
log_info_fd = STDERR_FILENO;
}
i_free_and_null(log_prefix);
i_free_and_null(log_stamp_format);
}