From 15649db953a9030e26607cb3246c59fb2f69b9c9 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 2 Mar 2011 13:37:06 +0100 Subject: [PATCH] inotify: refactored low-level functionality to inotify-backend module --- Makefile.include | 7 +++- src/inotify-backend.c | 93 ++++++++++++++++++++++++++++++++++++++++++ src/inotify-backend.h | 78 +++++++++++++++++++++++++++++++++++ src/inotify-syscalls.h | 67 ++++++++++++++++++++++++++++++ src/inotify.c | 89 ++++++++-------------------------------- 5 files changed, 261 insertions(+), 73 deletions(-) create mode 100644 src/inotify-backend.c create mode 100644 src/inotify-backend.h create mode 100644 src/inotify-syscalls.h diff --git a/Makefile.include b/Makefile.include index 3c8a293..60b8dfd 100644 --- a/Makefile.include +++ b/Makefile.include @@ -27,9 +27,14 @@ obj-rbtree = src/rbtree.o $(obj-xalloc) obj-tree = src/tree.o $(obj-xalloc) obj-fscrawl = src/fscrawl.o $(obj-strbuf) $(obj-path) $(obj-log) obj-log = src/log.o $(obj-strbuf) $(obj-xalloc) + +# inotify +obj-inotify-backend = src/inotify-backend.o $(obj-log) obj-inotify-watch = src/inotify-watch.o $(obj-tree) obj-inotify-map = src/inotify-map.o $(obj-inotify-watch) $(obj-path) $(obj-rbtree) $(obj-list) -obj-notify = src/event.o src/queue.o src/inotify.o $(obj-inotify-map) $(obj-xalloc) $(obj-fscrawl) $(obj-rbtree) +obj-inotify = src/inotify.o src/queue.o $(obj-inotify-backend) $(obj-inotify-map) + +obj-notify = src/event.o $(obj-inotify) $(obj-xalloc) $(obj-fscrawl) obj-ini = lib/ini/iniparser.o lib/ini/dictionary.o obj-mongo = src/database/mongo.o $(obj-path) $(obj-ini) obj-mysql = src/database/mysql.o $(obj-ini) $(obj-xalloc) diff --git a/src/inotify-backend.c b/src/inotify-backend.c new file mode 100644 index 0000000..2f5d2f7 --- /dev/null +++ b/src/inotify-backend.c @@ -0,0 +1,93 @@ +/* inotify-backend.c + * + * Copyright (C) 2010-2011 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "inotify-backend.h" +#include "inotify-syscalls.h" +#include "log.h" + +#define WATCH_MASK (IN_MOVE | IN_CREATE | IN_DELETE | IN_ONLYDIR) + +static int fd = -1; + +static unsigned inotify_qsize = 256; + +int inotify_backend_init(void) { + + if (fd >= 0) + close(fd); + + fd = inotify_init(); + return fd; +} + +void inotify_backend_exit(void) { + + if (fd >= 0) + close(fd); + fd = -1; +} + +int inotify_backend_watch(const char *path) { + + int wd = inotify_add_watch(fd, path, WATCH_MASK); + + if (wd < 0) { + if (errno != EACCES && errno != ENOTDIR) + logerrno(LOG_CRIT, "inotify_watch", errno); + return -errno; + } + return wd; +} + +int inotify_backend_ignore(int wd) { + + if (inotify_rm_watch(fd, wd) < 0) { + logerrno(LOG_CRIT, "intotify_ignore", errno); + return -errno; + } + return 0; +} + +#define MAX_PENDING 5 +#define THROTTLE_SLEEP_NS 2000000 +#define UPPER_BOUND(size) ((unsigned) (size) >> 1) + +int inotify_backend_read(void *buf, size_t size) { + + struct timespec tres = { 0, THROTTLE_SLEEP_NS }; + unsigned short tcount; + unsigned int ioready = 0; + + for(tcount = 0; tcount < MAX_PENDING; tcount++) { + + unsigned int events; + + if (ioctl(fd, FIONREAD, &ioready) < 0) + break; + + events = ioready / IN_EVENT_SIZE; + if (events > UPPER_BOUND(inotify_qsize)) + goto do_read; + + nanosleep(&tres, NULL); + } + + if (ioready) + goto do_read; + + return 0; +do_read: + return read(fd, buf, size); +} diff --git a/src/inotify-backend.h b/src/inotify-backend.h new file mode 100644 index 0000000..7ae9aea --- /dev/null +++ b/src/inotify-backend.h @@ -0,0 +1,78 @@ +/* inotify-backend.h + * + * Copyright (C) 2005, 2006, 2008, 2009 Free Software Foundation, Inc. + * Copyright (C) 2010-2011 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef __INOTIFY_BACKEND +#define __INOTIFY_BACKEND + +#include + +/* Structure describing an inotify event. */ +struct inotify_event { + int wd; /* Watch descriptor. */ + uint32_t mask; /* Watch mask. */ + uint32_t cookie; /* Cookie to synchronize two events. */ + uint32_t len; /* Length (including NULs) of name. */ + char name __flexarr; /* Name. */ +}; + +#define IN_EVENT_SIZE (sizeof(struct inotify_event) + 0x40) + +/* Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. */ +#define IN_ACCESS 0x00000001 /* File was accessed. */ +#define IN_MODIFY 0x00000002 /* File was modified. */ +#define IN_ATTRIB 0x00000004 /* Metadata changed. */ +#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed. */ +#define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed. */ +#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. */ +#define IN_OPEN 0x00000020 /* File was opened. */ +#define IN_MOVED_FROM 0x00000040 /* File was moved from X. */ +#define IN_MOVED_TO 0x00000080 /* File was moved to Y. */ +#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* Moves. */ +#define IN_CREATE 0x00000100 /* Subfile was created. */ +#define IN_DELETE 0x00000200 /* Subfile was deleted. */ +#define IN_DELETE_SELF 0x00000400 /* Self was deleted. */ +#define IN_MOVE_SELF 0x00000800 /* Self was moved. */ + +/* Events sent by the kernel. */ +#define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted. */ +#define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed. */ +#define IN_IGNORED 0x00008000 /* File was ignored. */ + +/* Helper events. */ +#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. */ +#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* Moves. */ + +/* Special flags. */ +#define IN_ONLYDIR 0x01000000 /* Only watch the path if it is a + directory. */ +#define IN_DONT_FOLLOW 0x02000000 /* Do not follow a sym link. */ +#define IN_MASK_ADD 0x20000000 /* Add to the mask of an already + existing watch. */ +#define IN_ISDIR 0x40000000 /* Event occurred against dir. */ +#define IN_ONESHOT 0x80000000 /* Only send event once. */ + +/* All events which a program can wait on. */ +#define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE \ + | IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM \ + | IN_MOVED_TO | IN_CREATE | IN_DELETE \ + | IN_DELETE_SELF | IN_MOVE_SELF) + +int inotify_backend_init(void); + +void inotify_backend_exit(void); + +int inotify_backend_watch(const char *path); + +int inotify_backend_ignore(int wd); + +int inotify_backend_read(void *buf, size_t size); + +#endif /* __INOTIFY_BACKEND */ diff --git a/src/inotify-syscalls.h b/src/inotify-syscalls.h new file mode 100644 index 0000000..77902f2 --- /dev/null +++ b/src/inotify-syscalls.h @@ -0,0 +1,67 @@ +#ifndef _LINUX_INOTIFY_SYSCALLS_H +#define _LINUX_INOTIFY_SYSCALLS_H + +#include +#include +#include + +#if defined(__i386__) +# define __NR_inotify_init 291 +# define __NR_inotify_add_watch 292 +# define __NR_inotify_rm_watch 293 +#elif defined(__x86_64__) +# define __NR_inotify_init 253 +# define __NR_inotify_add_watch 254 +# define __NR_inotify_rm_watch 255 +#elif defined(__alpha__) +# define __NR_inotify_init 444 +# define __NR_inotify_add_watch 445 +# define __NR_inotify_rm_watch 446 +#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) +# define __NR_inotify_init 275 +# define __NR_inotify_add_watch 276 +# define __NR_inotify_rm_watch 277 +#elif defined(__sparc__) || defined (__sparc64__) +# define __NR_inotify_init 151 +# define __NR_inotify_add_watch 152 +# define __NR_inotify_rm_watch 156 +#elif defined (__ia64__) +# define __NR_inotify_init 1277 +# define __NR_inotify_add_watch 1278 +# define __NR_inotify_rm_watch 1279 +#elif defined (__s390__) || defined (__s390x__) +# define __NR_inotify_init 284 +# define __NR_inotify_add_watch 285 +# define __NR_inotify_rm_watch 286 +#elif defined (__arm__) +# define __NR_inotify_init 316 +# define __NR_inotify_add_watch 317 +# define __NR_inotify_rm_watch 318 +#elif defined (__SH4__) +# define __NR_inotify_init 290 +# define __NR_inotify_add_watch 291 +# define __NR_inotify_rm_watch 292 +#elif defined (__SH5__) +# define __NR_inotify_init 318 +# define __NR_inotify_add_watch 319 +# define __NR_inotify_rm_watch 320 +#else +# error "Unsupported architecture" +#endif + +static inline int inotify_init(void) { + + return syscall(__NR_inotify_init); +} + +static inline int inotify_add_watch(int fd, const char *name, __u32 mask) { + + return syscall(__NR_inotify_add_watch, fd, name, mask); +} + +static inline int inotify_rm_watch(int fd, __u32 wd) { + + return syscall(__NR_inotify_rm_watch, fd, wd); +} + +#endif /* _LINUX_INOTIFY_SYSCALLS_H */ diff --git a/src/inotify.c b/src/inotify.c index 13e6596..0e39a75 100644 --- a/src/inotify.c +++ b/src/inotify.c @@ -14,10 +14,8 @@ #include #include #include -#include -#include -#include +#include "inotify-backend.h" #include "inotify-map.h" #include "inotify-watch.h" #include "xalloc.h" @@ -28,12 +26,6 @@ #include "fscrawl.h" #include "notify.h" -typedef struct inotify_event inoev; - -#define INOBUFSIZE ((1 << 12) * (sizeof(inoev) + 0x40)) - -#define WATCH_MASK (IN_MOVE | IN_CREATE | IN_DELETE | IN_ONLYDIR) - static int init = 0; /* Inotify file descriptor */ @@ -41,17 +33,12 @@ static int fd; static queue_t event_queue; -static int inotify_watch(const char *path) { +static int watch(const char *path) { - int wd = inotify_add_watch(fd, path, WATCH_MASK); - - if (wd < 0) { - if (errno != EACCES && errno != ENOTDIR) - logerrno(LOG_CRIT, "inotify_watch", errno); - return -1; - } + int wd = inotify_backend_watch(path); - inotify_map(wd, path); + if (wd >= 0) + inotify_map(wd, path); return wd; } @@ -64,7 +51,7 @@ static int addwatch(const char *path, const char *name) { if (!npath) return -1; - if (inotify_watch(npath) < 0) + if (watch(npath) < 0) goto clean; f = fsc_open(npath); @@ -88,7 +75,7 @@ static int addwatch(const char *path, const char *name) { if (ent->dir) { char *fullpath = path_normalize(ev->path, ev->filename, 1); if (fullpath) { - if (inotify_watch(fullpath) < 0) { + if (watch(fullpath) < 0) { xfree(fullpath); } } @@ -136,7 +123,7 @@ static int rmwatch(const char *path, const char *name) { return 0; } -static void proc_event(inoev *iev) { +static void proc_event(struct inotify_event *iev) { int i; struct list *watch_list; @@ -199,10 +186,7 @@ int notify_init() { if (init) return 0; - fd = inotify_init(); - - if (fd < 0) - return -1; + inotify_backend_init(); event_queue = queue_init(); @@ -217,8 +201,6 @@ void notify_exit() { if (!init) return; - - close(fd); inotify_unmap_all(); @@ -248,61 +230,24 @@ int notify_rm_watch(const char *path) { return rmwatch(path, NULL); } -notify_event* notify_read() { +#define BUFSZ (IN_EVENT_SIZE * (1 << 10)) - /* bytes ready on the inotify descriptor */ - int ioready; +notify_event* notify_read() { if (!init) die("inotify is not instantiated."); - /* if we don't have pending events, wait for more data on fd */ if (queue_isempty(event_queue)) { - /* time resolution */ - struct timespec tres = { 0, 2000000 }; + char buf[BUFSZ]; + int offset = 0, read = inotify_backend_read(buf, BUFSZ); - unsigned short tcount; - - for(tcount = 0; tcount < 10; tcount++) { - - if (ioctl(fd, FIONREAD, &ioready) == -1) - break; - - if (ioready > INOBUFSIZE) - break; - - nanosleep(&tres, NULL); + while(read > offset) { + struct inotify_event *ev = (struct inotify_event*) &buf[offset]; + proc_event(ev); + offset += sizeof(struct inotify_event) + ev->len; } } - /* otherwise, only read if the data available at - this given moment is "large enough" */ - else { - ioctl(fd, FIONREAD, &ioready); - - if (ioready < INOBUFSIZE / 2) - ioready = 0; - } - - while(ioready > 0) { - - char buf[INOBUFSIZE]; - int offset = 0, rbytes = read(fd, buf, INOBUFSIZE); - - logmsg(LOG_DEBUG, "%i bytes avail", ioready); - - if (rbytes == -1) { - logerrno(LOG_WARN, "INOTIFY", errno); - break; - } - - while(rbytes > offset) { - inoev *rev = (inoev *) &buf[offset]; - proc_event(rev); - offset += sizeof(inoev) + rev->len; - } - ioready -= rbytes; - } return queue_dequeue(event_queue); }