ioloop-notify-inotify.c revision fdb97244fa45c32c3593726c15aa69ce29bc7121
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#define _GNU_SOURCE
2423da95ee20fd4b3c260c1389cf2952d25f099cTimo Sirainen#include "lib.h"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "fd-close-on-exec.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "fd-set-nonblock.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "ioloop-internal.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "ioloop-notify-fd.h"
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen#include "buffer.h"
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen#include "network.h"
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include <stdio.h>
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen#include <unistd.h>
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen#include <fcntl.h>
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include <pwd.h>
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include <sys/ioctl.h>
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include <sys/inotify.h>
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#define INOTIFY_BUFLEN (32*1024)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstruct ioloop_notify_handler_context {
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen struct ioloop_notify_fd_context fd_ctx;
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen int inotify_fd;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct io *event_io;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen bool disabled;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen};
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic bool inotify_input_more(struct ioloop *ioloop)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct ioloop_notify_handler_context *ctx =
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen ioloop->notify_handler_context;
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen const struct inotify_event *event;
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen unsigned char event_buf[INOTIFY_BUFLEN];
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct io_notify *io;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ssize_t ret, pos;
d92f33f13830ba23d814342bf3ea8db721a15bb1Timo Sirainen
d92f33f13830ba23d814342bf3ea8db721a15bb1Timo Sirainen /* read as many events as there is available and fit into our buffer.
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen only full events are returned by the kernel. */
61e84692827b6a64912343f515c984853021483aTimo Sirainen ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (ret <= 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (ret == 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* nothing more to read */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen return FALSE;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen }
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen i_fatal("read(inotify) failed: %m");
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen }
7230352f1177a8ed2b924c6992e751fd2ab9bc27Timo Sirainen
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen i_fatal("gettimeofday(): %m");
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen for (pos = 0; pos < ret; ) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if ((size_t)(ret - pos) < sizeof(*event))
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen break;
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen event = (struct inotify_event *)(event_buf + pos);
0e5f8c4589cfeccb752307c8ac35a2f1633e4ecaTimo Sirainen pos += sizeof(*event) + event->len;
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen if (io != NULL) {
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen if ((event->mask & IN_IGNORED) != 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* calling inotify_rm_watch() would now give
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen EINVAL */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io->fd = -1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io->io.callback(io->io.context);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (pos != ret)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_error("read(inotify) returned partial event");
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return (size_t)ret >= sizeof(event_buf)-512;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void inotify_input(struct ioloop *ioloop)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
bbd0a870f8639767e4e4011d2aedadac08d5c66fTimo Sirainen while (inotify_input_more(ioloop)) ;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen}
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#undef io_add_notify
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenenum io_notify_result io_add_notify(const char *path, io_callback_t *callback,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen void *context, struct io **io_r)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
c58906589cafc32df4c04ffbef933baadd3f2276Timo Sirainen struct ioloop_notify_handler_context *ctx =
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen current_ioloop->notify_handler_context;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int wd;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen *io_r = NULL;
d02d34e138e32b4266f5a403d6c51d7803bf322fTimo Sirainen
106b804c819443791f1324f8bbe34429eeea6a13Timo Sirainen if (ctx == NULL)
d02d34e138e32b4266f5a403d6c51d7803bf322fTimo Sirainen ctx = io_loop_notify_handler_init();
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (ctx->disabled)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return IO_NOTIFY_NOSUPPORT;
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainen
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen wd = inotify_add_watch(ctx->inotify_fd, path,
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen IN_CREATE | IN_DELETE | IN_DELETE_SELF |
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen IN_MOVE | IN_CLOSE | IN_MODIFY);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen if (wd < 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen message then. */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (errno == ENOENT || errno == ESTALE)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return IO_NOTIFY_NOTFOUND;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (errno != ENOSPC)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen else {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_warning("Inotify watch limit for user exceeded, "
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "disabling. Increase "
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "/proc/sys/fs/inotify/max_user_watches");
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen }
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen ctx->disabled = TRUE;
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen return IO_NOTIFY_NOSUPPORT;
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen }
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen if (ctx->event_io == NULL) {
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen inotify_input, current_ioloop);
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen }
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return IO_NOTIFY_ADDED;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenvoid io_loop_notify_remove(struct io *_io)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct ioloop_notify_handler_context *ctx =
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen _io->ioloop->notify_handler_context;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen struct io_notify *io = (struct io_notify *)_io;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (io->fd != -1) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen /* ernro=EINVAL happens if the file itself is deleted and
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen kernel has sent IN_IGNORED event which we haven't read. */
4645cc6c911a95991d7af43b40f88e99506ea5e9Timo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen errno != EINVAL)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen i_error("inotify_rm_watch() failed: %m");
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen }
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen io_notify_fd_free(&ctx->fd_ctx, io);
4366a21968093172d9b757fe6894b1ee8916434eTimo Sirainen
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen if (ctx->fd_ctx.notifies == NULL)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen io_remove(&ctx->event_io);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen}
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainenstatic void ioloop_inotify_user_limit_exceeded(void)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen const struct passwd *pw;
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen const char *name;
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen uid_t uid = geteuid();
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen pw = getpwuid(uid);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen if (pw == NULL)
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen name = t_strdup_printf("UID %s", dec2str(uid));
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen else {
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen name = t_strdup_printf("%s (UID %s)",
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen dec2str(uid), pw->pw_name);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen }
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_warning("Inotify instance limit for user %s exceeded, disabling. "
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen "Increase /proc/sys/fs/inotify/max_user_instances", name);
}
static struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
{
struct ioloop *ioloop = current_ioloop;
struct ioloop_notify_handler_context *ctx;
ctx = ioloop->notify_handler_context =
i_new(struct ioloop_notify_handler_context, 1);
ctx->inotify_fd = inotify_init();
if (ctx->inotify_fd == -1) {
if (errno != EMFILE)
i_error("inotify_init() failed: %m");
else
ioloop_inotify_user_limit_exceeded();
ctx->disabled = TRUE;
} else {
fd_close_on_exec(ctx->inotify_fd, TRUE);
fd_set_nonblock(ctx->inotify_fd, TRUE);
}
return ctx;
}
void io_loop_notify_handler_deinit(struct ioloop *ioloop)
{
struct ioloop_notify_handler_context *ctx =
ioloop->notify_handler_context;
if (ctx->inotify_fd != -1) {
if (close(ctx->inotify_fd) < 0)
i_error("close(inotify) failed: %m");
ctx->inotify_fd = -1;
}
i_free(ctx);
}
#endif