ioloop-notify-inotify.c revision d347e6fe2a08ec1514980f5bc4a958d6c01b4720
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define _GNU_SOURCE
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifdef IOLOOP_NOTIFY_INOTIFY
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "fd-close-on-exec.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "fd-set-nonblock.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop-internal.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop-notify-fd.h"
8ef80b0b9c73fb0a0188788b14b3e15084b7a452Timo Sirainen#include "buffer.h"
56561d472db25ebda35ae6afdc7f7deb75c323fcTimo Sirainen#include "network.h"
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <stdio.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <unistd.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <fcntl.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <sys/ioctl.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <sys/inotify.h>
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen#define INOTIFY_BUFLEN (32*1024)
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct ioloop_notify_handler_context {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct ioloop_notify_fd_context fd_ctx;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen int inotify_fd;
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen struct io *event_io;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen bool disabled;
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen};
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void);
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainenstatic bool inotify_input_more(struct ioloop *ioloop)
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen{
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen struct ioloop_notify_handler_context *ctx =
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ioloop->notify_handler_context;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen const struct inotify_event *event;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen unsigned char event_buf[INOTIFY_BUFLEN];
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct io_notify *io;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen ssize_t ret, pos;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen /* read as many events as there is available and fit into our buffer.
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen only full events are returned by the kernel. */
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen ret = read(ctx->inotify_fd, event_buf, sizeof(event_buf));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (ret <= 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (ret == 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* nothing more to read */
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen return FALSE;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen }
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen i_fatal("read(inotify) failed: %m");
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen }
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen if (gettimeofday(&ioloop_timeval, &ioloop_timezone) < 0)
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen i_fatal("gettimeofday(): %m");
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen ioloop_time = ioloop_timeval.tv_sec;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen for (pos = 0; pos < ret; ) {
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen if ((size_t)(ret - pos) < sizeof(*event))
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen break;
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen event = (struct inotify_event *)(event_buf + pos);
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen pos += sizeof(*event) + event->len;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen io = io_notify_fd_find(&ctx->fd_ctx, event->wd);
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen if (io != NULL) {
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen if ((event->mask & IN_IGNORED) != 0) {
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen /* calling inotify_rm_watch() would now give
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen EINVAL */
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen io->fd = -1;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen }
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen io->io.callback(io->io.context);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen }
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen }
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen if (pos != ret)
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen i_error("read(inotify) returned partial event");
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen return (size_t)ret >= sizeof(event_buf)-512;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen}
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainenstatic void inotify_input(struct ioloop *ioloop)
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen{
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen while (inotify_input_more(ioloop)) ;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen}
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen#undef io_add_notify
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainenenum io_notify_result io_add_notify(const char *path, io_callback_t *callback,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen void *context, struct io **io_r)
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen{
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen struct ioloop_notify_handler_context *ctx =
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen current_ioloop->notify_handler_context;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen int wd;
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen
8d131435ba4648c8821160ec38d508c97177c715Timo Sirainen *io_r = NULL;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (ctx == NULL)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ctx = io_loop_notify_handler_init();
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen if (ctx->disabled)
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen return IO_NOTIFY_NOSUPPORT;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen wd = inotify_add_watch(ctx->inotify_fd, path,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen IN_CREATE | IN_DELETE | IN_DELETE_SELF |
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen IN_MOVE | IN_CLOSE | IN_MODIFY);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (wd < 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* ESTALE could happen with NFS. Don't bother giving an error
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen message then. */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (errno == ENOENT || errno == ESTALE)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return IO_NOTIFY_NOTFOUND;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_error("inotify_add_watch(%s) failed: %m", path);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ctx->disabled = TRUE;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return IO_NOTIFY_NOSUPPORT;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (ctx->event_io == NULL) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ctx->event_io = io_add(ctx->inotify_fd, IO_READ,
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen inotify_input, current_ioloop);
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen }
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainen *io_r = io_notify_fd_add(&ctx->fd_ctx, wd, callback, context);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return IO_NOTIFY_ADDED;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen}
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenvoid io_loop_notify_remove(struct ioloop *ioloop, struct io *_io)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen struct ioloop_notify_handler_context *ctx =
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ioloop->notify_handler_context;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen struct io_notify *io = (struct io_notify *)_io;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (io->fd != -1) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* ernro=EINVAL happens if the file itself is deleted and
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen kernel has sent IN_IGNORED event which we haven't read. */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (inotify_rm_watch(ctx->inotify_fd, io->fd) < 0 &&
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen errno != EINVAL)
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen i_error("inotify_rm_watch() failed: %m");
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen io_notify_fd_free(&ctx->fd_ctx, io);
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen if (ctx->fd_ctx.notifies == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen io_remove(&ctx->event_io);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen}
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct ioloop_notify_handler_context *io_loop_notify_handler_init(void)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen{
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen struct ioloop *ioloop = current_ioloop;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen struct ioloop_notify_handler_context *ctx;
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainen ctx = ioloop->notify_handler_context =
659fe5d24825b160cae512538088020d97a60239Timo Sirainen i_new(struct ioloop_notify_handler_context, 1);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->inotify_fd = inotify_init();
7b3bf1de3fa7eee2185d1e404812b50c295e2b93Timo Sirainen if (ctx->inotify_fd == -1) {
5cdd348121e62a6244ba2f93db781731f7129a71Timo Sirainen if (errno != EMFILE)
5cdd348121e62a6244ba2f93db781731f7129a71Timo Sirainen i_error("inotify_init() failed: %m");
5cdd348121e62a6244ba2f93db781731f7129a71Timo Sirainen else {
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen i_warning("Inotify instance limit for user exceeded, "
659fe5d24825b160cae512538088020d97a60239Timo Sirainen "disabling. Increase "
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen "/proc/sys/fs/inotify/max_user_instances");
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen }
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen ctx->disabled = TRUE;
e063aca6bc2f08bec516d4b631052ea9191f011dTimo Sirainen } else {
e063aca6bc2f08bec516d4b631052ea9191f011dTimo Sirainen fd_close_on_exec(ctx->inotify_fd, TRUE);
e063aca6bc2f08bec516d4b631052ea9191f011dTimo Sirainen fd_set_nonblock(ctx->inotify_fd, TRUE);
e063aca6bc2f08bec516d4b631052ea9191f011dTimo Sirainen }
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen return ctx;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid io_loop_notify_handler_deinit(struct ioloop *ioloop)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct ioloop_notify_handler_context *ctx =
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ioloop->notify_handler_context;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainen if (ctx->inotify_fd != -1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (close(ctx->inotify_fd) < 0)
7b3bf1de3fa7eee2185d1e404812b50c295e2b93Timo Sirainen i_error("close(inotify) failed: %m");
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen ctx->inotify_fd = -1;
7b3bf1de3fa7eee2185d1e404812b50c295e2b93Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(ctx);
659fe5d24825b160cae512538088020d97a60239Timo Sirainen}
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#endif
b032dc80e358f09893f09999f172ff12f5dbbb8eTimo Sirainen