failures.c revision a9e73c0ed3ad73c6d2eb0c25fe4f84f595b61762
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "lib.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "str.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "write-full.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "fd-close-on-exec.h"
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen#include "printf-upper-bound.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include <stdio.h>
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include <stdlib.h>
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include <syslog.h>
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include <time.h>
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void default_panic_handler(const char *format, va_list args)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen __attr_noreturn__;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void default_fatal_handler(int status, const char *format, va_list args)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen __attr_noreturn__;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void default_error_handler(const char *format, va_list args);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic void default_warning_handler(const char *format, va_list args);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void default_info_handler(const char *format, va_list args);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen/* Initialize working defaults */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic failure_callback_t *panic_handler __attr_noreturn__ =
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen default_panic_handler;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic fatal_failure_callback_t *fatal_handler __attr_noreturn__ =
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen default_fatal_handler;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic failure_callback_t *error_handler = default_error_handler;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic failure_callback_t *warning_handler = default_warning_handler;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic failure_callback_t *info_handler = default_info_handler;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic FILE *log_fd = NULL, *log_info_fd = NULL;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic char *log_prefix = NULL, *log_stamp_format = NULL;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen/* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic const char *get_log_stamp_format(const char *unused)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen __attr_format_arg__(1);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainenstatic const char *get_log_stamp_format(const char *unused __attr_unused__)
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen{
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen return log_stamp_format;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void write_prefix(FILE *f)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen struct tm *tm;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen char str[256];
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen time_t now;
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (log_prefix != NULL)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen fputs(log_prefix, f);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen if (log_stamp_format != NULL) {
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen now = time(NULL);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen tm = localtime(&now);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen if (strftime(str, sizeof(str),
e8eb96edcfe8cff7839f1258ab6e871e41a4785eTimo Sirainen get_log_stamp_format("unused"), tm) > 0)
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen fputs(str, f);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen }
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen}
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic int default_handler(const char *prefix, FILE *f,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen const char *format, va_list args)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen static int recursed = 0;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen va_list args2;
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen int old_errno = errno;
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen
e50c7afe297ab10e07a8acc816c76ce9d45ef409Timo Sirainen if (recursed == 2) {
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen /* we're being called from some signal handler, or
e50c7afe297ab10e07a8acc816c76ce9d45ef409Timo Sirainen printf_string_upper_bound() killed us again */
e50c7afe297ab10e07a8acc816c76ce9d45ef409Timo Sirainen return -1;
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen recursed++;
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen if (f == NULL) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen f = stderr;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (log_fd == NULL)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen log_fd = stderr;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen VA_COPY(args2, args);
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen t_push();
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen if (recursed == 2) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* printf_string_upper_bound() probably killed us last time,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen just write the format now. */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen fputs("recursed: ", f);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen fputs(format, f);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen } else {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen write_prefix(f);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen fputs(prefix, f);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* write may have failed, restore errno so %m works. although
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen it probably can't write the error then anyway. */
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen errno = old_errno;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* make sure there's no %n in there and fix %m */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen (void)printf_string_upper_bound(&format, args);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen vfprintf(f, format, args2);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen fputc('\n', f);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen t_pop();
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen errno = old_errno;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen recursed--;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return 0;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenstatic void default_panic_handler(const char *format, va_list args)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen (void)default_handler("Panic: ", log_fd, format, args);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen abort();
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void default_fatal_handler(int status, const char *format, va_list args)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (default_handler("Fatal: ", log_fd, format, args) < 0 &&
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen status == FATAL_DEFAULT)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen status = FATAL_LOGERROR;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (fflush(log_fd) < 0 && status == FATAL_DEFAULT)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen status = FATAL_LOGWRITE;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen exit(status);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenstatic void default_error_handler(const char *format, va_list args)
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen int old_errno = errno;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (default_handler("Error: ", log_fd, format, args) < 0)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen exit(FATAL_LOGERROR);
if (fflush(log_fd) < 0)
exit(FATAL_LOGWRITE);
errno = old_errno;
}
static void default_warning_handler(const char *format, va_list args)
{
int old_errno = errno;
(void)default_handler("Warning: ", log_fd, format, args);
if (fflush(log_fd) < 0)
exit(FATAL_LOGWRITE);
errno = old_errno;
}
static void default_info_handler(const char *format, va_list args)
{
int old_errno = errno;
(void)default_handler("Info: ", log_info_fd, format, args);
if (fflush(log_info_fd) < 0)
exit(FATAL_LOGWRITE);
errno = old_errno;
}
void i_panic(const char *format, ...)
{
va_list args;
va_start(args, format);
panic_handler(format, args);
va_end(args);
}
void i_fatal(const char *format, ...)
{
va_list args;
va_start(args, format);
fatal_handler(FATAL_DEFAULT, format, args);
va_end(args);
}
void i_fatal_status(int status, const char *format, ...)
{
va_list args;
va_start(args, format);
fatal_handler(status, format, args);
va_end(args);
}
void i_error(const char *format, ...)
{
va_list args;
va_start(args, format);
error_handler(format, args);
va_end(args);
}
void i_warning(const char *format, ...)
{
va_list args;
va_start(args, format);
warning_handler(format, args);
va_end(args);
}
void i_info(const char *format, ...)
{
va_list args;
va_start(args, format);
info_handler(format, args);
va_end(args);
}
void i_set_panic_handler(failure_callback_t *callback __attr_noreturn__)
{
if (callback == NULL)
callback = default_panic_handler;
panic_handler = callback;
}
void i_set_fatal_handler(fatal_failure_callback_t *callback __attr_noreturn__)
{
if (callback == NULL)
callback = default_fatal_handler;
fatal_handler = callback;
}
void i_set_error_handler(failure_callback_t *callback)
{
if (callback == NULL)
callback = default_error_handler;
error_handler = callback;
}
void i_set_warning_handler(failure_callback_t *callback)
{
if (callback == NULL)
callback = default_warning_handler;
warning_handler = callback;
}
void i_set_info_handler(failure_callback_t *callback)
{
if (callback == NULL)
callback = default_info_handler;
info_handler = callback;
}
static int syslog_handler(int level, const char *format, va_list args)
{
va_list args2;
static int recursed = 0;
if (recursed != 0)
return -1;
recursed++;
/* make sure there's no %n in there */
VA_COPY(args2, args);
(void)printf_string_upper_bound(&format, args);
vsyslog(level, format, args2);
recursed--;
return 0;
}
void i_syslog_panic_handler(const char *fmt, va_list args)
{
(void)syslog_handler(LOG_CRIT, fmt, args);
abort();
}
void i_syslog_fatal_handler(int status, const char *fmt, va_list args)
{
if (syslog_handler(LOG_CRIT, fmt, args) < 0 && status == FATAL_DEFAULT)
status = FATAL_LOGERROR;
exit(status);
}
void i_syslog_error_handler(const char *fmt, va_list args)
{
if (syslog_handler(LOG_ERR, fmt, args) < 0)
exit(FATAL_LOGERROR);
}
void i_syslog_warning_handler(const char *fmt, va_list args)
{
(void)syslog_handler(LOG_WARNING, fmt, args);
}
void i_syslog_info_handler(const char *fmt, va_list args)
{
(void)syslog_handler(LOG_INFO, fmt, args);
}
void i_set_failure_syslog(const char *ident, int options, int facility)
{
openlog(ident, options, facility);
i_set_panic_handler(i_syslog_panic_handler);
i_set_fatal_handler(i_syslog_fatal_handler);
i_set_error_handler(i_syslog_error_handler);
i_set_warning_handler(i_syslog_warning_handler);
i_set_info_handler(i_syslog_info_handler);
}
static void open_log_file(FILE **file, const char *path)
{
if (*file != NULL && *file != stderr)
(void)fclose(*file);
if (path == NULL || strcmp(path, "/dev/stderr") == 0)
*file = stderr;
else {
*file = fopen(path, "a");
if (*file == NULL) {
i_fatal_status(FATAL_LOGOPEN,
"Can't open log file %s: %m", path);
}
fd_close_on_exec(fileno(*file), TRUE);
}
}
void i_set_failure_file(const char *path, const char *prefix)
{
i_free(log_prefix);
log_prefix = i_strconcat(prefix, ": ", NULL);
if (log_info_fd != NULL && log_info_fd != log_fd &&
log_info_fd != stderr)
(void)fclose(log_info_fd);
open_log_file(&log_fd, path);
log_info_fd = log_fd;
i_set_panic_handler(NULL);
i_set_fatal_handler(NULL);
i_set_error_handler(NULL);
i_set_warning_handler(NULL);
}
static int internal_handler(char log_type, const char *format, va_list args)
{
string_t *str;
int ret;
t_push();
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_pop();
return ret;
}
static void i_internal_panic_handler(const char *fmt, va_list args)
__attr_noreturn__;
static void i_internal_panic_handler(const char *fmt, va_list args)
{
(void)internal_handler('F', fmt, args);
abort();
}
static void i_internal_fatal_handler(int status, const char *fmt, va_list args)
__attr_noreturn__;
static void i_internal_fatal_handler(int status, const char *fmt, va_list args)
{
if (internal_handler('F', fmt, args) < 0 && status == FATAL_DEFAULT)
status = FATAL_LOGERROR;
exit(status);
}
static void i_internal_error_handler(const char *fmt, va_list args)
{
if (internal_handler('E', fmt, args) < 0)
exit(FATAL_LOGERROR);
}
static void i_internal_warning_handler(const char *fmt, va_list args)
{
(void)internal_handler('W', fmt, args);
}
static void i_internal_info_handler(const char *fmt, va_list args)
{
(void)internal_handler('I', fmt, args);
}
void i_set_failure_internal(void)
{
i_set_panic_handler(i_internal_panic_handler);
i_set_fatal_handler(i_internal_fatal_handler);
i_set_error_handler(i_internal_error_handler);
i_set_warning_handler(i_internal_warning_handler);
i_set_info_handler(i_internal_info_handler);
}
void i_set_info_file(const char *path)
{
if (log_info_fd == log_fd)
log_info_fd = NULL;
open_log_file(&log_info_fd, path);
info_handler = default_info_handler;
}
void i_set_failure_timestamp_format(const char *fmt)
{
i_free(log_stamp_format);
log_stamp_format = i_strdup(fmt);
}
void failures_deinit(void)
{
if (log_info_fd == log_fd)
log_info_fd = NULL;
if (log_fd != NULL && log_fd != stderr) {
(void)fclose(log_fd);
log_fd = stderr;
}
if (log_info_fd != NULL && log_info_fd != stderr) {
(void)fclose(log_info_fd);
log_info_fd = stderr;
}
i_free_and_null(log_prefix);
i_free_and_null(log_stamp_format);
}