/src/dovecot/src/lib/failures.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ioloop.h" |
5 | | #include "str.h" |
6 | | #include "hostpid.h" |
7 | | #include "net.h" |
8 | | #include "process-title.h" |
9 | | #include "lib-signals.h" |
10 | | #include "backtrace-string.h" |
11 | | #include "printf-format-fix.h" |
12 | | #include "write-full.h" |
13 | | #include "time-util.h" |
14 | | #include "failures-private.h" |
15 | | |
16 | | #include <unistd.h> |
17 | | #include <fcntl.h> |
18 | | #include <syslog.h> |
19 | | #include <time.h> |
20 | | #include <poll.h> |
21 | | |
22 | 0 | #define LOG_TYPE_FLAG_PREFIX_LEN 0x40 |
23 | 0 | #define LOG_TYPE_FLAG_DISABLE_LOG_PREFIX 0x80 |
24 | | |
25 | | const char *failure_log_type_prefixes[] = { |
26 | | "Debug: ", |
27 | | "Info: ", |
28 | | "Warning: ", |
29 | | "Error: ", |
30 | | "Fatal: ", |
31 | | "Panic: " |
32 | | }; |
33 | | static_assert_array_size(failure_log_type_prefixes, LOG_TYPE_COUNT); |
34 | | |
35 | | const char *failure_log_type_names[LOG_TYPE_COUNT] = { |
36 | | "debug", "info", "warning", "error", "fatal", "panic" |
37 | | }; |
38 | | |
39 | | static int log_fd_write(int fd, const unsigned char *data, size_t len); |
40 | | |
41 | | static void error_handler_real(const struct failure_context *ctx, |
42 | | const char *format, va_list args); |
43 | | |
44 | | /* Initialize working defaults */ |
45 | | static failure_callback_t *fatal_handler ATTR_NORETURN = |
46 | | default_fatal_handler; |
47 | | static failure_callback_t *error_handler = default_error_handler; |
48 | | static failure_callback_t *info_handler = default_error_handler; |
49 | | static failure_callback_t *debug_handler = default_error_handler; |
50 | | static void (*failure_exit_callback)(int *) = NULL; |
51 | | |
52 | | static struct failure_context failure_ctx_debug = { .type = LOG_TYPE_DEBUG }; |
53 | | static struct failure_context failure_ctx_info = { .type = LOG_TYPE_INFO }; |
54 | | static struct failure_context failure_ctx_warning = { .type = LOG_TYPE_WARNING }; |
55 | | static struct failure_context failure_ctx_error = { .type = LOG_TYPE_ERROR }; |
56 | | |
57 | | static int log_fd = STDERR_FILENO, log_info_fd = STDERR_FILENO, |
58 | | log_debug_fd = STDERR_FILENO; |
59 | | static char *syslog_ident = NULL; |
60 | | static char *log_prefix = NULL; |
61 | | static char *log_stamp_format = NULL, *log_stamp_format_suffix = NULL; |
62 | | static bool failure_ignore_errors = FALSE, log_prefix_sent = FALSE; |
63 | | static bool coredump_on_error = FALSE; |
64 | | static void log_timestamp_add(const struct failure_context *ctx, string_t *str); |
65 | | static void log_prefix_add(const struct failure_context *ctx, string_t *str); |
66 | | static int i_failure_send_option_forced(const char *key, const char *value); |
67 | | static int internal_send_split(string_t *full_str, size_t prefix_len); |
68 | | |
69 | | static bool log_fd_can_close(int fd) |
70 | 0 | { |
71 | 0 | return fd != STDOUT_FILENO && fd != STDERR_FILENO; |
72 | 0 | } |
73 | | |
74 | | static const char *log_prefix_sanitize(const char *str) |
75 | 0 | { |
76 | | /* we really only care about LFs, which can break everything. */ |
77 | 0 | return t_str_replace(str, '\n', ' '); |
78 | 0 | } |
79 | | |
80 | | static int ATTR_FORMAT(2, 0) |
81 | | default_write(const struct failure_context *ctx, |
82 | | const char *format, va_list args) |
83 | 1 | { |
84 | 1 | string_t *data = t_str_new(256); |
85 | 1 | log_timestamp_add(ctx, data); |
86 | 1 | log_prefix_add(ctx, data); |
87 | 1 | size_t prefix_len = str_len(data); |
88 | | |
89 | | /* make sure there's no %n in there and fix %m */ |
90 | 1 | str_vprintfa(data, printf_format_fix(format), args); |
91 | | |
92 | 1 | int fd; |
93 | 1 | switch (ctx->type) { |
94 | 0 | case LOG_TYPE_DEBUG: |
95 | 0 | fd = log_debug_fd; |
96 | 0 | break; |
97 | 1 | case LOG_TYPE_INFO: |
98 | 1 | fd = log_info_fd; |
99 | 1 | break; |
100 | 0 | default: |
101 | 0 | fd = log_fd; |
102 | 0 | break; |
103 | 1 | } |
104 | | |
105 | 1 | const char *p; |
106 | 1 | while ((p = strchr(str_c(data), '\n')) != NULL) { |
107 | 0 | size_t line_len = p - str_c(data) + 1; |
108 | 0 | if (log_fd_write(fd, str_data(data), line_len) < 0) |
109 | 0 | return -1; |
110 | | /* delete the written line, not including the log prefix */ |
111 | 0 | str_delete(data, prefix_len, line_len - prefix_len); |
112 | 0 | } |
113 | | |
114 | 1 | str_append_c(data, '\n'); |
115 | 1 | return log_fd_write(fd, str_data(data), str_len(data)); |
116 | 1 | } |
117 | | |
118 | | static void default_on_handler_failure(const struct failure_context *ctx) |
119 | 0 | { |
120 | 0 | const char *log_type = "info"; |
121 | 0 | switch (ctx->type) { |
122 | 0 | case LOG_TYPE_DEBUG: |
123 | 0 | log_type = "debug"; |
124 | | /* fall through */ |
125 | 0 | case LOG_TYPE_INFO: |
126 | | /* we failed to log to info/debug log, try to log the |
127 | | write error to error log - maybe that'll work. */ |
128 | 0 | i_fatal_status(FATAL_LOGWRITE, "write() failed to %s log: %m", |
129 | 0 | log_type); |
130 | 0 | default: |
131 | 0 | failure_exit(FATAL_LOGWRITE); |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | static int ATTR_FORMAT(2, 0) |
136 | | syslog_write(const struct failure_context *ctx, |
137 | | const char *format, va_list args) |
138 | 0 | { |
139 | 0 | string_t *data = t_str_new(128); |
140 | 0 | if (ctx->type == LOG_TYPE_INFO) { |
141 | 0 | if (ctx->log_prefix != NULL) |
142 | 0 | str_append(data, log_prefix_sanitize(ctx->log_prefix)); |
143 | 0 | else if (log_prefix != NULL) |
144 | 0 | str_append(data, log_prefix); |
145 | 0 | } else { |
146 | 0 | log_prefix_add(ctx, data); |
147 | 0 | } |
148 | 0 | size_t prefix_len = str_len(data); |
149 | 0 | str_vprintfa(data, format, args); |
150 | |
|
151 | 0 | int level = LOG_ERR; |
152 | 0 | switch (ctx->type) { |
153 | 0 | case LOG_TYPE_DEBUG: |
154 | 0 | level = LOG_DEBUG; |
155 | 0 | break; |
156 | 0 | case LOG_TYPE_INFO: |
157 | 0 | level = LOG_INFO; |
158 | 0 | break; |
159 | 0 | case LOG_TYPE_WARNING: |
160 | 0 | level = LOG_WARNING; |
161 | 0 | break; |
162 | 0 | case LOG_TYPE_ERROR: |
163 | 0 | level = LOG_ERR; |
164 | 0 | break; |
165 | 0 | case LOG_TYPE_FATAL: |
166 | 0 | case LOG_TYPE_PANIC: |
167 | 0 | level = LOG_CRIT; |
168 | 0 | break; |
169 | 0 | case LOG_TYPE_COUNT: |
170 | 0 | case LOG_TYPE_OPTION: |
171 | 0 | i_unreached(); |
172 | 0 | } |
173 | 0 | char *p; |
174 | 0 | while ((p = strchr(str_c_modifiable(data) + prefix_len, '\n')) != NULL) { |
175 | 0 | size_t line_len = p - str_c_modifiable(data) + 1; |
176 | 0 | *p = '\0'; |
177 | 0 | syslog(level, "%s", str_c(data)); |
178 | | /* delete the written line, not including the log prefix */ |
179 | 0 | i_assert(line_len > prefix_len); |
180 | 0 | str_delete(data, prefix_len, line_len - prefix_len); |
181 | 0 | } |
182 | | |
183 | 0 | syslog(level, "%s", str_c(data)); |
184 | 0 | return 0; |
185 | 0 | } |
186 | | |
187 | | static void syslog_on_handler_failure(const struct failure_context *ctx ATTR_UNUSED) |
188 | 0 | { |
189 | 0 | failure_exit(FATAL_LOGERROR); |
190 | 0 | } |
191 | | |
192 | | static int ATTR_FORMAT(2, 0) |
193 | | internal_write(const struct failure_context *ctx, |
194 | | const char *format, va_list args) |
195 | 0 | { |
196 | 0 | string_t *data; |
197 | 0 | unsigned char log_type = ctx->type + 1; |
198 | |
|
199 | 0 | if (ctx->log_prefix != NULL) { |
200 | 0 | log_type |= LOG_TYPE_FLAG_DISABLE_LOG_PREFIX; |
201 | 0 | if (ctx->log_prefix_type_pos != 0) |
202 | 0 | log_type |= LOG_TYPE_FLAG_PREFIX_LEN; |
203 | 0 | } else if (!log_prefix_sent && log_prefix != NULL) { |
204 | 0 | if (i_failure_send_option_forced("prefix", log_prefix) < 0) { |
205 | | /* Failed to write log prefix. The log message writing |
206 | | would likely fail as well, but don't even try since |
207 | | the log prefix would be wrong. */ |
208 | 0 | return -1; |
209 | 0 | } |
210 | 0 | log_prefix_sent = TRUE; |
211 | 0 | } |
212 | | |
213 | 0 | data = t_str_new(128); |
214 | 0 | str_printfa(data, "\001%c%s ", log_type, my_pid); |
215 | 0 | if ((log_type & LOG_TYPE_FLAG_PREFIX_LEN) != 0) |
216 | 0 | str_printfa(data, "%u ", ctx->log_prefix_type_pos); |
217 | 0 | if (ctx->log_prefix != NULL) |
218 | 0 | str_append(data, log_prefix_sanitize(ctx->log_prefix)); |
219 | 0 | size_t prefix_len = str_len(data); |
220 | |
|
221 | 0 | str_vprintfa(data, format, args); |
222 | |
|
223 | 0 | if (str_len(data)+1 <= PIPE_BUF && strchr(str_c(data), '\n') == NULL) { |
224 | | /* fast path: Log line is short enough and has no LFs */ |
225 | 0 | str_append_c(data, '\n'); |
226 | 0 | return log_fd_write(STDERR_FILENO, |
227 | 0 | str_data(data), str_len(data)); |
228 | 0 | } |
229 | 0 | return internal_send_split(data, prefix_len); |
230 | 0 | } |
231 | | |
232 | | static void internal_on_handler_failure(const struct failure_context *ctx ATTR_UNUSED) |
233 | 0 | { |
234 | 0 | failure_exit(FATAL_LOGERROR); |
235 | 0 | } |
236 | | |
237 | | static struct failure_handler_vfuncs default_handler_vfuncs = { |
238 | | .write = &default_write, |
239 | | .on_handler_failure = &default_on_handler_failure, |
240 | | }; |
241 | | |
242 | | static struct failure_handler_vfuncs syslog_handler_vfuncs = { |
243 | | .write = &syslog_write, |
244 | | .on_handler_failure = &syslog_on_handler_failure, |
245 | | }; |
246 | | |
247 | | static struct failure_handler_vfuncs internal_handler_vfuncs = { |
248 | | .write = &internal_write, |
249 | | .on_handler_failure = &internal_on_handler_failure, |
250 | | }; |
251 | | |
252 | | struct failure_handler_config failure_handler = { .fatal_err_reset = FATAL_LOGWRITE, |
253 | | .v = &default_handler_vfuncs }; |
254 | | |
255 | | static int common_handler(const struct failure_context *ctx, |
256 | | const char *format, va_list args) |
257 | 1 | { |
258 | 1 | static int recursed = 0; |
259 | 1 | int ret; |
260 | | |
261 | 1 | if (recursed >= 2) { |
262 | | /* we're being called from some signal handler or we ran |
263 | | out of memory */ |
264 | 0 | return -1; |
265 | 0 | } |
266 | 1 | recursed++; |
267 | | |
268 | 1 | T_BEGIN { |
269 | 1 | ret = failure_handler.v->write(ctx, format, args); |
270 | 1 | } T_END; |
271 | | |
272 | 1 | if (ret < 0 && failure_ignore_errors) |
273 | 0 | ret = 0; |
274 | | |
275 | 1 | recursed--; |
276 | 1 | return ret; |
277 | 1 | } |
278 | | |
279 | | static void error_handler_real(const struct failure_context *ctx, |
280 | | const char *format, va_list args) |
281 | 1 | { |
282 | 1 | if (common_handler(ctx, format, args) < 0) |
283 | 0 | failure_handler.v->on_handler_failure(ctx); |
284 | 1 | if (ctx->type == LOG_TYPE_ERROR && coredump_on_error) |
285 | 0 | abort(); |
286 | 1 | } |
287 | | |
288 | | static void ATTR_FORMAT(2, 0) |
289 | | i_internal_error_handler(const struct failure_context *ctx, |
290 | | const char *format, va_list args); |
291 | | |
292 | | /* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */ |
293 | | static const char * |
294 | | get_log_stamp_format(const char *format_arg, unsigned int timestamp_usecs) |
295 | | ATTR_FORMAT_ARG(1); |
296 | | |
297 | | static const char *get_log_stamp_format(const char *format_arg ATTR_UNUSED, |
298 | | unsigned int timestamp_usecs) |
299 | 0 | { |
300 | 0 | if (log_stamp_format_suffix == NULL) |
301 | 0 | return log_stamp_format; |
302 | 0 | return t_strdup_printf("%s%06u%s", log_stamp_format, |
303 | 0 | timestamp_usecs, log_stamp_format_suffix); |
304 | 0 | } |
305 | | |
306 | | void failure_exit(int status) |
307 | 0 | { |
308 | 0 | static bool recursed = FALSE; |
309 | |
|
310 | 0 | if (failure_exit_callback != NULL && !recursed) { |
311 | 0 | recursed = TRUE; |
312 | 0 | failure_exit_callback(&status); |
313 | 0 | } |
314 | 0 | lib_exit(status); |
315 | 0 | } |
316 | | |
317 | | static void log_timestamp_add(const struct failure_context *ctx, string_t *str) |
318 | 1 | { |
319 | 1 | const struct tm *tm = ctx->timestamp; |
320 | 1 | char buf[256]; |
321 | 1 | struct timeval now; |
322 | | |
323 | 1 | if (log_stamp_format != NULL) { |
324 | 0 | if (tm == NULL) { |
325 | 0 | i_gettimeofday(&now); |
326 | 0 | tm = localtime(&now.tv_sec); |
327 | 0 | } else { |
328 | 0 | now.tv_usec = ctx->timestamp_usecs; |
329 | 0 | } |
330 | |
|
331 | 0 | if (strftime(buf, sizeof(buf), |
332 | 0 | get_log_stamp_format("unused", now.tv_usec), tm) > 0) |
333 | 0 | str_append(str, buf); |
334 | 0 | } |
335 | 1 | } |
336 | | |
337 | | static void log_prefix_add(const struct failure_context *ctx, string_t *str) |
338 | 1 | { |
339 | 1 | if (ctx->log_prefix == NULL) { |
340 | | /* use global log prefix */ |
341 | 1 | if (log_prefix != NULL) |
342 | 0 | str_append(str, log_prefix); |
343 | 1 | str_append(str, failure_log_type_prefixes[ctx->type]); |
344 | 1 | } else if (ctx->log_prefix_type_pos == 0) { |
345 | 0 | str_append(str, log_prefix_sanitize(ctx->log_prefix)); |
346 | 0 | str_append(str, failure_log_type_prefixes[ctx->type]); |
347 | 0 | } else { |
348 | 0 | const char *prefix = log_prefix_sanitize(ctx->log_prefix); |
349 | 0 | i_assert(ctx->log_prefix_type_pos <= strlen(prefix)); |
350 | 0 | str_append_data(str, prefix, ctx->log_prefix_type_pos); |
351 | 0 | str_append(str, failure_log_type_prefixes[ctx->type]); |
352 | 0 | str_append(str, prefix + ctx->log_prefix_type_pos); |
353 | 0 | } |
354 | 1 | } |
355 | | |
356 | | static void fd_wait_writable(int fd) |
357 | 0 | { |
358 | 0 | struct pollfd pfd = { |
359 | 0 | .fd = fd, |
360 | 0 | .events = POLLOUT | POLLERR | POLLHUP | POLLNVAL, |
361 | 0 | }; |
362 | | |
363 | | /* Use poll() instead of ioloop, because we don't want to recurse back |
364 | | to log writing in case something fails. */ |
365 | 0 | if (poll(&pfd, 1, -1) < 0 && errno != EINTR) { |
366 | | /* Unexpected error. We're already blocking on log writes, |
367 | | so we can't log it. */ |
368 | 0 | abort(); |
369 | 0 | } |
370 | 0 | } |
371 | | |
372 | | static int log_fd_write(int fd, const unsigned char *data, size_t len) |
373 | 1 | { |
374 | 1 | ssize_t ret; |
375 | 1 | unsigned int prev_signal_term_counter = signal_term_counter; |
376 | 1 | unsigned int terminal_eintr_count = 0; |
377 | 1 | const char *old_title = NULL; |
378 | 1 | bool failed = FALSE, process_title_changed = FALSE; |
379 | | |
380 | 1 | while (!failed && |
381 | 1 | (ret = write(fd, data, len)) != (ssize_t)len) { |
382 | 0 | if (ret > 0) { |
383 | | /* some was written, continue.. */ |
384 | 0 | data += ret; |
385 | 0 | len -= ret; |
386 | 0 | continue; |
387 | 0 | } |
388 | 0 | if (ret == 0) { |
389 | | /* out of disk space? */ |
390 | 0 | errno = ENOSPC; |
391 | 0 | failed = TRUE; |
392 | 0 | break; |
393 | 0 | } |
394 | 0 | switch (errno) { |
395 | 0 | case EAGAIN: { |
396 | | /* Log fd is nonblocking - wait until we can write more. |
397 | | Indicate in process title that the process is waiting |
398 | | because it's waiting on the log. |
399 | | |
400 | | Remember that the log fd is shared across processes, |
401 | | which also means the log fd flags are shared. So if |
402 | | one process changes the O_NONBLOCK flag for a log fd, |
403 | | all the processes see the change. To avoid problems, |
404 | | we'll wait using poll() instead of changing the |
405 | | O_NONBLOCK flag. */ |
406 | 0 | if (!process_title_changed) { |
407 | 0 | const char *title; |
408 | |
|
409 | 0 | process_title_changed = TRUE; |
410 | 0 | old_title = t_strdup(process_title_get()); |
411 | 0 | if (old_title == NULL) |
412 | 0 | title = "[blocking on log write]"; |
413 | 0 | else |
414 | 0 | title = t_strdup_printf("%s - [blocking on log write]", |
415 | 0 | old_title); |
416 | 0 | process_title_set(title); |
417 | 0 | } |
418 | 0 | fd_wait_writable(fd); |
419 | 0 | break; |
420 | 0 | } |
421 | 0 | case EINTR: |
422 | 0 | if (prev_signal_term_counter == signal_term_counter) { |
423 | | /* non-terminal signal. ignore. */ |
424 | 0 | } else if (terminal_eintr_count++ == 0) { |
425 | | /* we'd rather not die in the middle of |
426 | | writing to log. try again once more */ |
427 | 0 | } else { |
428 | | /* received two terminal signals. |
429 | | someone wants us dead. */ |
430 | 0 | failed = TRUE; |
431 | 0 | break; |
432 | 0 | } |
433 | 0 | break; |
434 | 0 | default: |
435 | 0 | failed = TRUE; |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | prev_signal_term_counter = signal_term_counter; |
439 | 0 | } |
440 | 1 | if (process_title_changed) |
441 | 0 | process_title_set(old_title); |
442 | 1 | return failed ? -1 : 0; |
443 | 1 | } |
444 | | |
445 | | static void ATTR_NORETURN |
446 | | default_fatal_finish(enum log_type type, int status) |
447 | 0 | { |
448 | 0 | const char *backtrace, *error; |
449 | 0 | static int recursed = 0; |
450 | |
|
451 | 0 | recursed++; |
452 | 0 | if ((type == LOG_TYPE_PANIC || status == FATAL_OUTOFMEM) && |
453 | 0 | recursed == 1) { |
454 | 0 | if (backtrace_get(&backtrace, &error) == 0) |
455 | 0 | i_error("Raw backtrace: %s", backtrace); |
456 | 0 | } |
457 | 0 | recursed--; |
458 | |
|
459 | 0 | if (type == LOG_TYPE_PANIC || getenv("CORE_ERROR") != NULL || |
460 | 0 | (status == FATAL_OUTOFMEM && getenv("CORE_OUTOFMEM") != NULL)) |
461 | 0 | abort(); |
462 | 0 | else |
463 | 0 | failure_exit(status); |
464 | 0 | } |
465 | | |
466 | | static void ATTR_NORETURN fatal_handler_real(const struct failure_context *ctx, |
467 | | const char *format, va_list args) |
468 | 0 | { |
469 | 0 | int status = ctx->exit_status; |
470 | 0 | if (common_handler(ctx, format, args) < 0 && |
471 | 0 | status == FATAL_DEFAULT) |
472 | 0 | status = failure_handler.fatal_err_reset; |
473 | 0 | default_fatal_finish(ctx->type, status); |
474 | 0 | } |
475 | | |
476 | | void default_fatal_handler(const struct failure_context *ctx, |
477 | | const char *format, va_list args) |
478 | 0 | { |
479 | 0 | failure_handler.v = &default_handler_vfuncs; |
480 | 0 | failure_handler.fatal_err_reset = FATAL_LOGWRITE; |
481 | 0 | fatal_handler_real(ctx, format, args); |
482 | 0 | } |
483 | | |
484 | | void default_error_handler(const struct failure_context *ctx, |
485 | | const char *format, va_list args) |
486 | 1 | { |
487 | 1 | failure_handler.v = &default_handler_vfuncs; |
488 | 1 | failure_handler.fatal_err_reset = FATAL_LOGWRITE; |
489 | 1 | error_handler_real(ctx, format, args); |
490 | 1 | } |
491 | | |
492 | | void i_log_type(const struct failure_context *ctx, const char *format, ...) |
493 | 0 | { |
494 | 0 | va_list args; |
495 | |
|
496 | 0 | va_start(args, format); |
497 | 0 | i_log_typev(ctx, format, args); |
498 | 0 | va_end(args); |
499 | 0 | } |
500 | | |
501 | | void i_log_typev(const struct failure_context *ctx, const char *format, |
502 | | va_list args) |
503 | 0 | { |
504 | 0 | switch (ctx->type) { |
505 | 0 | case LOG_TYPE_DEBUG: |
506 | 0 | debug_handler(ctx, format, args); |
507 | 0 | break; |
508 | 0 | case LOG_TYPE_INFO: |
509 | 0 | info_handler(ctx, format, args); |
510 | 0 | break; |
511 | 0 | default: |
512 | 0 | error_handler(ctx, format, args); |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | | void i_panic(const char *format, ...) |
517 | 0 | { |
518 | 0 | struct failure_context ctx; |
519 | 0 | va_list args; |
520 | |
|
521 | 0 | lib_set_clean_exit(TRUE); |
522 | 0 | i_zero(&ctx); |
523 | 0 | ctx.type = LOG_TYPE_PANIC; |
524 | |
|
525 | 0 | va_start(args, format); |
526 | 0 | fatal_handler(&ctx, format, args); |
527 | 0 | i_unreached(); |
528 | | /*va_end(args);*/ |
529 | 0 | } |
530 | | |
531 | | void i_fatal(const char *format, ...) |
532 | 0 | { |
533 | 0 | struct failure_context ctx; |
534 | 0 | va_list args; |
535 | |
|
536 | 0 | lib_set_clean_exit(TRUE); |
537 | 0 | i_zero(&ctx); |
538 | 0 | ctx.type = LOG_TYPE_FATAL; |
539 | 0 | ctx.exit_status = FATAL_DEFAULT; |
540 | |
|
541 | 0 | va_start(args, format); |
542 | 0 | fatal_handler(&ctx, format, args); |
543 | 0 | i_unreached(); |
544 | | /*va_end(args);*/ |
545 | 0 | } |
546 | | |
547 | | void i_fatal_status(int status, const char *format, ...) |
548 | 0 | { |
549 | 0 | struct failure_context ctx; |
550 | 0 | va_list args; |
551 | |
|
552 | 0 | lib_set_clean_exit(TRUE); |
553 | 0 | i_zero(&ctx); |
554 | 0 | ctx.type = LOG_TYPE_FATAL; |
555 | 0 | ctx.exit_status = status; |
556 | |
|
557 | 0 | va_start(args, format); |
558 | 0 | fatal_handler(&ctx, format, args); |
559 | 0 | i_unreached(); |
560 | | /*va_end(args);*/ |
561 | 0 | } |
562 | | |
563 | | void i_error(const char *format, ...) |
564 | 0 | { |
565 | 0 | int old_errno = errno; |
566 | 0 | va_list args; |
567 | |
|
568 | 0 | va_start(args, format); |
569 | 0 | error_handler(&failure_ctx_error, format, args); |
570 | 0 | va_end(args); |
571 | |
|
572 | 0 | errno = old_errno; |
573 | 0 | } |
574 | | |
575 | | void i_warning(const char *format, ...) |
576 | 0 | { |
577 | 0 | int old_errno = errno; |
578 | 0 | va_list args; |
579 | |
|
580 | 0 | va_start(args, format); |
581 | 0 | error_handler(&failure_ctx_warning, format, args); |
582 | 0 | va_end(args); |
583 | |
|
584 | 0 | errno = old_errno; |
585 | 0 | } |
586 | | |
587 | | void i_info(const char *format, ...) |
588 | 1 | { |
589 | 1 | int old_errno = errno; |
590 | 1 | va_list args; |
591 | | |
592 | 1 | va_start(args, format); |
593 | 1 | info_handler(&failure_ctx_info, format, args); |
594 | 1 | va_end(args); |
595 | | |
596 | 1 | errno = old_errno; |
597 | 1 | } |
598 | | |
599 | | void i_debug(const char *format, ...) |
600 | 0 | { |
601 | 0 | int old_errno = errno; |
602 | 0 | va_list args; |
603 | |
|
604 | 0 | va_start(args, format); |
605 | 0 | debug_handler(&failure_ctx_debug, format, args); |
606 | 0 | va_end(args); |
607 | |
|
608 | 0 | errno = old_errno; |
609 | 0 | } |
610 | | |
611 | | void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN) |
612 | 0 | { |
613 | 0 | fatal_handler = callback; |
614 | 0 | } |
615 | | |
616 | | void i_set_error_handler(failure_callback_t *callback) |
617 | 0 | { |
618 | 0 | coredump_on_error = getenv("CORE_ERROR") != NULL; |
619 | 0 | error_handler = callback; |
620 | 0 | } |
621 | | |
622 | | void i_set_info_handler(failure_callback_t *callback) |
623 | 0 | { |
624 | 0 | info_handler = callback; |
625 | 0 | } |
626 | | |
627 | | void i_set_debug_handler(failure_callback_t *callback) |
628 | 0 | { |
629 | 0 | debug_handler = callback; |
630 | 0 | } |
631 | | |
632 | | void i_get_failure_handlers(failure_callback_t **fatal_callback_r, |
633 | | failure_callback_t **error_callback_r, |
634 | | failure_callback_t **info_callback_r, |
635 | | failure_callback_t **debug_callback_r) |
636 | 0 | { |
637 | 0 | *fatal_callback_r = fatal_handler; |
638 | 0 | *error_callback_r = error_handler; |
639 | 0 | *info_callback_r = info_handler; |
640 | 0 | *debug_callback_r = debug_handler; |
641 | 0 | } |
642 | | |
643 | | void i_syslog_fatal_handler(const struct failure_context *ctx, |
644 | | const char *format, va_list args) |
645 | 0 | { |
646 | 0 | failure_handler.v = &syslog_handler_vfuncs; |
647 | 0 | failure_handler.fatal_err_reset = FATAL_LOGERROR; |
648 | 0 | fatal_handler_real(ctx, format, args); |
649 | 0 | } |
650 | | |
651 | | void i_syslog_error_handler(const struct failure_context *ctx, |
652 | | const char *format, va_list args) |
653 | 0 | { |
654 | 0 | failure_handler.v = &syslog_handler_vfuncs; |
655 | 0 | failure_handler.fatal_err_reset = FATAL_LOGERROR; |
656 | 0 | error_handler_real(ctx, format, args); |
657 | 0 | } |
658 | | |
659 | | void i_set_failure_syslog(const char *ident, int options, int facility) |
660 | 0 | { |
661 | | /* openlog() keeps using the pointer directly. Duplicate it in case |
662 | | caller frees the string. */ |
663 | 0 | i_free(syslog_ident); |
664 | 0 | syslog_ident = i_strdup(ident); |
665 | 0 | openlog(syslog_ident, options, facility); |
666 | |
|
667 | 0 | i_set_fatal_handler(i_syslog_fatal_handler); |
668 | 0 | i_set_error_handler(i_syslog_error_handler); |
669 | 0 | i_set_info_handler(i_syslog_error_handler); |
670 | 0 | i_set_debug_handler(i_syslog_error_handler); |
671 | 0 | } |
672 | | |
673 | | static void open_log_file(int *fd, const char *path) |
674 | 0 | { |
675 | 0 | const char *str; |
676 | |
|
677 | 0 | if (log_fd_can_close(*fd)) { |
678 | 0 | if (close(*fd) < 0) { |
679 | 0 | str = t_strdup_printf("close(%d) failed: %m\n", *fd); |
680 | 0 | (void)write_full(STDERR_FILENO, str, strlen(str)); |
681 | 0 | } |
682 | 0 | } |
683 | |
|
684 | 0 | if (path == NULL || strcmp(path, "/dev/stderr") == 0) |
685 | 0 | *fd = STDERR_FILENO; |
686 | 0 | else if (strcmp(path, "/dev/stdout") == 0) |
687 | 0 | *fd = STDOUT_FILENO; |
688 | 0 | else { |
689 | 0 | *fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600); |
690 | 0 | if (*fd == -1) { |
691 | 0 | *fd = STDERR_FILENO; |
692 | 0 | str = t_strdup_printf("Can't open log file %s: %m\n", |
693 | 0 | path); |
694 | 0 | (void)write_full(STDERR_FILENO, str, strlen(str)); |
695 | 0 | if (fd == &log_fd) |
696 | 0 | failure_exit(FATAL_LOGOPEN); |
697 | 0 | else |
698 | 0 | i_fatal_status(FATAL_LOGOPEN, "%s", str); |
699 | 0 | } |
700 | 0 | fd_close_on_exec(*fd, TRUE); |
701 | 0 | } |
702 | 0 | } |
703 | | |
704 | | void i_set_failure_file(const char *path, const char *prefix) |
705 | 0 | { |
706 | 0 | i_set_failure_prefix("%s", prefix); |
707 | |
|
708 | 0 | if (log_fd_can_close(log_info_fd) && log_info_fd != log_fd) { |
709 | 0 | if (close(log_info_fd) < 0) |
710 | 0 | i_error("close(%d) failed: %m", log_info_fd); |
711 | 0 | } |
712 | |
|
713 | 0 | if (log_fd_can_close(log_debug_fd) && log_debug_fd != log_info_fd && |
714 | 0 | log_debug_fd != log_fd) { |
715 | 0 | if (close(log_debug_fd) < 0) |
716 | 0 | i_error("close(%d) failed: %m", log_debug_fd); |
717 | 0 | } |
718 | |
|
719 | 0 | open_log_file(&log_fd, path); |
720 | | /* if info/debug logs are elsewhere, i_set_info/debug_file() |
721 | | overrides these later. */ |
722 | 0 | log_info_fd = log_fd; |
723 | 0 | log_debug_fd = log_fd; |
724 | |
|
725 | 0 | i_set_fatal_handler(default_fatal_handler); |
726 | 0 | i_set_error_handler(default_error_handler); |
727 | 0 | i_set_info_handler(default_error_handler); |
728 | 0 | i_set_debug_handler(default_error_handler); |
729 | 0 | } |
730 | | |
731 | | static int i_failure_send_option_forced(const char *key, const char *value) |
732 | 0 | { |
733 | 0 | const char *str; |
734 | |
|
735 | 0 | str = t_strdup_printf("\001%c%s %s=%s\n", LOG_TYPE_OPTION+1, |
736 | 0 | my_pid, key, value); |
737 | 0 | return log_fd_write(STDERR_FILENO, (const unsigned char *)str, |
738 | 0 | strlen(str)); |
739 | 0 | } |
740 | | |
741 | | static void i_failure_send_option(const char *key, const char *value) |
742 | 0 | { |
743 | 0 | if (error_handler == i_internal_error_handler) |
744 | 0 | (void)i_failure_send_option_forced(key, value); |
745 | 0 | } |
746 | | |
747 | | void i_set_failure_prefix(const char *prefix_fmt, ...) |
748 | 0 | { |
749 | 0 | va_list args; |
750 | |
|
751 | 0 | va_start(args, prefix_fmt); |
752 | 0 | i_free(log_prefix); |
753 | 0 | T_BEGIN { |
754 | 0 | log_prefix = i_strdup(log_prefix_sanitize( |
755 | 0 | t_strdup_vprintf(prefix_fmt, args))); |
756 | 0 | } T_END; |
757 | 0 | va_end(args); |
758 | |
|
759 | 0 | log_prefix_sent = FALSE; |
760 | 0 | } |
761 | | |
762 | | void i_unset_failure_prefix(void) |
763 | 0 | { |
764 | 0 | i_free(log_prefix); |
765 | 0 | log_prefix = i_strdup(""); |
766 | 0 | log_prefix_sent = FALSE; |
767 | 0 | } |
768 | | |
769 | | const char *i_get_failure_prefix(void) |
770 | 0 | { |
771 | 0 | return log_prefix != NULL ? log_prefix : ""; |
772 | 0 | } |
773 | | |
774 | | static int internal_send_split(string_t *full_str, size_t prefix_len) |
775 | 0 | { |
776 | | /* This function splits the log line into PIPE_BUF sized blocks, so |
777 | | the log process doesn't see partial lines. The log prefix is |
778 | | repeated for each sent line. However, if the log prefix is |
779 | | excessively long, we're still going to send the log lines even |
780 | | if they are longer than PIPE_BUF. LINE_MIN_TEXT_LEN controls the |
781 | | minimum number of bytes we're going to send of the actual log line |
782 | | regardless of the log prefix length. (Alternative solution could be |
783 | | to just forcibly split the line to PIPE_BUF length blocks without |
784 | | repeating the log prefix for subsequent lines.) */ |
785 | 0 | #define LINE_MIN_TEXT_LEN 128 |
786 | | #if LINE_MIN_TEXT_LEN >= PIPE_BUF |
787 | | # error LINE_MIN_TEXT_LEN too large |
788 | | #endif |
789 | 0 | string_t *str; |
790 | 0 | size_t max_text_len, pos = prefix_len; |
791 | |
|
792 | 0 | str = t_str_new(PIPE_BUF); |
793 | 0 | str_append_data(str, str_data(full_str), prefix_len); |
794 | 0 | if (prefix_len < PIPE_BUF) { |
795 | 0 | max_text_len = I_MAX(PIPE_BUF - prefix_len - 1, |
796 | 0 | LINE_MIN_TEXT_LEN); |
797 | 0 | } else { |
798 | 0 | max_text_len = LINE_MIN_TEXT_LEN; |
799 | 0 | } |
800 | |
|
801 | 0 | while (pos < str_len(full_str)) { |
802 | 0 | str_truncate(str, prefix_len); |
803 | 0 | const char *text = str_c(full_str) + pos; |
804 | 0 | const char *lf = strchr(text, '\n'); |
805 | 0 | size_t line_len; |
806 | 0 | if (lf == NULL || (size_t)(lf - text) > max_text_len) { |
807 | 0 | str_append_max(str, text, max_text_len); |
808 | 0 | str_append_c(str, '\n'); |
809 | 0 | line_len = max_text_len; |
810 | 0 | } else { |
811 | | /* write up to and including the LF */ |
812 | 0 | line_len = lf - text + 1; |
813 | 0 | str_append_data(str, text, line_len); |
814 | 0 | } |
815 | 0 | if (log_fd_write(STDERR_FILENO, |
816 | 0 | str_data(str), str_len(str)) < 0) |
817 | 0 | return -1; |
818 | 0 | pos += line_len; |
819 | 0 | } |
820 | 0 | return 0; |
821 | 0 | } |
822 | | |
823 | | |
824 | | static bool line_parse_prefix(const char *line, enum log_type *log_type_r, |
825 | | bool *replace_prefix_r, bool *have_prefix_len_r) |
826 | 0 | { |
827 | 0 | if (*line != 1) |
828 | 0 | return FALSE; |
829 | | |
830 | 0 | unsigned char log_type = (line[1] & 0x3f); |
831 | 0 | if (log_type == '\0') { |
832 | 0 | i_warning("Broken log line follows (type=NUL)"); |
833 | 0 | return FALSE; |
834 | 0 | } |
835 | 0 | log_type--; |
836 | |
|
837 | 0 | if (log_type > LOG_TYPE_OPTION) { |
838 | 0 | i_warning("Broken log line follows (type=%d)", log_type); |
839 | 0 | return FALSE; |
840 | 0 | } |
841 | 0 | *log_type_r = log_type; |
842 | 0 | *replace_prefix_r = (line[1] & LOG_TYPE_FLAG_DISABLE_LOG_PREFIX) != 0; |
843 | 0 | *have_prefix_len_r = (line[1] & LOG_TYPE_FLAG_PREFIX_LEN) != 0; |
844 | 0 | return TRUE; |
845 | 0 | } |
846 | | |
847 | | void i_failure_parse_line(const char *line, struct failure_line *failure) |
848 | 0 | { |
849 | 0 | bool have_prefix_len = FALSE; |
850 | |
|
851 | 0 | i_zero(failure); |
852 | 0 | if (!line_parse_prefix(line, &failure->log_type, |
853 | 0 | &failure->disable_log_prefix, |
854 | 0 | &have_prefix_len)) { |
855 | 0 | failure->log_type = LOG_TYPE_ERROR; |
856 | 0 | failure->text = line; |
857 | 0 | return; |
858 | 0 | } |
859 | | |
860 | 0 | line += 2; |
861 | 0 | failure->text = line; |
862 | 0 | while (*line >= '0' && *line <= '9') { |
863 | 0 | failure->pid = failure->pid*10 + (*line - '0'); |
864 | 0 | line++; |
865 | 0 | } |
866 | 0 | if (*line != ' ') { |
867 | | /* some old protocol? */ |
868 | 0 | failure->pid = 0; |
869 | 0 | return; |
870 | 0 | } |
871 | 0 | line++; |
872 | |
|
873 | 0 | if (have_prefix_len) { |
874 | 0 | if (str_parse_uint(line, &failure->log_prefix_len, &line) < 0 || |
875 | 0 | line[0] != ' ') { |
876 | | /* unexpected, but ignore */ |
877 | 0 | } else { |
878 | 0 | line++; |
879 | 0 | if (failure->log_prefix_len > strlen(line)) { |
880 | | /* invalid */ |
881 | 0 | failure->log_prefix_len = 0; |
882 | 0 | } |
883 | 0 | } |
884 | 0 | } |
885 | 0 | failure->text = line; |
886 | 0 | } |
887 | | |
888 | | static void ATTR_NORETURN ATTR_FORMAT(2, 0) |
889 | | i_internal_fatal_handler(const struct failure_context *ctx, |
890 | | const char *format, va_list args) |
891 | 0 | { |
892 | 0 | failure_handler.v = &internal_handler_vfuncs; |
893 | 0 | failure_handler.fatal_err_reset = FATAL_LOGERROR; |
894 | 0 | fatal_handler_real(ctx, format, args); |
895 | | |
896 | |
|
897 | 0 | } |
898 | | |
899 | | static void |
900 | | i_internal_error_handler(const struct failure_context *ctx, |
901 | | const char *format, va_list args) |
902 | 0 | { |
903 | 0 | failure_handler.v = &internal_handler_vfuncs; |
904 | 0 | failure_handler.fatal_err_reset = FATAL_LOGERROR; |
905 | 0 | error_handler_real(ctx, format, args); |
906 | 0 | } |
907 | | |
908 | | void i_set_failure_internal(void) |
909 | 0 | { |
910 | 0 | fd_set_nonblock(STDERR_FILENO, TRUE); |
911 | 0 | i_set_fatal_handler(i_internal_fatal_handler); |
912 | 0 | i_set_error_handler(i_internal_error_handler); |
913 | 0 | i_set_info_handler(i_internal_error_handler); |
914 | 0 | i_set_debug_handler(i_internal_error_handler); |
915 | 0 | } |
916 | | |
917 | | bool i_failure_handler_is_internal(failure_callback_t *const callback) |
918 | 0 | { |
919 | 0 | return callback == i_internal_fatal_handler || |
920 | 0 | callback == i_internal_error_handler; |
921 | 0 | } |
922 | | |
923 | | void i_set_failure_ignore_errors(bool ignore) |
924 | 0 | { |
925 | 0 | failure_ignore_errors = ignore; |
926 | 0 | } |
927 | | |
928 | | void i_set_info_file(const char *path) |
929 | 0 | { |
930 | 0 | if (log_info_fd == log_fd) |
931 | 0 | log_info_fd = STDERR_FILENO; |
932 | |
|
933 | 0 | open_log_file(&log_info_fd, path); |
934 | 0 | info_handler = default_error_handler; |
935 | | /* write debug-level messages to the info_log_path, |
936 | | until i_set_debug_file() was called */ |
937 | 0 | log_debug_fd = log_info_fd; |
938 | 0 | i_set_debug_handler(default_error_handler); |
939 | 0 | } |
940 | | |
941 | | void i_set_debug_file(const char *path) |
942 | 0 | { |
943 | 0 | if (log_debug_fd == log_fd || log_debug_fd == log_info_fd) |
944 | 0 | log_debug_fd = STDERR_FILENO; |
945 | |
|
946 | 0 | open_log_file(&log_debug_fd, path); |
947 | 0 | debug_handler = default_error_handler; |
948 | 0 | } |
949 | | |
950 | | bool i_failure_have_stdout_logs(void) |
951 | 0 | { |
952 | 0 | return log_fd == STDOUT_FILENO || |
953 | 0 | log_info_fd == STDOUT_FILENO || |
954 | 0 | log_debug_fd == STDOUT_FILENO; |
955 | 0 | } |
956 | | |
957 | | void i_set_failure_timestamp_format(const char *fmt) |
958 | 0 | { |
959 | 0 | const char *p; |
960 | |
|
961 | 0 | i_free(log_stamp_format); |
962 | 0 | i_free_and_null(log_stamp_format_suffix); |
963 | |
|
964 | 0 | p = strstr(fmt, "%{usecs}"); |
965 | 0 | if (p == NULL) |
966 | 0 | log_stamp_format = i_strdup(fmt); |
967 | 0 | else { |
968 | 0 | log_stamp_format = i_strdup_until(fmt, p); |
969 | 0 | log_stamp_format_suffix = i_strdup(p + 8); |
970 | 0 | } |
971 | 0 | } |
972 | | |
973 | | void i_set_failure_send_ip(const struct ip_addr *ip) |
974 | 0 | { |
975 | 0 | i_failure_send_option("ip", net_ip2addr(ip)); |
976 | 0 | } |
977 | | |
978 | | void i_set_failure_send_prefix(const char *prefix) |
979 | 0 | { |
980 | 0 | i_failure_send_option("prefix", prefix); |
981 | 0 | } |
982 | | |
983 | | void i_set_failure_exit_callback(void (*callback)(int *status)) |
984 | 0 | { |
985 | 0 | failure_exit_callback = callback; |
986 | 0 | } |
987 | | |
988 | | void failures_deinit(void) |
989 | 0 | { |
990 | 0 | if (log_debug_fd == log_info_fd || log_debug_fd == log_fd) |
991 | 0 | log_debug_fd = STDERR_FILENO; |
992 | |
|
993 | 0 | if (log_info_fd == log_fd) |
994 | 0 | log_info_fd = STDERR_FILENO; |
995 | |
|
996 | 0 | if (log_fd_can_close(log_fd)) { |
997 | 0 | i_close_fd(&log_fd); |
998 | 0 | log_fd = STDERR_FILENO; |
999 | 0 | } |
1000 | |
|
1001 | 0 | if (log_fd_can_close(log_info_fd)) { |
1002 | 0 | i_close_fd(&log_info_fd); |
1003 | 0 | log_info_fd = STDERR_FILENO; |
1004 | 0 | } |
1005 | |
|
1006 | 0 | if (log_fd_can_close(log_debug_fd)) { |
1007 | 0 | i_close_fd(&log_debug_fd); |
1008 | 0 | log_debug_fd = STDERR_FILENO; |
1009 | 0 | } |
1010 | |
|
1011 | 0 | i_free_and_null(log_prefix); |
1012 | 0 | i_free_and_null(log_stamp_format); |
1013 | 0 | i_free_and_null(log_stamp_format_suffix); |
1014 | 0 | i_free(syslog_ident); |
1015 | 0 | } |
1016 | | |
1017 | | #undef i_unreached |
1018 | | void i_unreached(const char *source_filename, int source_linenum) |
1019 | 0 | { |
1020 | 0 | i_panic("file %s: line %d: unreached", source_filename, source_linenum); |
1021 | 0 | } |