view src/lib/ioloop-notify-dn.c @ 3649:8c63af0ab91a HEAD

Fixed some printf-type mismatches
author Timo Sirainen <tss@iki.fi>
date Wed, 12 Oct 2005 20:14:21 +0300
parents 377f69be914f
children 4fa8e287486e
line wrap: on
line source

/* Copyright (C) 2003 Timo Sirainen */

/* Logic is pretty much based on dnotify by Oskar Liljeblad. */

#define _GNU_SOURCE
#include "lib.h"

#ifdef IOLOOP_NOTIFY_DNOTIFY

#include "ioloop-internal.h"
#include "fd-set-nonblock.h"
#include "fd-close-on-exec.h"

#include <signal.h>
#include <unistd.h>
#include <fcntl.h>

struct ioloop_notify_handler_context {
	struct io *event_io;
	int disabled;
};

static int event_pipe[2] = { -1, -1 };

static void sigrt_handler(int signo __attr_unused__, siginfo_t *si,
			  void *data __attr_unused__)
{
	int ret;

	ret = write(event_pipe[1], &si->si_fd, sizeof(int));
	if (ret < 0 && errno != EINTR && errno != EAGAIN)
		i_fatal("write(event_pipe) failed: %m");

	i_assert(ret <= 0 || ret == sizeof(int));
}

static void event_callback(void *context)
{
	struct ioloop *ioloop = context;
	struct io *io;
	int fd, ret;

	ret = read(event_pipe[0], &fd, sizeof(fd));
	if (ret < 0)
		i_fatal("read(event_pipe) failed: %m");
	if (ret != sizeof(fd)) {
		i_fatal("read(event_pipe) returned %d != %"PRIuSIZE_T,
			ret, sizeof(fd));
	}

	if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
		i_fatal("gettimeofday(): %m");
	ioloop_time = ioloop_timeval.tv_sec;

	for (io = ioloop->notifys; io != NULL; io = io->next) {
		if (io->fd == fd) {
			io->callback(io->context);
			break;
		}
	}
}

struct io *io_loop_notify_add(struct ioloop *ioloop, const char *path,
			      io_callback_t *callback, void *context)
{
	struct ioloop_notify_handler_context *ctx =
		ioloop->notify_handler_context;
	struct io *io;
	int fd;

	fd = open(path, O_RDONLY);
	if (fd == -1) {
		i_error("open(%s) for dnotify failed: %m", path);
		return NULL;
	}

	if (fcntl(fd, F_SETSIG, SIGRTMIN) < 0) {
		if (errno == EINVAL) {
			/* dnotify not in kernel. disable it. */
			ctx->disabled = TRUE;
		} else {
			i_error("fcntl(F_SETSIG) failed: %m");
		}
		(void)close(fd);
		return NULL;
	}
	if (fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_RENAME |
		  DN_MULTISHOT) < 0) {
		/* we fail here if we're trying to add dnotify to
		   non-directory fd. fail silently in that case. */
		if (errno != ENOTDIR)
			i_error("fcntl(F_NOTIFY) failed: %m");
		(void)fcntl(fd, F_SETSIG, 0);
		(void)close(fd);
		return NULL;
	}

	if (ctx->event_io == NULL) {
		ctx->event_io =
			io_add(event_pipe[0], IO_READ, event_callback, ioloop);
	}

	io = p_new(ioloop->pool, struct io, 1);
	io->fd = fd;

	io->callback = callback;
        io->context = context;

	io->next = ioloop->notifys;
	ioloop->notifys = io;
	return io;
}

void io_loop_notify_remove(struct ioloop *ioloop, struct io *io)
{
	struct ioloop_notify_handler_context *ctx =
		ioloop->notify_handler_context;
	struct io **io_p;

	if (ctx->disabled)
		return;

	for (io_p = &ioloop->notifys; *io_p != NULL; io_p = &(*io_p)->next) {
		if (*io_p == io) {
			*io_p = io->next;
			break;
		}
	}

	if (fcntl(io->fd, F_NOTIFY, 0) < 0)
		i_error("fcntl(F_NOTIFY, 0) failed: %m");
	if (fcntl(io->fd, F_SETSIG, 0) < 0)
		i_error("fcntl(F_SETSIG, 0) failed: %m");
	if (close(io->fd))
		i_error("close(dnotify) failed: %m");

	p_free(ioloop->pool, io);

	if (ioloop->notifys == NULL) {
		io_remove(ctx->event_io);
		ctx->event_io = NULL;
	}
}

void io_loop_notify_handler_init(struct ioloop *ioloop)
{
	struct ioloop_notify_handler_context *ctx;
	struct sigaction act;

	i_assert(event_pipe[0] == -1);

	ctx = ioloop->notify_handler_context =
		i_new(struct ioloop_notify_handler_context, 1);

	if (pipe(event_pipe) < 0) {
		i_fatal("pipe() failed: %m");
		return;
	}

	fd_set_nonblock(event_pipe[0], TRUE);
	fd_set_nonblock(event_pipe[1], TRUE);

	fd_close_on_exec(event_pipe[0], TRUE);
	fd_close_on_exec(event_pipe[1], TRUE);

	/* SIGIO is sent if queue gets full. we'll just ignore it. */
        signal(SIGIO, SIG_IGN);

	act.sa_sigaction = sigrt_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;

	if (sigaction(SIGRTMIN, &act, NULL) < 0)
		i_fatal("sigaction(SIGRTMIN) failed: %m");
}

void io_loop_notify_handler_deinit(struct ioloop *ioloop __attr_unused__)
{
	struct ioloop_notify_handler_context *ctx =
		ioloop->notify_handler_context;

	signal(SIGRTMIN, SIG_IGN);

	if (close(event_pipe[0]) < 0)
		i_error("close(event_pipe[0]) failed: %m");
	if (close(event_pipe[1]) < 0)
		i_error("close(event_pipe[1]) failed: %m");

	i_free(ctx);
}

#endif