ioloop-notify-inotify.c revision 2a4e8f370c566ffd360922227fc73d0ee36abee7
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
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen#include "fd-close-on-exec.h"
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>
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen#include <linux/inotify-syscalls.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;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool disabled;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen};
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool 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
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainenstruct io *io_loop_notify_add(struct ioloop *ioloop, const char *path,
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 int watchdescriptor;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen if (ctx->disabled)
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen return NULL;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen watchdescriptor = inotify_add_watch(ctx->inotify_fd, path,
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen IN_CREATE | IN_DELETE | IN_MOVE |
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen IN_CLOSE | IN_MODIFY);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (watchdescriptor < 0) {
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen ctx->disabled = TRUE;
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return NULL;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen if (ctx->event_io == NULL) {
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen event_callback, ioloop);
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen }
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen io = p_new(ioloop->pool, struct io, 1);
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen io->fd = -1;
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
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen if (ctx->disabled)
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen return;
d6500661eb699ff335ac570c8646b6e067e1aac6Timo 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
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->notify_context) < 0)
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen i_error("inotify_rm_watch() failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen p_free(ioloop->pool, io);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (ioloop->notifys == NULL) {
2a4e8f370c566ffd360922227fc73d0ee36abee7Timo 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
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen ctx->inotify_fd = inotify_init();
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (ctx->inotify_fd == -1) {
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen i_error("inotify_init() failed: %m");
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen ctx->disabled = TRUE;
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen return;
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen }
01c43cb586726efc06342114c0545db4b1733d2cTimo Sirainen fd_close_on_exec(ctx->inotify_fd, TRUE);
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
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (ctx->inotify_fd != -1)
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (close(ctx->inotify_fd) < 0)
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen i_error("close(inotify descriptor) failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (ctx->buf != NULL)
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen buffer_free(ctx->buf);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen i_free(ctx);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#endif