ioloop-notify-inotify.c revision 45312f52ff3a3d4c137447be4c7556500c2f8bf2
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen#define _GNU_SOURCE
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen#include "lib.h"
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen
419be6cd72f6e11705576bbba683b29c32eaa762Timo Sirainen#include "fd-close-on-exec.h"
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen#include "fd-set-nonblock.h"
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen#include "ioloop-internal.h"
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen#include "ioloop-notify-fd.h"
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen#include "buffer.h"
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen#include "network.h"
ffaa309c211897ab875bbe0b093bc7e709bb1e5dTimo Sirainen
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen#include <stdio.h>
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen#include <unistd.h>
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen#include <fcntl.h>
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen#include <sys/ioctl.h>
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen#include <sys/inotify.h>
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen#define INOTIFY_BUFLEN (32*1024)
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen
78a5b3e697af5db96fe0dffed600b0d6370bb8e5Timo Sirainenstruct ioloop_notify_handler_context {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct ioloop_notify_fd_context fd_ctx;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen int inotify_fd;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct io *event_io;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen bool disabled;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen};
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenstatic bool inotify_input_more(struct ioloop *ioloop)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen{
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct ioloop_notify_handler_context *ctx =
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen ioloop->notify_handler_context;
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen const struct inotify_event *event;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen unsigned char event_buf[INOTIFY_BUFLEN];
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct io_notify *io;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen ssize_t ret, pos;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen /* read as many events as there is available and fit into our buffer.
89502bb187e8285b2a155559894ca80374ac3ae7Timo Sirainen only full events are returned by the kernel. */
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if (ret <= 0) {
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if (ret == 0) {
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen /* nothing more to read */
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen return FALSE;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen }
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen i_fatal("read(inotify) failed: %m");
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen }
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen i_fatal("gettimeofday(): %m");
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen ioloop_time = ioloop_timeval.tv_sec;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen for (pos = 0; pos < ret; ) {
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if ((size_t)(ret - pos) < sizeof(*event))
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen break;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen event = (struct inotify_event *)(event_buf + pos);
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen pos += sizeof(*event) + event->len;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if (io != NULL) {
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen if ((event->mask & IN_IGNORED) != 0) {
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen /* calling inotify_rm_watch() would now give
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen EINVAL */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen io->fd = -1;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen }
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen io->io.callback(io->io.context);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen }
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen }
fcd443a32b01c4da131f36649d5a5fa5f8452dcfTimo Sirainen if (pos != ret)
fcd443a32b01c4da131f36649d5a5fa5f8452dcfTimo Sirainen i_error("read(inotify) returned partial event");
ed45903d7ad49b3d54f0883b203632fbccf8eff2Timo Sirainen return (size_t)ret >= sizeof(event_buf)-512;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen}
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenstatic void inotify_input(struct ioloop *ioloop)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen{
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen while (inotify_input_more(ioloop)) ;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen}
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen#undef io_add_notify
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainenenum io_notify_result io_add_notify(const char *path, io_callback_t *callback,
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen void *context, struct io **io_r)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen{
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen struct ioloop_notify_handler_context *ctx =
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen current_ioloop->notify_handler_context;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen int wd;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen *io_r = NULL;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen if (ctx == NULL)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen ctx = io_loop_notify_handler_init();
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen if (ctx->disabled)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen return IO_NOTIFY_NOSUPPORT;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen wd = inotify_add_watch(ctx->inotify_fd, path,
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen IN_CREATE | IN_DELETE | IN_DELETE_SELF |
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen IN_MOVE | IN_CLOSE | IN_MODIFY);
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen if (wd < 0) {
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen message then. */
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen if (errno == ENOENT || errno == ESTALE)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen return IO_NOTIFY_NOTFOUND;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen ctx->disabled = TRUE;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen return IO_NOTIFY_NOSUPPORT;
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen }
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen if (ctx->event_io == NULL) {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen inotify_input, current_ioloop);
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen }
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen return IO_NOTIFY_ADDED;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen}
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenvoid io_loop_notify_remove(struct io *_io)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen{
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen struct ioloop_notify_handler_context *ctx =
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen _io->ioloop->notify_handler_context;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct io_notify *io = (struct io_notify *)_io;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen if (io->fd != -1) {
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen /* ernro=EINVAL happens if the file itself is deleted and
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen kernel has sent IN_IGNORED event which we haven't read. */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
e93ab1b206e3792a8dabc460ad2ee60aaf6830b1Timo Sirainen errno != EINVAL)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen i_error("inotify_rm_watch() failed: %m");
e93ab1b206e3792a8dabc460ad2ee60aaf6830b1Timo Sirainen }
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen io_notify_fd_free(&ctx->fd_ctx, io);
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen if (ctx->fd_ctx.notifies == NULL)
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen io_remove(&ctx->event_io);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen}
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen{
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct ioloop *ioloop = current_ioloop;
ed45903d7ad49b3d54f0883b203632fbccf8eff2Timo Sirainen struct ioloop_notify_handler_context *ctx;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen ctx = ioloop->notify_handler_context =
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen i_new(struct ioloop_notify_handler_context, 1);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen ctx->inotify_fd = inotify_init();
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (ctx->inotify_fd == -1) {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (errno != EMFILE)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen i_error("inotify_init() failed: %m");
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen else {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen i_warning("Inotify instance limit for user exceeded, "
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen "disabling. Increase "
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen "/proc/sys/fs/inotify/max_user_instances");
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen }
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen ctx->disabled = TRUE;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen } else {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen fd_close_on_exec(ctx->inotify_fd, TRUE);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen fd_set_nonblock(ctx->inotify_fd, TRUE);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen }
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen return ctx;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen}
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen{
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen struct ioloop_notify_handler_context *ctx =
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen ioloop->notify_handler_context;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen if (ctx->inotify_fd != -1) {
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen if (close(ctx->inotify_fd) < 0)
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen i_error("close(inotify) failed: %m");
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen ctx->inotify_fd = -1;
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen }
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen i_free(ctx);
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen}
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen#endif
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen