ioloop-notify-kqueue.c revision f6c0407545ee77d4647c8cd912e3156b0a48e2f1
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen/*
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen * BSD kqueue() based ioloop notify handler.
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen *
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen * Copyright (c) 2005 Vaclav Haisman <v.haisman@sh.cvut.cz>
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen *
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen * This library is free software; you can redistribute it and/or modify
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen * it under the terms of the GNU Lesser General Public License as published
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen * by the Free Software Foundation; either version 2 of the License, or
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen * (at your option) any later version.
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen */
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#define _GNU_SOURCE
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include "lib.h"
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#ifdef IOLOOP_NOTIFY_KQUEUE
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include "ioloop-internal.h"
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen#include "fd-close-on-exec.h"
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include <unistd.h>
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include <fcntl.h>
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include <sys/types.h>
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include <sys/event.h>
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#include <sys/time.h>
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen#include <sys/stat.h>
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen/* kevent.udata's type just has to be different in NetBSD than in
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen FreeBSD and OpenBSD.. */
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen#ifdef __NetBSD__
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen# define MY_EV_SET(a, b, c, d, e, f, g) \
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen EV_SET(a, b, c, d, e, f, (intptr_t)g)
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen#else
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen# define MY_EV_SET(a, b, c, d, e, f, g) \
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen EV_SET(a, b, c, d, e, f, g)
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen#endif
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainenstruct ioloop_notify_handler_context {
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen int kq;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct io *event_io;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen};
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainenstatic void event_callback(void *context)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen{
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct ioloop_notify_handler_context *ctx = context;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct io *io;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct kevent ev;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen struct timespec ts;
1aaa503bb6e3c5d0a3d17e00c8ef9ecece4d867dTimo Sirainen int ret;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen i_fatal("gettimeofday() failed: %m");
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen ts.tv_sec = 0;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen ts.tv_nsec = 0;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen ret = kevent(ctx->kq, NULL, 0, &ev, 1, &ts);
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen if (ret <= 0) {
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen if (ret == 0 || errno == EINTR)
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen return;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen i_fatal("kevent(notify) failed: %m");
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen }
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen io = (void *)ev.udata;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io->callback(io->context);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen}
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainenvoid io_loop_notify_handler_init(struct ioloop *ioloop)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen{
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct ioloop_notify_handler_context *ctx;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ctx = ioloop->notify_handler_context =
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen p_new(ioloop->pool, struct ioloop_notify_handler_context, 1);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ctx->kq = kqueue();
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen if (ctx->kq < 0)
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen i_fatal("kqueue(notify) failed: %m");
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen fd_close_on_exec(ctx->kq, TRUE);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen}
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen{
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct ioloop_notify_handler_context *ctx =
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ioloop->notify_handler_context;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen if (ctx->event_io)
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen io_remove(&ctx->event_io);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen if (close(ctx->kq) < 0)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen i_error("close(kqueue notify) failed: %m");
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen p_free(ioloop->pool, ctx);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen}
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainenstruct io *io_loop_notify_add(struct ioloop *ioloop, const char *path,
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io_callback_t *callback, void *context)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen{
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct ioloop_notify_handler_context *ctx =
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ioloop->notify_handler_context;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen struct kevent ev;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct io *io;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen int fd;
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen struct stat sb;
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen fd = open(path, O_RDONLY);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen if (fd == -1) {
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen if (errno != ENOENT)
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen i_error("open(%s) for kq notify failed: %m", path);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen return NULL;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen }
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen if (fstat(fd, &sb) < 0) {
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen i_error("fstat(%d, %s) for kq notify failed: %m", fd, path);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen (void)close(fd);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen return NULL;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen }
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen if (!S_ISDIR(sb.st_mode)) {
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen (void)close(fd);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen return NULL;
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen }
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen fd_close_on_exec(fd, TRUE);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io = p_new(ioloop->pool, struct io, 1);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io->fd = fd;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io->callback = callback;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io->context = context;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen
4dc5662260a63669054cd0dc1bac2ccab3fa2ae7Timo Sirainen /* EV_CLEAR flag is needed because the EVFILT_VNODE filter reports
4dc5662260a63669054cd0dc1bac2ccab3fa2ae7Timo Sirainen event state transitions and not the current state. With this flag,
4dc5662260a63669054cd0dc1bac2ccab3fa2ae7Timo Sirainen the same event is only returned once. */
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen MY_EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE, 0, io);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) {
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen i_error("kevent(%d, %s) for notify failed: %m", fd, path);
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen (void)close(fd);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen p_free(ioloop->pool, io);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen return NULL;
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen }
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen if (ctx->event_io == NULL) {
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ctx->event_io =
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen io_add(ctx->kq, IO_READ, event_callback,
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ioloop->notify_handler_context);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen }
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen return io;
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen}
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainenvoid io_loop_notify_remove(struct ioloop *ioloop, struct io *io)
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen{
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen struct ioloop_notify_handler_context *ctx =
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen ioloop->notify_handler_context;
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen struct kevent ev;
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen i_assert((io->condition & IO_NOTIFY) != 0);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
f6c0407545ee77d4647c8cd912e3156b0a48e2f1Timo Sirainen MY_EV_SET(&ev, io->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen if (kevent(ctx->kq, &ev, 1, NULL, 0, 0) < 0)
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen i_error("kevent(%d) for notify remove failed: %m", io->fd);
a40f21e8239a87a8fab2648955b0f568908ca8b5Timo Sirainen if (close(io->fd) < 0)
35565557e05721a761132cec2ba1d93acacb6c14Timo Sirainen i_error("close(%d) for notify remove failed: %m", io->fd);
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen}
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen
6a029ebed745994ce2e5f64182d8b5c8f10d53d6Timo Sirainen#endif