#include "lib.h"
#include "ioloop.h"
+#include "istream.h"
#include "hash.h"
#include "str.h"
-#include "istream.h"
+#include "strescape.h"
+#include "time-util.h"
#include "master-service-private.h"
#include "master-service-settings.h"
#include "doveadm.h"
#define LAST_LOG_TYPE LOG_TYPE_PANIC
#define TEST_LOG_MSG_PREFIX "This is Dovecot's "
+#define LOG_ERRORS_FNAME "log-errors"
+#define LOG_TIMESTAMP_FORMAT "%b %d %H:%M:%S"
static void cmd_log_test(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED)
{
}
}
+static void cmd_log_error_write(const char *const *args)
+{
+ /* <type> <timestamp> <prefix> <text> */
+ const char *type_prefix = "?";
+ unsigned int type;
+ time_t t;
+
+ /* find type's prefix */
+ for (type = 0; type < LOG_TYPE_COUNT; type++) {
+ if (strcmp(args[0], failure_log_type_names[type]) == 0) {
+ type_prefix = failure_log_type_prefixes[type];
+ break;
+ }
+ }
+
+ if (str_to_time(args[1], &t) < 0) {
+ i_error("Invalid timestamp: %s", args[1]);
+ t = 0;
+ }
+
+ printf("%s %s%s%s\n", t_strflocaltime(LOG_TIMESTAMP_FORMAT, t),
+ args[2], type_prefix, args[3]);
+}
+
+static void cmd_log_errors(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED)
+{
+ struct istream *input;
+ const char *path, *line, *const *args;
+ int fd;
+
+ path = t_strconcat(doveadm_settings->base_dir,
+ "/"LOG_ERRORS_FNAME, NULL);
+ fd = net_connect_unix(path);
+ if (fd == -1)
+ i_fatal("net_connect_unix(%s) failed: %m", path);
+ net_set_nonblock(fd, FALSE);
+
+ input = i_stream_create_fd(fd, (size_t)-1, TRUE);
+ while ((line = i_stream_read_next_line(input)) != NULL) T_BEGIN {
+ args = t_strsplit_tabescaped(line);
+ if (str_array_length(args) == 4)
+ cmd_log_error_write(args);
+ else
+ i_error("Invalid input from log: %s", line);
+ } T_END;
+ i_stream_destroy(&input);
+}
+
struct doveadm_cmd doveadm_cmd_log[] = {
{ cmd_log_test, "log test", "" },
{ cmd_log_reopen, "log reopen", "" },
- { cmd_log_find, "log find", "[<dir>]" }
+ { cmd_log_find, "log find", "[<dir>]" },
+ { cmd_log_errors, "log errors", "" }
};
void doveadm_register_log_commands(void)
log_DEPENDENCIES = $(LIBDOVECOT_DEPS)
log_SOURCES = \
+ doveadm-connection.c \
log-connection.c \
+ log-error-buffer.c \
log-settings.c \
main.c
noinst_HEADERS = \
- log-connection.h
+ doveadm-connection.h \
+ log-connection.h \
+ log-error-buffer.h
#include "hash.h"
#include "master-interface.h"
#include "master-service.h"
+#include "log-error-buffer.h"
#include "log-connection.h"
#include <stdio.h>
struct log_connection {
struct log_connection *prev, *next;
+ struct log_error_buffer *errorbuf;
int fd;
int listen_fd;
struct io *io;
}
}
+static void
+client_log_ctx(struct log_connection *log,
+ const struct failure_context *ctx, time_t log_time,
+ const char *prefix, const char *text)
+{
+ struct log_error err;
+
+ switch (ctx->type) {
+ case LOG_TYPE_DEBUG:
+ case LOG_TYPE_INFO:
+ case LOG_TYPE_COUNT:
+ case LOG_TYPE_OPTION:
+ break;
+ case LOG_TYPE_WARNING:
+ case LOG_TYPE_ERROR:
+ case LOG_TYPE_FATAL:
+ case LOG_TYPE_PANIC:
+ memset(&err, 0, sizeof(err));
+ err.type = ctx->type;
+ err.timestamp = log_time;
+ err.prefix = prefix;
+ err.text = text;
+ log_error_buffer_add(log->errorbuf, &err);
+ break;
+ }
+ i_set_failure_prefix(prefix);
+ i_log_type(ctx, "%s", text);
+ i_set_failure_prefix("log: ");
+}
+
static void
client_log_fatal(struct log_connection *log, struct log_client *client,
- const char *line, const struct tm *tm)
+ const char *line, time_t log_time, const struct tm *tm)
{
struct failure_context failure_ctx;
const char *prefix = log->default_prefix;
line, net_ip2addr(&client->ip));
}
}
- i_set_failure_prefix(prefix);
- i_log_type(&failure_ctx, "master: %s", line);
- i_set_failure_prefix("log: ");
+ client_log_ctx(log, &failure_ctx, log_time, prefix,
+ t_strconcat("master: ", line, NULL));
}
static void
-log_parse_master_line(const char *line, const struct tm *tm)
+log_parse_master_line(const char *line, time_t log_time, const struct tm *tm)
{
struct log_connection *const *logs, *log;
struct log_client *client;
}
log_client_free(log, client, pid);
} else if (strncmp(line, "FATAL ", 6) == 0) {
- client_log_fatal(log, client, line + 6, tm);
+ client_log_fatal(log, client, line + 6, log_time, tm);
} else if (strncmp(line, "DEFAULT-FATAL ", 14) == 0) {
/* If the client has logged a fatal/panic, don't log this
message. */
if (client == NULL || !client->fatal_logged)
- client_log_fatal(log, client, line + 14, tm);
+ client_log_fatal(log, client, line + 14, log_time, tm);
} else {
i_error("Received unknown command from master: %s", line);
}
}
static void
-log_it(struct log_connection *log, const char *line, const struct tm *tm)
+log_it(struct log_connection *log, const char *line,
+ time_t log_time, const struct tm *tm)
{
struct failure_line failure;
struct failure_context failure_ctx;
if (log->master) {
T_BEGIN {
- log_parse_master_line(line, tm);
+ log_parse_master_line(line, log_time, tm);
} T_END;
return;
}
prefix = client != NULL && client->prefix != NULL ?
client->prefix : log->default_prefix;
- i_set_failure_prefix(prefix);
- i_log_type(&failure_ctx, "%s", failure.text);
- i_set_failure_prefix("log: ");
+ client_log_ctx(log, &failure_ctx, log_time, prefix, failure.text);
}
static int log_connection_handshake(struct log_connection *log)
tm = *localtime(&now);
while ((line = i_stream_next_line(log->input)) != NULL)
- log_it(log, line, &tm);
+ log_it(log, line, now, &tm);
}
if (log->input->eof)
}
}
-struct log_connection *log_connection_create(int fd, int listen_fd)
+struct log_connection *
+log_connection_create(struct log_error_buffer *errorbuf, int fd, int listen_fd)
{
struct log_connection *log;
log = i_new(struct log_connection, 1);
+ log->errorbuf = errorbuf;
log->fd = fd;
log->listen_fd = listen_fd;
log->io = io_add(fd, IO_READ, log_connection_input, log);
#ifndef LOG_CONNECTION_H
#define LOG_CONNECTION_H
-struct log_connection *log_connection_create(int fd, int listen_fd);
+struct log_connection *
+log_connection_create(struct log_error_buffer *errorbuf, int fd, int listen_fd);
void log_connection_destroy(struct log_connection *log);
void log_connections_init(void);
/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "buffer.h"
#include "settings-parser.h"
#include "service-settings.h"
#include <stddef.h>
+/* <settings checks> */
+static struct file_listener_settings log_unix_listeners_array[] = {
+ { "log-errors", 0600, "", "" }
+};
+static struct file_listener_settings *log_unix_listeners[] = {
+ &log_unix_listeners_array[0]
+};
+static buffer_t log_unix_listeners_buf = {
+ log_unix_listeners,
+ sizeof(log_unix_listeners), { 0, }
+};
+/* </settings checks> */
+
struct service_settings log_service_settings = {
.name = "log",
.protocol = "",
.process_limit = 1,
.client_limit = 0,
.service_count = 0,
- .idle_kill = 0,
+ .idle_kill = -1U,
.vsz_limit = (uoff_t)-1,
- .unix_listeners = ARRAY_INIT,
+ .unix_listeners = { { &log_unix_listeners_buf,
+ sizeof(log_unix_listeners[0]) } },
.fifo_listeners = ARRAY_INIT,
.inet_listeners = ARRAY_INIT,
#include "master-interface.h"
#include "master-service.h"
#include "master-service-settings.h"
+#include "log-error-buffer.h"
#include "log-connection.h"
+#include "doveadm-connection.h"
#include <unistd.h>
+static struct log_error_buffer *errorbuf;
+
static void
sig_reopen_logs(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
{
lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE,
sig_reopen_logs, NULL);
+ errorbuf = log_error_buffer_init();
log_connections_init();
}
static void main_deinit(void)
{
log_connections_deinit();
+ log_error_buffer_deinit(&errorbuf);
}
static void client_connected(struct master_service_connection *conn)
{
+ if (conn->fifo)
+ log_connection_create(errorbuf, conn->fd, conn->listen_fd);
+ else if (strcmp(conn->name, "log-errors") == 0)
+ doveadm_connection_create(errorbuf, conn->fd);
+ else {
+ i_error("Unknown listener name: %s", conn->name);
+ return;
+ }
+
master_service_client_connection_accept(conn);
- log_connection_create(conn->fd, conn->listen_fd);
}
int main(int argc, char *argv[])