Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib/ioloop-notify-dn.c @ 5248:12ac5f685814 HEAD
Various cleanups to ioloop code.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Mar 2007 00:04:21 +0200 |
parents | 9b8c3efa85ec |
children | 772f4e8bd2a9 |
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 "ioloop-notify-fd.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 ioloop_notify_fd_context fd_ctx; struct io *event_io; int event_pipe[2]; bool disabled; }; static int sigrt_refcount = 0; static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void); static void sigrt_handler(int signo __attr_unused__, siginfo_t *si, void *data __attr_unused__) { struct ioloop_notify_handler_context *ctx = current_ioloop->notify_handler_context; int saved_errno = errno; int ret; ret = write(ctx->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)); errno = saved_errno; } static void dnotify_input(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; struct io_notify *io; int fd_buf[256], i, ret; ret = read(ctx->event_pipe[0], fd_buf, sizeof(fd_buf)); if (ret < 0) i_fatal("read(event_pipe) failed: %m"); if ((ret % sizeof(fd_buf[0])) != 0) i_fatal("read(event_pipe) returned %d", ret); ret /= sizeof(fd_buf[0]); if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0) i_fatal("gettimeofday(): %m"); ioloop_time = ioloop_timeval.tv_sec; for (i = 0; i < ret; i++) { io = io_notify_fd_find(&ctx->fd_ctx, fd_buf[i]); if (io != NULL) io->io.callback(io->io.context); } } #undef io_add_notify struct io *io_add_notify(const char *path, io_callback_t *callback, void *context) { struct ioloop_notify_handler_context *ctx = current_ioloop->notify_handler_context; int fd; if (ctx == NULL) ctx = io_loop_notify_handler_init(); if (ctx->disabled) return NULL; 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) { /* not supported, disable dnotify */ 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) { if (errno == ENOTDIR) { /* we're trying to add dnotify to a non-directory fd. fail silently. */ } else if (errno == EINVAL) { /* dnotify not in kernel. disable it. */ ctx->disabled = TRUE; } else { 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(ctx->event_pipe[0], IO_READ, dnotify_input, current_ioloop); } return io_notify_fd_add(&ctx->fd_ctx, fd, callback, context); } void io_loop_notify_remove(struct ioloop *ioloop, struct io *_io) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; struct io_notify *io = (struct io_notify *)_io; 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"); io_notify_fd_free(&ctx->fd_ctx, io); if (ctx->fd_ctx.notifies == NULL) io_remove(&ctx->event_io); } static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void) { struct ioloop_notify_handler_context *ctx; struct sigaction act; ctx = current_ioloop->notify_handler_context = i_new(struct ioloop_notify_handler_context, 1); if (pipe(ctx->event_pipe) < 0) { ctx->disabled = TRUE; i_error("dnotify: pipe() failed: %m"); return ctx; } fd_set_nonblock(ctx->event_pipe[0], TRUE); fd_set_nonblock(ctx->event_pipe[1], TRUE); fd_close_on_exec(ctx->event_pipe[0], TRUE); fd_close_on_exec(ctx->event_pipe[1], TRUE); if (sigrt_refcount++ == 0) { /* 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) { if (errno == EINVAL) { /* kernel is too old to understand even RT signals, so there's no way dnotify works */ ctx->disabled = TRUE; } else { i_fatal("sigaction(SIGRTMIN) failed: %m"); } } } return ctx; } void io_loop_notify_handler_deinit(struct ioloop *ioloop) { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; if (--sigrt_refcount == 0) signal(SIGRTMIN, SIG_IGN); if (close(ctx->event_pipe[0]) < 0) i_error("close(event_pipe[0]) failed: %m"); if (close(ctx->event_pipe[1]) < 0) i_error("close(event_pipe[1]) failed: %m"); i_free(ctx); } #endif