#include "lib.h"
#include "lib-signals.h"
+#include "str.h"
#include "base64.h"
#include "ioloop.h"
#include "istream.h"
#include "process-title.h"
#include "settings-parser.h"
#include "iostream-ssl.h"
+#include "ostream-multiplex.h"
#include "master-service.h"
#include "master-service-ssl.h"
#include "master-service-settings.h"
static void client_connection_input(struct client_connection *conn);
+static failure_callback_t *orig_error_callback, *orig_fatal_callback;
+static failure_callback_t *orig_info_callback, *orig_debug_callback = NULL;
+
+static bool log_recursing = FALSE;
+
+
+static void ATTR_FORMAT(2, 0)
+doveadm_server_log_handler(const struct failure_context *ctx,
+ const char *format, va_list args)
+{
+ if (!log_recursing && doveadm_client != NULL &&
+ doveadm_client->log_out != NULL) T_BEGIN {
+ /* prevent re-entering this code if
+ any of the following code causes logging */
+ log_recursing = TRUE;
+ char c = doveadm_log_type_to_char(ctx->type);
+ const char *ptr,*start;
+ bool corked = o_stream_is_corked(doveadm_client->log_out);
+ va_list va;
+ va_copy(va, args);
+ string_t *str = t_str_new(128);
+ str_vprintfa(str, format, va);
+ va_end(va);
+ start = str_c(str);
+ if (!corked)
+ o_stream_cork(doveadm_client->log_out);
+ while((ptr = strchr(start, '\n'))!=NULL) {
+ o_stream_nsend(doveadm_client->log_out, &c, 1);
+ o_stream_nsend(doveadm_client->log_out, start, ptr-start+1);
+ str_delete(str, 0, ptr-start+1);
+ }
+ if (str->used > 0) {
+ o_stream_nsend(doveadm_client->log_out, &c, 1);
+ o_stream_nsend(doveadm_client->log_out, str->data, str->used);
+ o_stream_nsend(doveadm_client->log_out, "\n", 1);
+ }
+ o_stream_uncork(doveadm_client->log_out);
+ if (corked)
+ o_stream_cork(doveadm_client->log_out);
+ log_recursing = FALSE;
+ } T_END;
+
+ switch(ctx->type) {
+ case LOG_TYPE_DEBUG:
+ orig_debug_callback(ctx, format, args);
+ break;
+ case LOG_TYPE_INFO:
+ orig_info_callback(ctx, format, args);
+ break;
+ case LOG_TYPE_WARNING:
+ case LOG_TYPE_ERROR:
+ orig_error_callback(ctx, format, args);
+ break;
+ default:
+ i_unreached();
+ }
+}
+
+static void doveadm_server_capture_logs(void)
+{
+ i_assert(orig_debug_callback == NULL);
+ i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback,
+ &orig_info_callback, &orig_debug_callback);
+ i_set_error_handler(doveadm_server_log_handler);
+ i_set_info_handler(doveadm_server_log_handler);
+ i_set_debug_handler(doveadm_server_log_handler);
+}
+
+static void doveadm_server_restore_logs(void)
+{
+ i_assert(orig_debug_callback != NULL);
+ i_set_error_handler(orig_error_callback);
+ i_set_info_handler(orig_info_callback);
+ i_set_debug_handler(orig_debug_callback);
+ orig_fatal_callback = NULL;
+ orig_error_callback = NULL;
+ orig_info_callback = NULL;
+ orig_debug_callback = NULL;
+}
+
static void
doveadm_cmd_server_post(struct client_connection *conn, const char *cmd_name)
{
io_loop_set_current(prev_ioloop);
lib_signals_reset_ioloop();
o_stream_switch_ioloop(conn->output);
+ if (conn->log_out != NULL)
+ o_stream_switch_ioloop(conn->log_out);
io_loop_set_current(ioloop);
io_loop_destroy(&ioloop);
}
o_stream_nsend(conn->output, "+\n", 2);
conn->authenticated = TRUE;
- doveadm_print_ostream = conn->output;
}
if (!conn->io_setup) {
conn->io_setup = TRUE;
+ if (conn->use_multiplex) {
+ o_stream_flush(conn->output);
+ struct ostream *os = conn->output;
+ conn->output = o_stream_create_multiplex(os, (size_t)-1);
+ o_stream_set_name(conn->output, o_stream_get_name(os));
+ o_stream_set_no_error_handling(conn->output, TRUE);
+ o_stream_unref(&os);
+ conn->log_out =
+ o_stream_multiplex_add_channel(conn->output,
+ DOVEADM_LOG_CHANNEL_ID);
+ o_stream_set_no_error_handling(conn->log_out, TRUE);
+ o_stream_set_name(conn->log_out, t_strdup_printf("%s (log)",
+ o_stream_get_name(conn->output)));
+ doveadm_server_capture_logs();
+ }
doveadm_print_ostream = conn->output;
}
if (conn->input != NULL) {
i_stream_destroy(&conn->input);
}
+ if (conn->log_out != NULL) {
+ doveadm_server_restore_logs();
+ o_stream_unref(&conn->log_out);
+ }
if (conn->fd > 0 && close(conn->fd) < 0)
i_error("close(client) failed: %m");
#include "ioloop.h"
#include "net.h"
#include "istream.h"
+#include "istream-multiplex.h"
#include "ostream.h"
#include "ostream-dot.h"
#include "str.h"
#include <sysexits.h>
#include <unistd.h>
+#define DOVEADM_LOG_CHANNEL_ID 'L'
+
#define MAX_INBUF_SIZE (1024*32)
enum server_reply_state {
unsigned int minor;
struct io *io;
+ struct io *io_log;
struct istream *input;
+ struct istream *log_input;
struct ostream *output;
struct ssl_iostream *ssl_iostream;
struct timeout *to_input;
i_error("doveadm server disconnected before handshake: %s", error);
}
+static void server_connection_print_log(struct server_connection *conn)
+{
+ const char *line;
+ struct failure_context ctx;
+ i_zero(&ctx);
+
+ while((line = i_stream_read_next_line(conn->log_input))!=NULL) {
+ /* skip empty lines */
+ if (*line == '\0') continue;
+
+ if (!doveadm_log_type_from_char(line[0], &ctx.type))
+ i_warning("Doveadm server sent invalid log type 0x%02x",
+ line[0]);
+ line++;
+ i_log_type(&ctx, "%s", line);
+ }
+}
+
+static void server_connection_start_multiplex(struct server_connection *conn)
+{
+ struct istream *is = conn->input;
+ conn->input = i_stream_create_multiplex(is, MAX_INBUF_SIZE);
+ i_stream_unref(&is);
+ io_remove(&conn->io);
+ conn->io = io_add_istream(conn->input, server_connection_input, conn);
+ conn->log_input = i_stream_multiplex_add_channel(conn->input, DOVEADM_LOG_CHANNEL_ID);
+ conn->io_log = io_add_istream(conn->log_input, server_connection_print_log, conn);
+ i_stream_set_return_partial_line(conn->log_input, TRUE);
+}
+
static void server_connection_input(struct server_connection *conn)
{
const char *line;
continue;
}
if (strcmp(line, "+") == 0) {
+ if (conn->minor > 0)
+ server_connection_start_multiplex(conn);
server_connection_authenticated(conn);
break;
} else if (strcmp(line, "-") == 0) {
if (size == 0)
return FALSE;
+ /* check logs */
+ (void)server_connection_print_log(conn);
+
switch (conn->state) {
case SERVER_REPLY_STATE_DONE:
i_error("doveadm server sent unexpected input");
o_stream_destroy(&conn->cmd_output);
if (conn->ssl_iostream != NULL)
ssl_iostream_unref(&conn->ssl_iostream);
+ if (conn->io_log != NULL)
+ io_remove(&conn->io_log);
+ /* make sure all logs got consumed */
+ if (conn->log_input != NULL) {
+ server_connection_print_log(conn);
+ i_stream_unref(&conn->log_input);
+ }
if (conn->io != NULL)
io_remove(&conn->io);
if (conn->fd != -1) {
if (close(conn->fd) < 0)
i_error("close(server) failed: %m");
}
+
pool_unref(&conn->pool);
}