ioloop-notify-inotify.c revision 36d2b3dc8766ef336a289a51075ca2f3236ef1ef
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>
660b4d36110c44b1e4b4b45a78c22d1569ccdb54Timo Sirainen#include <sys/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
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainenstruct inotify_io {
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen struct io io;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen int wd;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen};
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo 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) {
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen struct inotify_io *iio = (struct inotify_io *)io;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen if (iio->wd == 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
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void event_callback(struct ioloop *ioloop)
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;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen struct inotify_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;
36d2b3dc8766ef336a289a51075ca2f3236ef1efTimo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
36d2b3dc8766ef336a289a51075ca2f3236ef1efTimo Sirainen message then. */
36d2b3dc8766ef336a289a51075ca2f3236ef1efTimo Sirainen if (errno != ESTALE)
36d2b3dc8766ef336a289a51075ca2f3236ef1efTimo 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
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen io = p_new(ioloop->pool, struct inotify_io, 1);
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen io->io.fd = -1;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen io->io.callback = callback;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen io->io.context = context;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen io->wd = watchdescriptor;
554560d3e21f729fc60ca1ca6697484598e81154Timo Sirainen return &io->io;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo 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;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen struct inotify_io *io = (struct inotify_io *)_io;
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->wd) < 0)
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen i_error("inotify_rm_watch() failed: %m");
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen if (ioloop->notifys == NULL)
2a4e8f370c566ffd360922227fc73d0ee36abee7Timo Sirainen io_remove(&ctx->event_io);
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