ioloop-notify-inotify.c revision 8d6a6eccd3f2e34df967b90bb45e20755241bdbb
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Copyright (c) 2005-2011 Dovecot authors, see the included COPYING file */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher#define _GNU_SOURCE
33396dc46ea52c18f47db1b5d590880806521005Sumit Bose#include "lib.h"
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher
33396dc46ea52c18f47db1b5d590880806521005Sumit Bose#ifdef IOLOOP_NOTIFY_INOTIFY
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "fd-close-on-exec.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "fd-set-nonblock.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "ioloop-internal.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "ioloop-notify-fd.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "buffer.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "network.h"
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher#include "ipwd.h"
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher#include <stdio.h>
e65df5b966b27e13283c65f59f99ac44781e0333Simo Sorce#include <unistd.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <fcntl.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <sys/ioctl.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <sys/inotify.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#define INOTIFY_BUFLEN (32*1024)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstruct ioloop_notify_handler_context {
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher struct ioloop_notify_fd_context fd_ctx;
cc98edd9479d4622634a1275c98058916c14059aStephen Gallagher
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher int inotify_fd;
cc98edd9479d4622634a1275c98058916c14059aStephen Gallagher struct io *event_io;
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina bool disabled;
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina};
1183d29d87c5c7439cf2364b7d7324d4a13b6e35Stephen Gallagher
1183d29d87c5c7439cf2364b7d7324d4a13b6e35Stephen Gallagherstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic bool inotify_input_more(struct ioloop *ioloop)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct ioloop_notify_handler_context *ctx =
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ioloop->notify_handler_context;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const struct inotify_event *event;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned char event_buf[INOTIFY_BUFLEN];
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct io_notify *io;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ssize_t ret, pos;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* read as many events as there is available and fit into our buffer.
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher only full events are returned by the kernel. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce if (ret <= 0) {
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce if (ret == 0 || errno == EAGAIN) {
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce /* nothing more to read */
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce return FALSE;
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce }
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce i_fatal("read(inotify) failed: %m");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (gettimeofday(&ioloop_timeval, NULL) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("gettimeofday(): %m");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ioloop_time = ioloop_timeval.tv_sec;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (pos = 0; pos < ret; ) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if ((size_t)(ret - pos) < sizeof(*event))
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher break;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher event = (struct inotify_event *)(event_buf + pos);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher pos += sizeof(*event) + event->len;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
d921c1eba437662437847279f251a0a5d8f70127Maxim io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
d921c1eba437662437847279f251a0a5d8f70127Maxim if (io != NULL) {
d921c1eba437662437847279f251a0a5d8f70127Maxim if ((event->mask & IN_IGNORED) != 0) {
d921c1eba437662437847279f251a0a5d8f70127Maxim /* calling inotify_rm_watch() would now give
d921c1eba437662437847279f251a0a5d8f70127Maxim EINVAL */
d921c1eba437662437847279f251a0a5d8f70127Maxim io->fd = -1;
d921c1eba437662437847279f251a0a5d8f70127Maxim }
327127bb7fcc07f882209f029e14026de1b23c94Maxim io_loop_call_io(&io->io);
327127bb7fcc07f882209f029e14026de1b23c94Maxim }
327127bb7fcc07f882209f029e14026de1b23c94Maxim }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (pos != ret)
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina i_error("read(inotify) returned partial event");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return (size_t)ret >= sizeof(event_buf)-512;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void inotify_input(struct ioloop *ioloop)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher while (inotify_input_more(ioloop)) ;
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce}
bc9235cfb80bd64a3bfa959e8d26d5ad1be0bdf4Jakub Hrozek
bc9235cfb80bd64a3bfa959e8d26d5ad1be0bdf4Jakub Hrozek#undef io_add_notify
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherenum io_notify_result io_add_notify(const char *path, io_callback_t *callback,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher void *context, struct io **io_r)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct ioloop_notify_handler_context *ctx =
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher current_ioloop->notify_handler_context;
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagher int wd;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *io_r = NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx == NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx = io_loop_notify_handler_init();
068dbee9ca7bf5b37330eff91c94ae10f288d09fJakub Hrozek if (ctx->disabled)
98ce3c3e85a4bb2e1822bf8ab2a1c2ab9e3dd61dJakub Hrozek return IO_NOTIFY_NOSUPPORT;
be65f065fef1d387281096ef095a2acef39ecc12Jakub Hrozek
e124844907ed6973915e4d56f5442ecd07535a12Jakub Hrozek wd = inotify_add_watch(ctx->inotify_fd, path,
f36078af138f052cd9a30360867b0ebd0805af5eJakub Hrozek IN_CREATE | IN_DELETE | IN_DELETE_SELF |
34c78b745eb349eef2b0f13ef2b722632aebe619Jan Cholasta IN_MOVE | IN_CLOSE | IN_MODIFY);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (wd < 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* ESTALE could happen with NFS. Don't bother giving an error
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher message then. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (errno == ENOENT || errno == ESTALE)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return IO_NOTIFY_NOTFOUND;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (errno != ENOSPC)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_error("inotify_add_watch(%s) failed: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher else {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_warning("Inotify watch limit for user exceeded, "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "disabling. Increase "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "/proc/sys/fs/inotify/max_user_watches");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->disabled = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return IO_NOTIFY_NOSUPPORT;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->event_io == NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher inotify_input, current_ioloop);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return IO_NOTIFY_ADDED;
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek}
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bose
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid io_loop_notify_remove(struct io *_io)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher{
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher struct ioloop_notify_handler_context *ctx =
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher _io->ioloop->notify_handler_context;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher struct io_notify *io = (struct io_notify *)_io;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher if (io->fd != -1) {
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher /* ernro=EINVAL happens if the file itself is deleted and
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher kernel has sent IN_IGNORED event which we haven't read. */
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher errno != EINVAL)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher i_error("inotify_rm_watch() failed: %m");
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher }
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher io_notify_fd_free(&ctx->fd_ctx, io);
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek if (ctx->fd_ctx.notifies == NULL)
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek io_remove(&ctx->event_io);
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek}
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void ioloop_inotify_user_limit_exceeded(void)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct passwd pw;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *name;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher uid_t uid = geteuid();
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (i_getpwuid(uid, &pw) <= 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher name = t_strdup_printf("UID %s", dec2str(uid));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher else {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher name = t_strdup_printf("%s (UID %s)",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher dec2str(uid), pw.pw_name);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_warning("Inotify instance limit for user %s exceeded, disabling. "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "Increase /proc/sys/fs/inotify/max_user_instances", name);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek struct ioloop *ioloop = current_ioloop;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek struct ioloop_notify_handler_context *ctx;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek ctx = ioloop->notify_handler_context =
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek i_new(struct ioloop_notify_handler_context, 1);
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek ctx->inotify_fd = inotify_init();
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek if (ctx->inotify_fd == -1) {
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek if (errno != EMFILE)
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek i_error("inotify_init() failed: %m");
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek else
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek ioloop_inotify_user_limit_exceeded();
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek ctx->disabled = TRUE;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek } else {
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek fd_close_on_exec(ctx->inotify_fd, TRUE);
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek fd_set_nonblock(ctx->inotify_fd, TRUE);
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek }
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek return ctx;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozekvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher{
f5b6f977d4144c28e9c66f3f1c9d634d595d1117Marko Myllynen struct ioloop_notify_handler_context *ctx =
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ioloop->notify_handler_context;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->inotify_fd != -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (close(ctx->inotify_fd) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_error("close(inotify) failed: %m");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->inotify_fd = -1;
72e60fd4eabcfbcdbfe01e8c38b94052bc6c2067Jakub Hrozek }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_free(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#endif
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher