Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3050:83034650fef4 HEAD
rewrote rawlog code. it's now fully asynchronous which could make it a bit
faster, and it should also fix some hangs with it. Timestamps are written
now only with IMAP.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 07 Jan 2005 15:22:01 +0200 |
parents | 7f503848533e |
children | b1efe06092ac |
files | src/util/rawlog.c |
diffstat | 1 files changed, 227 insertions(+), 88 deletions(-) [+] |
line wrap: on
line diff
--- a/src/util/rawlog.c Fri Jan 07 14:40:40 2005 +0200 +++ b/src/util/rawlog.c Fri Jan 07 15:22:01 2005 +0200 @@ -5,6 +5,8 @@ #include "ioloop.h" #include "network.h" #include "write-full.h" +#include "istream.h" +#include "ostream.h" #include "process-title.h" #include "restrict-access.h" @@ -14,86 +16,257 @@ #include <sys/stat.h> #include <sys/socket.h> +#define MAX_PROXY_INPUT_SIZE 4096 +#define OUTBUF_THRESHOLD 1024 + #define TIMESTAMP_WAIT_TIME 5 #define TIMESTAMP_FORMAT "* OK [RAWLOG TIMESTAMP] %Y-%m-%d %H:%M:%S\n" static struct ioloop *ioloop; -static int client_in, client_out, imap_in, imap_out; -static int log_in, log_out; + +struct rawlog_proxy { + int client_in_fd, client_out_fd, server_fd; + struct io *client_io, *server_io; + struct istream *server_input; + struct ostream *client_output, *server_output; + + int log_in, log_out; + + time_t last_write; + unsigned int last_out_lf:1; + unsigned int write_timestamps:1; +}; + +static void rawlog_proxy_destroy(struct rawlog_proxy *proxy) +{ + if (proxy->log_in != -1) { + if (close(proxy->log_in) < 0) + i_error("close(in) failed: %m"); + } + if (proxy->log_out != -1) { + if (close(proxy->log_out) < 0) + i_error("close(out) failed: %m"); + } + if (proxy->client_io != NULL) + io_remove(proxy->client_io); + if (proxy->server_io != NULL) + io_remove(proxy->server_io); -static time_t last_write = 0; -static int last_lf = TRUE; + i_stream_unref(proxy->server_input); + o_stream_unref(proxy->client_output); + o_stream_unref(proxy->server_output); + + if (close(proxy->client_in_fd) < 0) + i_error("close(client_in_fd) failed: %m"); + if (close(proxy->client_out_fd) < 0) + i_error("close(client_out_fd) failed: %m"); + if (close(proxy->server_fd) < 0) + i_error("close(server_fd) failed: %m"); + i_free(proxy); + + io_loop_stop(ioloop); +} -static void copy(int in, int out, int log) +static void proxy_write_in(struct rawlog_proxy *proxy, + const void *data, size_t size) +{ + if (proxy->log_out == -1 || size == 0) + return; + + if (write_full(proxy->log_in, data, size) < 0) { + /* failed, disable logging */ + i_error("write(in) failed: %m"); + (void)close(proxy->log_in); + proxy->log_in = -1; + } +} + +static void proxy_write_out(struct rawlog_proxy *proxy, + const void *data, size_t size) { struct tm *tm; - char buf[1024]; - ssize_t r_ret, s_ret; + char buf[256]; - if (last_lf && ioloop_time - last_write > TIMESTAMP_WAIT_TIME) { + if (proxy->log_out == -1 || size == 0) + return; + + if (proxy->last_out_lf && proxy->write_timestamps && + ioloop_time - proxy->last_write >= TIMESTAMP_WAIT_TIME) { tm = localtime(&ioloop_time); if (strftime(buf, sizeof(buf), TIMESTAMP_FORMAT, tm) <= 0) i_fatal("strftime() failed"); - if (write_full(log, buf, strlen(buf)) < 0) + if (write_full(proxy->log_out, buf, strlen(buf)) < 0) i_fatal("Can't write to log file: %m"); } - net_set_nonblock(in, TRUE); - r_ret = net_receive(in, buf, sizeof(buf)); - if (r_ret == 0) - return; + if (write_full(proxy->log_out, data, size) < 0) { + /* failed, disable logging */ + i_error("write(out) failed: %m"); + (void)close(proxy->log_out); + proxy->log_out = -1; + } + + proxy->last_write = ioloop_time; + proxy->last_out_lf = ((const unsigned char *)buf)[size-1] == '\n'; +} + +static void server_input(void *context) +{ + struct rawlog_proxy *proxy = context; + unsigned char buf[OUTBUF_THRESHOLD]; + ssize_t ret; - if (r_ret < 0) { - if (r_ret == -1) - i_error("imap_in: net_receive() failed: %m"); + if (o_stream_get_buffer_used_size(proxy->client_output) > + OUTBUF_THRESHOLD) { + /* client's output buffer is already quite full. + don't send more until we're below threshold. */ + io_remove(proxy->server_io); + proxy->server_io = NULL; + return; + } - /* disconnected */ - io_loop_stop(ioloop); + ret = net_receive(proxy->server_fd, buf, sizeof(buf)); + if (ret > 0) { + (void)o_stream_send(proxy->client_output, buf, ret); + proxy_write_out(proxy, buf, ret); + } else if (ret <= 0) + rawlog_proxy_destroy(proxy); +} + +static void client_input(void *context) +{ + struct rawlog_proxy *proxy = context; + unsigned char buf[OUTBUF_THRESHOLD]; + ssize_t ret; + + if (o_stream_get_buffer_used_size(proxy->server_output) > + OUTBUF_THRESHOLD) { + /* proxy's output buffer is already quite full. + don't send more until we're below threshold. */ + io_remove(proxy->client_io); + proxy->client_io = NULL; return; } - last_lf = buf[r_ret-1] == '\n'; - if (write_full(log, buf, r_ret) < 0) - i_fatal("Can't write to log file: %m"); + ret = net_receive(proxy->client_in_fd, buf, sizeof(buf)); + if (ret > 0) { + (void)o_stream_send(proxy->server_output, buf, ret); + proxy_write_in(proxy, buf, ret); + } else if (ret < 0) + rawlog_proxy_destroy(proxy); +} + +static int server_output(void *context) +{ + struct rawlog_proxy *proxy = context; + + if (o_stream_flush(proxy->server_output) < 0) { + rawlog_proxy_destroy(proxy); + return 1; + } - net_set_nonblock(out, FALSE); - do { - s_ret = net_transmit(out, buf, r_ret); - if (s_ret < 0) { - if (s_ret == -1) - i_error("imap_in: net_transmit() failed: %m"); + if (proxy->client_io == NULL && + o_stream_get_buffer_used_size(proxy->server_output) < + OUTBUF_THRESHOLD) { + /* there's again space in proxy's output buffer, so we can + read more from client. */ + proxy->client_io = io_add(proxy->client_in_fd, IO_READ, + client_input, proxy); + } + return 1; +} + +static int client_output(void *context) +{ + struct rawlog_proxy *proxy = context; - /* disconnected */ - io_loop_stop(ioloop); - return; - } - r_ret -= s_ret; - } while (r_ret > 0); + if (o_stream_flush(proxy->client_output) < 0) { + rawlog_proxy_destroy(proxy); + return 1; + } - last_write = time(NULL); + if (proxy->server_io == NULL && + o_stream_get_buffer_used_size(proxy->client_output) < + OUTBUF_THRESHOLD) { + /* there's again space in client's output buffer, so we can + read more from proxy. */ + proxy->server_io = + io_add(proxy->server_fd, IO_READ, server_input, proxy); + } + return 1; } -static void imap_input(void *context __attr_unused__) +static void proxy_open_logs(struct rawlog_proxy *proxy, const char *path) { - copy(imap_in, client_out, log_out); -} + time_t now; + struct tm *tm; + const char *fname; + char timestamp[50]; + + now = time(NULL); + tm = localtime(&now); + if (strftime(timestamp, sizeof(timestamp), "%Y%m%d-%H%M%S", tm) <= 0) + i_fatal("strftime() failed"); -static void client_input(void *context __attr_unused__) -{ - copy(client_in, imap_out, log_in); + fname = t_strdup_printf("%s/%s-%s.in", path, timestamp, + dec2str(getpid())); + proxy->log_in = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (proxy->log_in == -1) { + i_error("rawlog_open: open() failed for %s: %m", fname); + return; + } + + fname = t_strdup_printf("%s/%s-%s.out", path, timestamp, + dec2str(getpid())); + proxy->log_out = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (proxy->log_out == -1) { + i_error("rawlog_open: open() failed for %s: %m", fname); + close(proxy->log_in); + proxy->log_in = -1; + return; + } } -static void rawlog_open(void) +static struct rawlog_proxy * +rawlog_proxy_create(int client_in_fd, int client_out_fd, int server_fd, + const char *path, int write_timestamps) { - struct io *io_imap, *io_client; - const char *home, *path, *fname; - char timestamp[50]; - struct tm *tm; + struct rawlog_proxy *proxy; + + proxy = i_new(struct rawlog_proxy, 1); + proxy->server_fd = server_fd; + proxy->server_input = + i_stream_create_file(server_fd, default_pool, + MAX_PROXY_INPUT_SIZE, FALSE); + proxy->server_output = + o_stream_create_file(server_fd, default_pool, + (size_t)-1, FALSE); + proxy->server_io = io_add(server_fd, IO_READ, server_input, proxy); + o_stream_set_flush_callback(proxy->server_output, server_output, proxy); + + proxy->client_in_fd = client_in_fd; + proxy->client_out_fd = client_out_fd; + proxy->client_output = + o_stream_create_file(client_out_fd, default_pool, + (size_t)-1, FALSE); + proxy->client_io = io_add(proxy->client_in_fd, IO_READ, + client_input, proxy); + o_stream_set_flush_callback(proxy->client_output, client_output, proxy); + + proxy->last_out_lf = TRUE; + proxy->write_timestamps = write_timestamps; + + proxy_open_logs(proxy, path); + return proxy; +} + +static void rawlog_open(int write_timestamps) +{ + const char *home, *path; struct stat st; int sfd[2]; - pid_t pid, parent_pid; - time_t now; + pid_t pid; home = getenv("HOME"); if (home == NULL) @@ -103,15 +276,15 @@ path = t_strconcat(home, "/dovecot.rawlog", NULL); if (lstat(path, &st) < 0) { if (errno != ENOENT) - i_warning("stat() failed for %s: %m", path); + i_warning("lstat() failed for %s: %m", path); return; } + if (!S_ISDIR(st.st_mode)) + return; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) i_fatal("socketpair() failed: %m"); - parent_pid = getpid(); - pid = fork(); if (pid < 0) i_fatal("fork() failed: %m"); @@ -130,46 +303,12 @@ restrict_access_by_env(TRUE); - /* open the files after dropping privileges */ - now = time(NULL); - tm = localtime(&now); - if (strftime(timestamp, sizeof(timestamp), "%Y%m%d-%H%M%S", tm) <= 0) - i_fatal("strftime() failed"); - - fname = t_strdup_printf("%s/%s-%s.in", path, timestamp, - dec2str(getpid())); - log_in = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); - if (log_in == -1) { - i_warning("rawlog_open: open() failed for %s: %m", fname); - return; - } - - fname = t_strdup_printf("%s/%s-%s.out", path, timestamp, - dec2str(getpid())); - log_out = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); - if (log_out == -1) { - i_warning("rawlog_open: open() failed for %s: %m", fname); - close(log_in); - return; - } - process_title_set(t_strdup_printf("[%s:%s rawlog]", getenv("USER"), - dec2str(parent_pid))); - - /* child */ - client_in = 0; - client_out = 1; - imap_in = sfd[0]; - imap_out = sfd[0]; + dec2str(getppid()))); ioloop = io_loop_create(system_pool); - io_imap = io_add(imap_in, IO_READ, imap_input, NULL); - io_client = io_add(client_in, IO_READ, client_input, NULL); - + rawlog_proxy_create(0, 1, sfd[0], path, write_timestamps); io_loop_run(ioloop); - - io_remove(io_imap); - io_remove(io_client); io_loop_destroy(ioloop); lib_deinit(); @@ -186,11 +325,11 @@ if (argc < 2) i_fatal("Usage: rawlog <binary> <arguments>"); - rawlog_open(); - argv++; executable = argv[0]; + rawlog_open(strstr(executable, "/imap") != NULL); + /* hide path, it's ugly */ p = strrchr(argv[0], '/'); if (p != NULL) argv[0] = p+1;