rawlog.c revision fcae6bd5f2fa9da7ef1302279488b6ed97cf8e3c
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "lib.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "ioloop.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "fd-set-nonblock.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "network.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "write-full.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "istream.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "ostream.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "process-title.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include "restrict-access.h"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include <stdlib.h>
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include <unistd.h>
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include <fcntl.h>
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include <sys/stat.h>
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#include <sys/socket.h>
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#define MAX_PROXY_INPUT_SIZE 4096
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#define OUTBUF_THRESHOLD 1024
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#define TIMESTAMP_WAIT_TIME 5
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina#define TIMESTAMP_FORMAT "* OK [RAWLOG TIMESTAMP] %Y-%m-%d %H:%M:%S\n"
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic struct ioloop *ioloop;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinaenum rawlog_flags {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina RAWLOG_FLAG_LOG_INPUT = 0x01,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina RAWLOG_FLAG_LOG_OUTPUT = 0x02,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina RAWLOG_FLAG_LOG_TIMESTAMPS = 0x04,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina RAWLOG_FLAG_LOG_BOUNDARIES = 0X10
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina};
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastruct rawlog_proxy {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina int client_in_fd, client_out_fd, server_fd;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct io *client_io, *server_io;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct istream *server_input;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct ostream *client_output, *server_output;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina int fd_in, fd_out;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina enum rawlog_flags flags;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina time_t last_write;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina unsigned int last_out_lf:1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina};
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void rawlog_proxy_destroy(struct rawlog_proxy *proxy)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->fd_in != -1) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (close(proxy->fd_in) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("close(in) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->fd_out != -1) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (close(proxy->fd_out) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("close(out) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->client_io != NULL)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina io_remove(&proxy->client_io);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->server_io != NULL)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina io_remove(&proxy->server_io);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_stream_destroy(&proxy->server_input);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_destroy(&proxy->client_output);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_destroy(&proxy->server_output);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (close(proxy->client_in_fd) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("close(client_in_fd) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (close(proxy->client_out_fd) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("close(client_out_fd) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (close(proxy->server_fd) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("close(server_fd) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_free(proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina io_loop_stop(ioloop);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void proxy_write_in(struct rawlog_proxy *proxy,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina const void *data, size_t size)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->fd_in == -1 || size == 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina write_full(proxy->fd_in, "<<<", 3);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (write_full(proxy->fd_in, data, size) < 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* failed, disable logging */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("write(in) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)close(proxy->fd_in);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->fd_in = -1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina } else if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina write_full(proxy->fd_in, ">>>\n", 4);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void proxy_write_out(struct rawlog_proxy *proxy,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina const void *data, size_t size)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct tm *tm;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina char buf[256];
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->fd_out == -1 || size == 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->last_out_lf &&
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (proxy->flags & RAWLOG_FLAG_LOG_TIMESTAMPS) != 0 &&
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina ioloop_time - proxy->last_write >= TIMESTAMP_WAIT_TIME) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina tm = localtime(&ioloop_time);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (strftime(buf, sizeof(buf), TIMESTAMP_FORMAT, tm) <= 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("strftime() failed");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (write_full(proxy->fd_out, buf, strlen(buf)) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("Can't write to log file: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina write_full(proxy->fd_out, "<<<", 3);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (write_full(proxy->fd_out, data, size) < 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* failed, disable logging */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("write(out) failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)close(proxy->fd_out);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->fd_out = -1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina } else if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina write_full(proxy->fd_out, ">>>\n", 4);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->last_write = ioloop_time;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->last_out_lf = ((const unsigned char *)buf)[size-1] == '\n' ||
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void server_input(struct rawlog_proxy *proxy)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina unsigned char buf[OUTBUF_THRESHOLD];
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina ssize_t ret;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (o_stream_get_buffer_used_size(proxy->client_output) >
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina OUTBUF_THRESHOLD) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* client's output buffer is already quite full.
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina don't send more until we're below threshold. */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina io_remove(&proxy->server_io);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina ret = net_receive(proxy->server_fd, buf, sizeof(buf));
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (ret > 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)o_stream_send(proxy->client_output, buf, ret);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy_write_out(proxy, buf, ret);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina } else if (ret <= 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina rawlog_proxy_destroy(proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void client_input(struct rawlog_proxy *proxy)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina unsigned char buf[OUTBUF_THRESHOLD];
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina ssize_t ret;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (o_stream_get_buffer_used_size(proxy->server_output) >
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina OUTBUF_THRESHOLD) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* proxy's output buffer is already quite full.
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina don't send more until we're below threshold. */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina io_remove(&proxy->client_io);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina ret = net_receive(proxy->client_in_fd, buf, sizeof(buf));
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (ret > 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)o_stream_send(proxy->server_output, buf, ret);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy_write_in(proxy, buf, ret);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina } else if (ret < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina rawlog_proxy_destroy(proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic int server_output(struct rawlog_proxy *proxy)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (o_stream_flush(proxy->server_output) < 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina rawlog_proxy_destroy(proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return 1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->client_io == NULL &&
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_get_buffer_used_size(proxy->server_output) <
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina OUTBUF_THRESHOLD) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* there's again space in proxy's output buffer, so we can
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina read more from client. */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->client_io = io_add(proxy->client_in_fd, IO_READ,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina client_input, proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return 1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic int client_output(struct rawlog_proxy *proxy)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (o_stream_flush(proxy->client_output) < 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina rawlog_proxy_destroy(proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return 1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->server_io == NULL &&
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_get_buffer_used_size(proxy->client_output) <
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina OUTBUF_THRESHOLD) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* there's again space in client's output buffer, so we can
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina read more from proxy. */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->server_io =
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina io_add(proxy->server_fd, IO_READ, server_input, proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return 1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void proxy_open_logs(struct rawlog_proxy *proxy, const char *path)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina time_t now;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct tm *tm;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina const char *fname;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina char timestamp[50];
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina now = time(NULL);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina tm = localtime(&now);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (strftime(timestamp, sizeof(timestamp), "%Y%m%d-%H%M%S", tm) <= 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("strftime() failed");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if ((proxy->flags & RAWLOG_FLAG_LOG_INPUT) != 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina fname = t_strdup_printf("%s/%s-%s.in", path, timestamp,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina dec2str(getpid()));
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->fd_in = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->fd_in == -1) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("rawlog_open: open() failed for %s: %m", fname);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if ((proxy->flags & RAWLOG_FLAG_LOG_OUTPUT) != 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina fname = t_strdup_printf("%s/%s-%s.out", path, timestamp,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina dec2str(getpid()));
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->fd_out = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (proxy->fd_out == -1) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_error("rawlog_open: open() failed for %s: %m", fname);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)close(proxy->fd_in);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->fd_in = -1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic struct rawlog_proxy *
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinarawlog_proxy_create(int client_in_fd, int client_out_fd, int server_fd,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina const char *path, enum rawlog_flags flags)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct rawlog_proxy *proxy;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy = i_new(struct rawlog_proxy, 1);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->server_fd = server_fd;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->server_input =
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_stream_create_fd(server_fd, MAX_PROXY_INPUT_SIZE, FALSE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->server_output = o_stream_create_fd(server_fd, (size_t)-1, FALSE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->server_io = io_add(server_fd, IO_READ, server_input, proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_set_flush_callback(proxy->server_output, server_output, proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->client_in_fd = client_in_fd;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->client_out_fd = client_out_fd;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->client_output =
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_create_fd(client_out_fd, (size_t)-1, FALSE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->client_io = io_add(proxy->client_in_fd, IO_READ,
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina client_input, proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina o_stream_set_flush_callback(proxy->client_output, client_output, proxy);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina fd_set_nonblock(client_in_fd, TRUE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina fd_set_nonblock(client_out_fd, TRUE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->last_out_lf = TRUE;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->flags = flags;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy->fd_in = proxy->fd_out = -1;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina proxy_open_logs(proxy, path);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return proxy;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina}
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březinastatic void rawlog_open(enum rawlog_flags flags)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina{
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina const char *chroot_dir, *home, *path;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina struct stat st;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina int sfd[2];
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina pid_t pid;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina chroot_dir = getenv("RESTRICT_CHROOT");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina home = getenv("HOME");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (chroot_dir != NULL)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina home = t_strconcat(chroot_dir, home, NULL);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina else if (home == NULL)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina home = ".";
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* see if we want rawlog */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina path = t_strconcat(home, "/dovecot.rawlog", NULL);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (lstat(path, &st) < 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (errno != ENOENT)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_warning("lstat() failed for %s: %m", path);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina else if (getenv("DEBUG") != NULL)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_info("rawlog: %s doesn't exist", path);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (!S_ISDIR(st.st_mode)) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (getenv("DEBUG") != NULL)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_info("rawlog: %s is not a directory", path);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (chroot_dir != NULL) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* we'll chroot soon. skip over the chroot in the path. */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina path += strlen(chroot_dir);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("socketpair() failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina fd_set_nonblock(sfd[0], TRUE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina fd_set_nonblock(sfd[1], TRUE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina pid = fork();
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (pid < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("fork() failed: %m");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (pid > 0) {
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina /* parent */
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (dup2(sfd[1], 0) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("dup2(sfd, 0)");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina if (dup2(sfd[1], 1) < 0)
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina i_fatal("dup2(sfd, 1)");
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)close(sfd[0]);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)close(sfd[1]);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina return;
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina }
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina (void)close(sfd[1]);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina restrict_access_by_env(getenv("HOME"), TRUE);
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina process_title_set(t_strdup_printf("[%s:%s rawlog]", getenv("USER"),
676bf6dda60776d9db79dad1c2506c0e57bb5503Pavel Březina dec2str(getppid())));
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina ioloop = io_loop_create();
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina rawlog_proxy_create(0, 1, sfd[0], path, flags);
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina io_loop_run(ioloop);
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina io_loop_destroy(&ioloop);
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina lib_deinit();
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina exit(0);
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina}
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březinaint main(int argc, char *argv[])
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina{
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina char *executable, *p;
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina enum rawlog_flags flags;
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina flags = RAWLOG_FLAG_LOG_INPUT | RAWLOG_FLAG_LOG_OUTPUT;
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina lib_init();
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina i_set_failure_internal();
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina process_title_init(&argv);
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina argc--;
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina argv++;
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina while (argc > 0 && *argv[0] == '-') {
if (strcmp(argv[0], "-i") == 0)
flags &= ~RAWLOG_FLAG_LOG_OUTPUT;
else if (strcmp(argv[0], "-o") == 0)
flags &= ~RAWLOG_FLAG_LOG_INPUT;
else if (strcmp(argv[0], "-b") == 0)
flags |= RAWLOG_FLAG_LOG_BOUNDARIES;
else {
argc = 0;
break;
}
argc--;
argv++;
}
if (argc < 1)
i_fatal("Usage: rawlog [-i | -o] [-b] <binary> <arguments>");
executable = argv[0];
if (strstr(executable, "/imap") != NULL)
flags |= RAWLOG_FLAG_LOG_TIMESTAMPS;
rawlog_open(flags);
/* hide the executable path, it's ugly */
p = strrchr(argv[0], '/');
if (p != NULL) argv[0] = p+1;
execv(executable, argv);
i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
/* not reached */
return FATAL_EXEC;
}