ioloop-notify-inotify.c revision fefe9afdb0fb4cfc70afe3006ec88ba09ab3762d
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2015 Dovecot authors, see the included COPYING file */
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#define _GNU_SOURCE
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#include "lib.h"
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#include "fd-close-on-exec.h"
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#include "fd-set-nonblock.h"
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#include "ioloop-private.h"
a8fe899601735459641edae975c0fa08be8482e2Timo Sirainen#include "ioloop-notify-fd.h"
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#include "buffer.h"
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#include "net.h"
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#include "ipwd.h"
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen#include <stdio.h>
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#include <unistd.h>
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#include <fcntl.h>
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#include <sys/ioctl.h>
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#include <sys/inotify.h>
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen#define INOTIFY_BUFLEN (32*1024)
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainenstruct ioloop_notify_handler_context {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct ioloop_notify_fd_context fd_ctx;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen int inotify_fd;
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen struct io *event_io;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen bool disabled;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen};
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstatic bool inotify_input_more(struct ioloop *ioloop)
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen{
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen struct ioloop_notify_handler_context *ctx =
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ioloop->notify_handler_context;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen const struct inotify_event *event;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen unsigned char event_buf[INOTIFY_BUFLEN];
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen struct io_notify *io;
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen ssize_t ret, pos;
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen /* read as many events as there is available and fit into our buffer.
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen only full events are returned by the kernel. */
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen if (ret <= 0) {
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen if (ret == 0 || errno == EAGAIN) {
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen /* nothing more to read */
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen return FALSE;
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen i_fatal("read(inotify) failed: %m");
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen i_fatal("gettimeofday(): %m");
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ioloop_time = ioloop_timeval.tv_sec;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen for (pos = 0; pos < ret; ) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if ((size_t)(ret - pos) < sizeof(*event))
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen break;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen event = (struct inotify_event *)(event_buf + pos);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen i_assert(event->len < (size_t)ret);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen pos += sizeof(*event) + event->len;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (io != NULL) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if ((event->mask & IN_IGNORED) != 0) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen /* calling inotify_rm_watch() would now give
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen EINVAL */
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen io->fd = -1;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen io_loop_call_io(&io->io);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (pos != ret)
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen i_error("read(inotify) returned partial event");
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen return (size_t)ret >= sizeof(event_buf)-512;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen}
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstatic void inotify_input(struct ioloop *ioloop)
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen{
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen while (inotify_input_more(ioloop)) ;
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen}
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen#undef io_add_notify
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainenenum io_notify_result
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainenio_add_notify(const char *path, unsigned int source_linenum,
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen io_callback_t *callback, void *context, struct io **io_r)
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen{
6a262c9bd8f57cf1e57112e0522dbdab28ae8c29Timo Sirainen struct ioloop_notify_handler_context *ctx =
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen current_ioloop->notify_handler_context;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen int wd;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen *io_r = NULL;
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen if (ctx == NULL)
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen ctx = io_loop_notify_handler_init();
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen if (ctx->disabled)
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen return IO_NOTIFY_NOSUPPORT;
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen wd = inotify_add_watch(ctx->inotify_fd, path,
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen IN_CREATE | IN_DELETE | IN_DELETE_SELF |
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen IN_MOVE | IN_MODIFY);
ecf44c74416ffa4e7c331e49a1e283be6b1aa668Timo Sirainen if (wd < 0) {
ecf44c74416ffa4e7c331e49a1e283be6b1aa668Timo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
ecf44c74416ffa4e7c331e49a1e283be6b1aa668Timo Sirainen message then. */
ecf44c74416ffa4e7c331e49a1e283be6b1aa668Timo Sirainen if (errno == ENOENT || errno == ESTALE)
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen return IO_NOTIFY_NOTFOUND;
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen if (errno != ENOSPC)
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen else {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen i_warning("Inotify watch limit for user exceeded, "
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen "disabling. Increase "
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen "/proc/sys/fs/inotify/max_user_watches");
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen }
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen ctx->disabled = TRUE;
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen return IO_NOTIFY_NOSUPPORT;
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen }
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen if (ctx->event_io == NULL) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen inotify_input, current_ioloop);
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen }
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen (*io_r)->source_linenum = source_linenum;
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen return IO_NOTIFY_ADDED;
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen}
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainenvoid io_loop_notify_remove(struct io *_io)
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen{
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen struct ioloop_notify_handler_context *ctx =
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen _io->ioloop->notify_handler_context;
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen struct io_notify *io = (struct io_notify *)_io;
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen if (io->fd != -1) {
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen /* ernro=EINVAL happens if the file itself is deleted and
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen kernel has sent IN_IGNORED event which we haven't read. */
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen errno != EINVAL)
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen i_error("inotify_rm_watch() failed: %m");
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen }
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen io_notify_fd_free(&ctx->fd_ctx, io);
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen if (ctx->fd_ctx.notifies == NULL)
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen io_remove(&ctx->event_io);
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen}
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainenstatic void ioloop_inotify_user_limit_exceeded(void)
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen{
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen struct passwd pw;
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen const char *name;
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen uid_t uid = geteuid();
adea69875046ece77dc36abd3f88a241a3f17ad9Timo Sirainen
0950aed81d1e9618264e6aa4d214d89e005ec8d6Timo Sirainen if (i_getpwuid(uid, &pw) <= 0)
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen name = t_strdup_printf("UID %s", dec2str(uid));
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen else {
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen name = t_strdup_printf("%s (UID %s)",
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen dec2str(uid), pw.pw_name);
d03a871a77f8ec36f48f5fea98d810e51b186fdbTimo Sirainen }
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen i_warning("Inotify instance limit for user %s exceeded, disabling. "
d03a871a77f8ec36f48f5fea98d810e51b186fdbTimo Sirainen "Increase /proc/sys/fs/inotify/max_user_instances", name);
fcaf124d4a727424a338cccfd4274c2393818cd3Timo Sirainen}
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen{
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen struct ioloop *ioloop = current_ioloop;
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen struct ioloop_notify_handler_context *ctx;
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen ctx = ioloop->notify_handler_context =
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen i_new(struct ioloop_notify_handler_context, 1);
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen ctx->inotify_fd = inotify_init();
737561538a2dcdcda948a1da2830a612d8263a23Timo Sirainen if (ctx->inotify_fd == -1) {
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen if (errno != EMFILE)
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen i_error("inotify_init() failed: %m");
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen else
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen ioloop_inotify_user_limit_exceeded();
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen ctx->disabled = TRUE;
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen } else {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen fd_close_on_exec(ctx->inotify_fd, TRUE);
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen fd_set_nonblock(ctx->inotify_fd, TRUE);
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen }
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen return ctx;
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen}
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen{
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen struct ioloop_notify_handler_context *ctx =
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen ioloop->notify_handler_context;
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen while (ctx->fd_ctx.notifies != NULL) {
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen struct io_notify *io = ctx->fd_ctx.notifies;
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen struct io *_io = &io->io;
6c00502d4ece417ead501db8f0ee3e8287ba4459Timo Sirainen
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen i_warning("I/O notify leak: %p (line %u, fd %d)",
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen (void *)_io->callback,
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen _io->source_linenum, io->fd);
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen io_remove(&_io);
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen }
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen if (ctx->inotify_fd != -1) {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen if (close(ctx->inotify_fd) < 0)
ded274d94e795765b1d2e76da2ea74b22fbcd1d5Timo Sirainen i_error("close(inotify) failed: %m");
d4847b921058734e0668bc7690465c91523d9ec0Martti Rannanjärvi ctx->inotify_fd = -1;
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen }
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen i_free(ctx);
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen}
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen#endif
643a81fff9003cba13deb49a565a3c8171da524dTimo Sirainen