ioloop-notify-inotify.c revision fdb97244fa45c32c3593726c15aa69ce29bc7121
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen#define _GNU_SOURCE
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "lib.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "fd-close-on-exec.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "fd-set-nonblock.h"
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen#include "ioloop-internal.h"
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen#include "ioloop-notify-fd.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "buffer.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "network.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <stdio.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#include <unistd.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#include <fcntl.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#include <pwd.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#include <sys/ioctl.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#include <sys/inotify.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#define INOTIFY_BUFLEN (32*1024)
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstruct ioloop_notify_handler_context {
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen struct ioloop_notify_fd_context fd_ctx;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen int inotify_fd;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct io *event_io;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
b8cd2f2f99351605725b7260f5da89cff76d0a3aTimo Sirainen bool disabled;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen};
9508ac436fff0e1dcea975855c139cd251deb703Timo Sirainen
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic bool inotify_input_more(struct ioloop *ioloop)
ed16ab579bd058ec5e2b5d02bb41fdadd9e05b31Timo Sirainen{
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainen struct ioloop_notify_handler_context *ctx =
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen ioloop->notify_handler_context;
ed16ab579bd058ec5e2b5d02bb41fdadd9e05b31Timo Sirainen const struct inotify_event *event;
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen unsigned char event_buf[INOTIFY_BUFLEN];
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen struct io_notify *io;
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen ssize_t ret, pos;
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainen /* read as many events as there is available and fit into our buffer.
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainen only full events are returned by the kernel. */
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (ret <= 0) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (ret == 0) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen /* nothing more to read */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_fatal("read(inotify) failed: %m");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_fatal("gettimeofday(): %m");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen for (pos = 0; pos < ret; ) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if ((size_t)(ret - pos) < sizeof(*event))
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen break;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen event = (struct inotify_event *)(event_buf + pos);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen pos += sizeof(*event) + event->len;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (io != NULL) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if ((event->mask & IN_IGNORED) != 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen /* calling inotify_rm_watch() would now give
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen EINVAL */
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen io->fd = -1;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen io->io.callback(io->io.context);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (pos != ret)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_error("read(inotify) returned partial event");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return (size_t)ret >= sizeof(event_buf)-512;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic void inotify_input(struct ioloop *ioloop)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen while (inotify_input_more(ioloop)) ;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen#undef io_add_notify
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenenum io_notify_result io_add_notify(const char *path, io_callback_t *callback,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen void *context, struct io **io_r)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct ioloop_notify_handler_context *ctx =
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen current_ioloop->notify_handler_context;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen int wd;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen *io_r = NULL;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (ctx == NULL)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ctx = io_loop_notify_handler_init();
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (ctx->disabled)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return IO_NOTIFY_NOSUPPORT;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen wd = inotify_add_watch(ctx->inotify_fd, path,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen IN_CREATE | IN_DELETE | IN_DELETE_SELF |
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen IN_MOVE | IN_CLOSE | IN_MODIFY);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (wd < 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen message then. */
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (errno == ENOENT || errno == ESTALE)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return IO_NOTIFY_NOTFOUND;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (errno != ENOSPC)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen else {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_warning("Inotify watch limit for user exceeded, "
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen "disabling. Increase "
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen "/proc/sys/fs/inotify/max_user_watches");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ctx->disabled = TRUE;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return IO_NOTIFY_NOSUPPORT;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (ctx->event_io == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen inotify_input, current_ioloop);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return IO_NOTIFY_ADDED;
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen}
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_loop_notify_remove(struct io *_io)
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen{
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen struct ioloop_notify_handler_context *ctx =
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen _io->ioloop->notify_handler_context;
b8cd2f2f99351605725b7260f5da89cff76d0a3aTimo Sirainen struct io_notify *io = (struct io_notify *)_io;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen if (io->fd != -1) {
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen /* ernro=EINVAL happens if the file itself is deleted and
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen kernel has sent IN_IGNORED event which we haven't read. */
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen errno != EINVAL)
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainen i_error("inotify_rm_watch() failed: %m");
01cd9d4a8050a1dbf1da2c830f9755a45d6d004aTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_notify_fd_free(&ctx->fd_ctx, io);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (ctx->fd_ctx.notifies == NULL)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen io_remove(&ctx->event_io);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainenstatic void ioloop_inotify_user_limit_exceeded(void)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen{
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen const struct passwd *pw;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen const char *name;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen uid_t uid = geteuid();
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen pw = getpwuid(uid);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (pw == NULL)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen name = t_strdup_printf("UID %s", dec2str(uid));
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen name = t_strdup_printf("%s (UID %s)",
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen dec2str(uid), pw->pw_name);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_warning("Inotify instance limit for user %s exceeded, disabling. "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Increase /proc/sys/fs/inotify/max_user_instances", name);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen}
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct ioloop *ioloop = current_ioloop;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct ioloop_notify_handler_context *ctx;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ctx = ioloop->notify_handler_context =
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_new(struct ioloop_notify_handler_context, 1);
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen ctx->inotify_fd = inotify_init();
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen if (ctx->inotify_fd == -1) {
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen if (errno != EMFILE)
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen i_error("inotify_init() failed: %m");
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen else
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen ioloop_inotify_user_limit_exceeded();
ab45534d66792946b5794ab99a843d2f2b1d556fTimo Sirainen ctx->disabled = TRUE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen } else {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen fd_close_on_exec(ctx->inotify_fd, TRUE);
ac84cb764c3a6f4b4b9ded2510d425fb5744dfe8Timo Sirainen fd_set_nonblock(ctx->inotify_fd, TRUE);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return ctx;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct ioloop_notify_handler_context *ctx =
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ioloop->notify_handler_context;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen if (ctx->inotify_fd != -1) {
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (close(ctx->inotify_fd) < 0)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_error("close(inotify) failed: %m");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ctx->inotify_fd = -1;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_free(ctx);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen#endif
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen