ioloop-notify-inotify.c revision 85e2264f7d3b1a336ac5066c99130414dfc77902
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (c) 2005-2015 Dovecot authors, see the included COPYING file */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define _GNU_SOURCE
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen#include "lib.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "fd-close-on-exec.h"
abf015c9682f0f723db87a7c97bc284ef814818fTimo Sirainen#include "fd-set-nonblock.h"
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#include "ioloop-private.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "ioloop-notify-fd.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "buffer.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "net.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "ipwd.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include <stdio.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include <unistd.h>
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen#include <fcntl.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include <sys/ioctl.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include <sys/inotify.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define INOTIFY_BUFLEN (32*1024)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstruct ioloop_notify_handler_context {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct ioloop_notify_fd_context fd_ctx;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen int inotify_fd;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen struct io *event_io;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen bool disabled;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen};
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainenstatic bool inotify_input_more(struct ioloop *ioloop)
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen{
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen struct ioloop_notify_handler_context *ctx =
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen ioloop->notify_handler_context;
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen const struct inotify_event *event;
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen unsigned char event_buf[INOTIFY_BUFLEN];
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct io_notify *io;
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen ssize_t ret, pos;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen /* read as many events as there is available and fit into our buffer.
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen only full events are returned by the kernel. */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret <= 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret == 0 || errno == EAGAIN) {
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen /* nothing more to read */
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen return FALSE;
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen i_fatal("read(inotify) failed: %m");
12797080b552a3c1727b73b61cc7427bec0c7472Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen i_fatal("gettimeofday(): %m");
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen for (pos = 0; pos < ret; ) {
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if ((size_t)(ret - pos) < sizeof(*event))
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainen break;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen event = (struct inotify_event *)(event_buf + pos);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen i_assert(event->len < (size_t)ret);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen pos += sizeof(*event) + event->len;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
38f227941bcf673e0e523c1ac7267bca9cbcd2c4Timo Sirainen io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (io != NULL) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((event->mask & IN_IGNORED) != 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* calling inotify_rm_watch() would now give
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen EINVAL */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen io->fd = -1;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen }
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen io_loop_call_io(&io->io);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen }
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen }
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (pos != ret)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen i_error("read(inotify) returned partial event");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen return (size_t)ret >= sizeof(event_buf)-512;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen}
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
2eb2cf8eeb763bd5ca9b6848dce32f0303e88ec1Timo Sirainenstatic void inotify_input(struct ioloop *ioloop)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen while (inotify_input_more(ioloop)) ;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen#undef io_add_notify
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenenum io_notify_result io_add_notify(const char *path, io_callback_t *callback,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen void *context, struct io **io_r)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen{
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen struct ioloop_notify_handler_context *ctx =
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen current_ioloop->notify_handler_context;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen int wd;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen *io_r = NULL;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ctx == NULL)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ctx = io_loop_notify_handler_init();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ctx->disabled)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen return IO_NOTIFY_NOSUPPORT;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen wd = inotify_add_watch(ctx->inotify_fd, path,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen IN_CREATE | IN_DELETE | IN_DELETE_SELF |
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen IN_MOVE | IN_MODIFY);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (wd < 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen message then. */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (errno == ENOENT || errno == ESTALE)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen return IO_NOTIFY_NOTFOUND;
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (errno != ENOSPC)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen else {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen i_warning("Inotify watch limit for user exceeded, "
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen "disabling. Increase "
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen "/proc/sys/fs/inotify/max_user_watches");
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen }
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen ctx->disabled = TRUE;
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen return IO_NOTIFY_NOSUPPORT;
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen }
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen if (ctx->event_io == NULL) {
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen inotify_input, current_ioloop);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen }
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen return IO_NOTIFY_ADDED;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen}
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenvoid io_loop_notify_remove(struct io *_io)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen{
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen struct ioloop_notify_handler_context *ctx =
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen _io->ioloop->notify_handler_context;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen struct io_notify *io = (struct io_notify *)_io;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (io->fd != -1) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* ernro=EINVAL happens if the file itself is deleted and
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen kernel has sent IN_IGNORED event which we haven't read. */
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen errno != EINVAL)
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen i_error("inotify_rm_watch() failed: %m");
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen }
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen io_notify_fd_free(&ctx->fd_ctx, io);
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen if (ctx->fd_ctx.notifies == NULL)
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen io_remove(&ctx->event_io);
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen}
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainenstatic void ioloop_inotify_user_limit_exceeded(void)
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen{
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen struct passwd pw;
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen const char *name;
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen uid_t uid = geteuid();
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (i_getpwuid(uid, &pw) <= 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen name = t_strdup_printf("UID %s", dec2str(uid));
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen else {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen name = t_strdup_printf("%s (UID %s)",
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen dec2str(uid), pw.pw_name);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen }
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen i_warning("Inotify instance limit for user %s exceeded, disabling. "
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen "Increase /proc/sys/fs/inotify/max_user_instances", name);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct ioloop *ioloop = current_ioloop;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct ioloop_notify_handler_context *ctx;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx = ioloop->notify_handler_context =
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_new(struct ioloop_notify_handler_context, 1);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->inotify_fd = inotify_init();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ctx->inotify_fd == -1) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (errno != EMFILE)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("inotify_init() failed: %m");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen else
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ioloop_inotify_user_limit_exceeded();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->disabled = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen } else {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen fd_close_on_exec(ctx->inotify_fd, TRUE);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen fd_set_nonblock(ctx->inotify_fd, TRUE);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return ctx;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen}
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct ioloop_notify_handler_context *ctx =
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen ioloop->notify_handler_context;
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen if (ctx->inotify_fd != -1) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (close(ctx->inotify_fd) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("close(inotify) failed: %m");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->inotify_fd = -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_free(ctx);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#endif
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainen