From eda08c2f4e6e2d2589bcc7b4e44664bfec0bb612 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Mon, 29 Nov 2010 16:12:13 +0000 Subject: [PATCH] working on fsevents, fixed some warnings. --- fsevents.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++----- inotify.c | 6 +- lsyncd.c | 8 +- 3 files changed, 205 insertions(+), 25 deletions(-) diff --git a/fsevents.c b/fsevents.c index 70c45d7..90a95d1 100644 --- a/fsevents.c +++ b/fsevents.c @@ -1,5 +1,4 @@ -/** - * fsevents.c from Lsyncd - Live (Mirror) Syncing Demon +/** fsevents.c from Lsyncd - Live (Mirror) Syncing Demon * * License: GPLv2 (see COPYING) or any later version * @@ -7,30 +6,175 @@ * * ----------------------------------------------------------------------- * - * Event interface for MacOS 10(.5) /dev/fsevents interface. + * Event interface for MacOS 10.5 (Leopard) /dev/fsevents interface. * - * WARNING! AFAIK this interface is not strictly considered "public" API - * by Apple. Thus it might easily change between versions. Also its said, - * altough every event receiver has its own message queue, the OS X kernel - * only deletes a message after *all* registered receivers handled it. So - * one receiver blocking overflows all receivers. So spotlight might have - * to do more stuff, when Lsyncd might cause an overflow. Use at own risk. + * Special thanks go to Amit Singh and his fslogger demonstration that showed + * how apples /dev/fsevents can be used. http://osxbook.com/software/fslogger/ * - * Special thanks go to Amit Singh and his fslogger demonstration that - * showed how apples /dev/fsevents can be used. - * http://osxbook.com/software/fslogger/ + * -- WARNING -- Quoting http://www.osxbook.com/software/fslogger/ -- + * + * The interface that fslogger [and thus Lsyncd] uses is private to Apple. + * Currently, there is a caveat regarding the use of this interface by third + * parties (including fslogger [and thus Lsyncd]). While the change + * notification interface supports multiple clients, there is a single kernel + * buffer for holding events that are to be delivered to one or more + * subscribers, with the primary subscriber being Spotlight. Now, the kernel + * must hold events until it has notified all subscribers that are interested + * in them. Since there is a single buffer, a slow subscriber can cause it to + * overflow. If this happens, events will be dropped — for all subscribers, + * including Spotlight. Consequently, Spotlight may need to look at the entire + * volume to determine "what changed". */ #include "lsyncd.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bsd/sys/fsevents.h" + #include #include #include + +/* the fsevents pseudo-device */ +#define DEV_FSEVENTS "/dev/fsevents" + +/* buffer for reading from the device */ +#define FSEVENT_BUFSIZ 131072 +/* limited by MAX_KFS_EVENTS */ +#define EVENT_QUEUE_SIZE 4096 +#define KFS_NUM_ARGS FSE_MAX_ARGS + +/* OS 10.5 structuce */ +/* an event argument */ +struct kfs_event_arg { + /* argument type */ + u_int16_t type; + + /* size of argument data that follows this field */ + u_int16_t len; + + union { + struct vnode *vp; + char *str; + void *ptr; + int32_t int32; + dev_t dev; + ino_t ino; + int32_t mode; + uid_t uid; + gid_t gid; + uint64_t timestamp; + } data; +}; + +/* OS 10.5 structuce */ +/* an event */ +struct kfs_event { + + /* event type */ + int32_t type; + + /* pid of the process that performed the operation */ + pid_t pid; + + /* event arguments */ + struct kfs_event_arg args[KFS_NUM_ARGS]; +}; + +/** + * fsevents (cloned) filedescriptor + */ +static int fsevents_fd = -1; + static const luaL_reg lfseventslib[] = { {NULL, NULL} }; + +static size_t const readbuf_size = 131072; +static char * readbuf = NULL; + +/** + * Called when fsevents has something to read + */ +static void +fsevents_ready(lua_State *L, struct observance *obs) +{ + if (obs->fd != fsevents_fd) { + logstring("Error", "Internal, fsevents_fd != ob->fd"); + exit(-1); // ERRNO + } + while(true) { + ptrdiff_t len; + int err; + len = read (fsevents_fd, readbuf, readbuf_size); + err = errno; + if (len == 0) { + /* nothing more */ + break; + } + if (len < 0) { + if (err == EAGAIN) { + /* nothing more inotify */ + break; + } else { + printlogf(L, "Error", "Read fail on fsevents"); + exit(-1); // ERRNO + } + } + { + int off = 0; + int32_t atype; + uint32_t aflags; + + while (off < len && !hup && !term) { + struct kfs_event *event = (struct kfs_event *) &readbuf[off]; + off += sizeof(int32_t) + sizeof(pid_t); + + if (event->type == FSE_EVENTS_DROPPED) { + logstring("Fsevents", "Events dropped!"); + load_runner_func(L, "overflow"); + if (lua_pcall(L, 0, 0, -2)) { + exit(-1); // ERRNO + } + lua_pop(L, 1); + hup = 1; + off += sizeof(u_int16_t); + continue; + } + atype = event->type & FSE_TYPE_MASK; + aflags = FSE_GET_FLAGS(event->type); + } + + } + } +} + +/** + * Called to close/tidy fsevents + */ +static void +fsevents_tidy(struct observance *obs) +{ + if (obs->fd != fsevents_fd) { + logstring("Error", "Internal, fsevents_fd != ob->fd"); + exit(-1); // ERRNO + } + close(fsevents_fd); + +} + + /** * registers fsevents functions. */ @@ -45,14 +189,46 @@ register_fsevents(lua_State *L) { */ extern void open_fsevents(lua_State *L) { - // TODO + + int8_t event_list[] = { // action to take for each event + FSE_REPORT, /* FSE_CREATE_FILE */ + FSE_REPORT, /* FSE_DELETE */ + FSE_REPORT, /* FSE_STAT_CHANGED */ + FSE_REPORT, /* FSE_RENAME */ + FSE_REPORT, /* FSE_CONTENT_MODIFIED */ + FSE_REPORT, /* FSE_EXCHANGE */ + FSE_REPORT, /* FSE_FINDER_INFO_CHANGED */ + FSE_REPORT, /* FSE_CREATE_DIR */ + FSE_REPORT, /* FSE_CHOWN */ + FSE_REPORT, /* FSE_XATTR_MODIFIED */ + FSE_REPORT, /* FSE_XATTR_REMOVED */ + }; + struct fsevent_clone_args fca = { + .event_list = (int8_t *) event_list, + .num_events = sizeof(event_list)/sizeof(int8_t), + .event_queue_depth = EVENT_QUEUE_SIZE, + .fd = &fsevents_fd, + }; + int fd = open(DEV_FSEVENTS, O_RDONLY); + if (fd < 0) { + printlogf(L, "Error", + "Cannot access %s monitor! (%d:%s)", + DEV_FSEVENTS, errno, strerror(errno)); + exit(-1); // ERRNO + } + + if (ioctl(fd, FSEVENTS_CLONE, (char *)&fca) < 0) { + printlogf(L, "Error", + "Cannot control %s monitor! (%d:%s)", + DEV_FSEVENTS, errno, strerror(errno)); + exit(-1); // ERRNO + } + + /* fd has been cloned, closes access fd */ + close(fd); + close_exec_fd(fsevents_fd); + non_block_fd(fsevents_fd); + observe_fd(fsevents_fd, fsevents_ready, NULL, fsevents_tidy, NULL); } -/** - * closes fsevents - */ -extern void -close_fsevents() { - // TODO -} diff --git a/inotify.c b/inotify.c index 83e2316..4e012d8 100644 --- a/inotify.c +++ b/inotify.c @@ -71,7 +71,7 @@ static int l_addwatch(lua_State *L) { const char *path = luaL_checkstring(L, 1); - lua_Integer wd = inotify_add_watch(inotify_fd, path, standard_event_mask); + int wd = inotify_add_watch(inotify_fd, path, standard_event_mask); printlogf(L, "Inotify", "addwatch(%s)->%d", path, wd); lua_pushinteger(L, wd); return 1; @@ -86,7 +86,7 @@ l_addwatch(lua_State *L) static int l_rmwatch(lua_State *L) { - lua_Integer wd = luaL_checkinteger(L, 1); + int wd = luaL_checkinteger(L, 1); inotify_rm_watch(inotify_fd, wd); printlogf(L, "Inotify", "rmwatch()<-%d", wd); return 0; @@ -352,7 +352,7 @@ open_inotify(lua_State *L) { readbuf = s_malloc(readbuf_size); inotify_fd = inotify_init(); - if (inotify_fd == -1) { + if (inotify_fd < 0) { printlogf(L, "Error", "Cannot access inotify monitor! (%d:%s)", errno, strerror(errno)); diff --git a/lsyncd.c b/lsyncd.c index ef5b382..e7dccc2 100644 --- a/lsyncd.c +++ b/lsyncd.c @@ -1213,6 +1213,7 @@ masterloop(lua_State *L) { while(true) { bool have_alarm; + bool force_alarm; clock_t now = times(NULL); clock_t alarm_time; @@ -1223,7 +1224,8 @@ masterloop(lua_State *L) } if (lua_type(L, -1) == LUA_TBOOLEAN) { - have_alarm = lua_toboolean(L, -1); + have_alarm = false; + force_alarm = lua_toboolean(L, -1); } else { have_alarm = true; alarm_time = @@ -1231,7 +1233,9 @@ masterloop(lua_State *L) } lua_pop(L, 2); - if (have_alarm && time_before_eq(alarm_time, now)) { + if (force_alarm || + (have_alarm && time_before_eq(alarm_time, now)) + ) { /* there is a delay that wants to be handled already thus instead * of reading/writing from observances it jumps directly to * handling */