ioloop-notify-inotify.c revision 9d7451b57769988f7e3e41cd8790e65429ffc5c7
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen/* Copyright (C) 2005 Johannes Berg */
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#define _GNU_SOURCE
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "lib.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "ioloop-internal.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "buffer.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "network.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include <stdio.h>
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include <unistd.h>
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include <fcntl.h>
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include <sys/ioctl.h>
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include <linux/inotify.h>
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#define INITIAL_INOTIFY_BUFLEN (FILENAME_MAX + sizeof(struct inotify_event))
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#define MAXIMAL_INOTIFY_BUFLEN (32*1024)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenstruct ioloop_notify_handler_context {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen int inotify_fd;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct io *event_io;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen buffer_t *buf;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen};
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenstatic int event_read_next(struct ioloop *ioloop)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct ioloop_notify_handler_context *ctx =
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ioloop->notify_handler_context;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct io *io;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct inotify_event *event;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ssize_t ret;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen size_t record_length;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen int required_bytes;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ioctl(ctx->inotify_fd, FIONREAD, &required_bytes))
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_fatal("ioctl(inotify_fd, FIONREAD) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (required_bytes <= 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return FALSE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (required_bytes > MAXIMAL_INOTIFY_BUFLEN)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen required_bytes = MAXIMAL_INOTIFY_BUFLEN;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen event = buffer_get_space_unsafe(ctx->buf, 0, required_bytes);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ret = read(ctx->inotify_fd, (void *)event, required_bytes);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ret == 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return FALSE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ret < 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_fatal("read(inotify_fd) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_fatal("gettimeofday(): %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen while ((size_t)required_bytes > sizeof(*event)) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (io = ioloop->notifys; io != NULL; io = io->next) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (io->notify_context == event->wd) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->callback(io->context);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen record_length = event->len + sizeof(struct inotify_event);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if ((size_t)required_bytes < record_length)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen required_bytes -= record_length;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen /* this might point outside the area if the loop
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen won't run again */
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen event = PTR_OFFSET(event, record_length);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return TRUE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenstatic void event_callback(void *context)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct ioloop *ioloop = context;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen while (event_read_next(ioloop)) ;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenstruct io *io_loop_notify_add(struct ioloop *ioloop, int fd,
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen enum io_condition condition,
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io_callback_t *callback, void *context)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct ioloop_notify_handler_context *ctx =
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ioloop->notify_handler_context;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct io *io;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct inotify_watch_request req;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen int added = FALSE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen int watchdescriptor;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if ((condition & IO_FILE_NOTIFY) != 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return NULL;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ctx->event_io == NULL) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen added = TRUE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen event_callback, ioloop);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen /* now set up the notification request and shoot it off */
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen req.fd = fd;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen req.mask = IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE | IN_MODIFY;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen watchdescriptor = ioctl(ctx->inotify_fd, INOTIFY_WATCH, &req);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (watchdescriptor < 0) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_error("ioctl(INOTIFY_WATCH) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (added) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io_remove(ctx->event_io);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ctx->event_io = NULL;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return NULL;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io = p_new(ioloop->pool, struct io, 1);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->fd = fd;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->condition = condition;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->callback = callback;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->context = context;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->notify_context = watchdescriptor;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io->next = ioloop->notifys;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ioloop->notifys = io;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return io;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenvoid io_loop_notify_remove(struct ioloop *ioloop, struct io *io)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct ioloop_notify_handler_context *ctx =
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ioloop->notify_handler_context;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct io **io_p;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (io_p = &ioloop->notifys; *io_p != NULL; io_p = &(*io_p)->next) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*io_p == io) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen *io_p = io->next;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ioctl(ctx->inotify_fd, INOTIFY_IGNORE, &io->notify_context) < 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_error("ioctl(INOTIFY_IGNORE) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen p_free(ioloop->pool, io);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ioloop->notifys == NULL) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io_remove(ctx->event_io);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ctx->event_io = NULL;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenvoid io_loop_notify_handler_init(struct ioloop *ioloop)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct ioloop_notify_handler_context *ctx;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ctx = ioloop->notify_handler_context =
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_new(struct ioloop_notify_handler_context, 1);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ctx->inotify_fd = open("/dev/inotify", O_RDONLY);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ctx->inotify_fd < 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_fatal("open(/dev/inotify) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ctx->buf = buffer_create_dynamic(default_pool, INITIAL_INOTIFY_BUFLEN);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen struct ioloop_notify_handler_context *ctx =
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen ioloop->notify_handler_context;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (close(ctx->inotify_fd) < 0)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_error("close(/dev/inotify) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen buffer_free(ctx->buf);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_free(ctx);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#endif