rawlog.c revision d54ef607275c4899e082fbeeb210346484d2e85f
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "lib.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "rawlog.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#ifdef BUILD_RAWLOG
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "ioloop.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "network.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "write-full.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "process-title.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include <stdlib.h>
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include <unistd.h>
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen#include <fcntl.h>
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen#include <sys/stat.h>
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include <sys/socket.h>
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#define TIMESTAMP_WAIT_TIME 5
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen#define TIMESTAMP_FORMAT " * OK [RAWLOG TIMESTAMP] %Y-%m-%d %H:%M:%S\n"
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainenstatic IOLoop ioloop;
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainenstatic int client_in, client_out, imap_in, imap_out;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic int log_in, log_out;
1f18053d463f0294387b5e4dd11f9010bda9a24eTimo Sirainen
1f18053d463f0294387b5e4dd11f9010bda9a24eTimo Sirainenstatic time_t last_write = 0;
1f18053d463f0294387b5e4dd11f9010bda9a24eTimo Sirainenstatic int last_lf = TRUE;
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic void copy(int in, int out, int log)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct tm *tm;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen char buf[1024];
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen ssize_t r_ret, s_ret;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (last_lf && ioloop_time - last_write > TIMESTAMP_WAIT_TIME) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen tm = localtime(&ioloop_time);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (strftime(buf, sizeof(buf), TIMESTAMP_FORMAT, tm) <= 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("strftime() failed");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (write_full(log, buf, strlen(buf)) < 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("Can't write to log file: %m");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen net_set_nonblock(in, TRUE);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen do {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen r_ret = net_receive(in, buf, sizeof(buf));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen } while (r_ret == 0);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (r_ret < 0) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (r_ret == -1)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_error("imap_in: net_receive() failed: %m");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen /* disconnected */
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen io_loop_stop(ioloop);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen last_lf = buf[r_ret-1] == '\n';
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (write_full(log, buf, r_ret) < 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("Can't write to log file: %m");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen net_set_nonblock(out, FALSE);
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen do {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen s_ret = net_transmit(out, buf, r_ret);
if (s_ret < 0) {
if (s_ret == -1)
i_error("imap_in: net_transmit() failed: %m");
/* disconnected */
io_loop_stop(ioloop);
return;
}
r_ret -= s_ret;
} while (r_ret > 0);
last_write = time(NULL);
}
static void imap_input(void *context __attr_unused__, int fd __attr_unused__,
IO io __attr_unused__)
{
copy(imap_in, client_out, log_out);
}
static void client_input(void *context __attr_unused__, int fd __attr_unused__,
IO io __attr_unused__)
{
copy(client_in, imap_out, log_in);
}
void rawlog_open(int *hin, int *hout)
{
IO io_imap, io_client;
const char *home, *path, *fname;
char timestamp[50];
struct tm *tm;
struct stat st;
int sfd[2];
pid_t pid, parent_pid;
home = getenv("HOME");
if (home == NULL)
home = ".";
/* see if we want rawlog */
path = t_strconcat(home, "/rawlog", NULL);
if (stat(path, &st) < 0) {
if (errno != ENOENT)
i_warning("stat() failed for %s: %m", path);
return;
}
/* yes, open the files. Do it before forking to make sure we don't
unneededly do it. */
tm = localtime(&ioloop_time);
if (strftime(timestamp, sizeof(timestamp), "%Y%m%d-%H%M%S", tm) <= 0)
i_fatal("strftime() failed");
fname = t_strdup_printf("%s/%s-%d.in", path, timestamp, (int)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-%d.out", path, timestamp, (int)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;
}
/* we need to fork the rawlog writer to separate process since
imap process does blocking writes. */
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");
if (pid > 0) {
/* parent */
close(log_in); close(log_out);
close(*hin); close(*hout);
close(sfd[0]);
*hin = *hout = sfd[1];
return;
}
close(sfd[1]);
process_title_set(t_strdup_printf("[%s:%d rawlog]", getenv("USER"),
(int)parent_pid));
/* child */
client_in = *hin;
client_out = *hout;
imap_in = sfd[0];
imap_out = sfd[0];
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);
io_loop_run(ioloop);
io_remove(io_imap);
io_remove(io_client);
io_loop_destroy(ioloop);
lib_deinit();
exit(0);
}
#else
void rawlog_open(int *hin __attr_unused__, int *hout __attr_unused__)
{
}
#endif