view src/imap/rawlog.c @ 765:553f050c8313 HEAD

Added buffer API. Point is to hide all buffer writing behind this API which verifies that nothing overflows. Much better than doing the same checks all around the code, even if it is slightly slower. Buffer reading is still mostly done directly, that isn't such a big security risk and I can't think of a reasonable API for it anyway.
author Timo Sirainen <tss@iki.fi>
date Sun, 08 Dec 2002 07:23:07 +0200
parents ad4292a30b1d
children 9cb7022749e7
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "rawlog.h"

#ifdef BUILD_RAWLOG

#include "ioloop.h"
#include "network.h"
#include "write-full.h"
#include "process-title.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>

#define TIMESTAMP_WAIT_TIME 5
#define TIMESTAMP_FORMAT " * OK [RAWLOG TIMESTAMP] %Y-%m-%d %H:%M:%S\n"

static IOLoop ioloop;
static int client_in, client_out, imap_in, imap_out;
static int log_in, log_out;

static time_t last_write = 0;
static int last_lf = TRUE;

static void copy(int in, int out, int log)
{
	struct tm *tm;
	char buf[1024];
	ssize_t r_ret, s_ret;

	if (last_lf && ioloop_time - 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)
			i_fatal("Can't write to log file: %m");
	}

	net_set_nonblock(in, TRUE);
	r_ret = read(in, buf, sizeof(buf));
	if (r_ret <= 0) {
		if (r_ret < 0)
			i_error("imap_in: read() failed: %m");

		/* disconnected */
		io_loop_stop(ioloop);
		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");

	net_set_nonblock(out, FALSE);
	do {
		s_ret = write(out, buf, r_ret);
		if (s_ret <= 0) {
			if (r_ret < 0)
				i_error("imap_in: write() 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