Fix handling recursive inotify based directory watchers.
This commit is contained in:
parent
0ec498207d
commit
5246593432
|
@ -15,25 +15,42 @@ final class InotifyEventDriverWatchers(Events : EventDriverEvents) : EventDriver
|
||||||
private {
|
private {
|
||||||
alias Loop = typeof(Events.init.loop);
|
alias Loop = typeof(Events.init.loop);
|
||||||
Loop m_loop;
|
Loop m_loop;
|
||||||
string[int][WatcherID] m_watches; // TODO: use a @nogc (allocator based) map
|
|
||||||
|
struct WatchState {
|
||||||
|
string[int] watcherPaths;
|
||||||
|
string basePath;
|
||||||
|
bool recursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchState[WatcherID] m_watches; // TODO: use a @nogc (allocator based) map
|
||||||
}
|
}
|
||||||
|
|
||||||
this(Events events) { m_loop = events.loop; }
|
this(Events events) { m_loop = events.loop; }
|
||||||
|
|
||||||
final override WatcherID watchDirectory(string path, bool recursive, FileChangesCallback callback)
|
final override WatcherID watchDirectory(string path, bool recursive, FileChangesCallback callback)
|
||||||
{
|
{
|
||||||
|
import std.path : buildPath, pathSplitter;
|
||||||
|
import std.range : drop;
|
||||||
|
import std.range.primitives : walkLength;
|
||||||
|
|
||||||
enum IN_NONBLOCK = 0x800; // value in core.sys.linux.sys.inotify is incorrect
|
enum IN_NONBLOCK = 0x800; // value in core.sys.linux.sys.inotify is incorrect
|
||||||
auto handle = () @trusted { return inotify_init1(IN_NONBLOCK); } ();
|
auto handle = () @trusted { return inotify_init1(IN_NONBLOCK); } ();
|
||||||
if (handle == -1) return WatcherID.invalid;
|
if (handle == -1) return WatcherID.invalid;
|
||||||
|
|
||||||
auto ret = WatcherID(handle);
|
auto ret = WatcherID(handle);
|
||||||
|
|
||||||
addWatch(ret, path);
|
m_watches[ret] = WatchState(null, path, recursive);
|
||||||
|
|
||||||
|
addWatch(ret, path, ".");
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
try {
|
try {
|
||||||
|
auto base_segements = path.pathSplitter.walkLength;
|
||||||
if (path.isDir) () @trusted {
|
if (path.isDir) () @trusted {
|
||||||
foreach (de; path.dirEntries(SpanMode.shallow))
|
foreach (de; path.dirEntries(SpanMode.depth))
|
||||||
if (de.isDir) addWatch(ret, de.name);
|
if (de.isDir) {
|
||||||
|
auto subdir = de.name.pathSplitter.drop(base_segements).buildPath;
|
||||||
|
addWatch(ret, path, subdir);
|
||||||
|
}
|
||||||
} ();
|
} ();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: decide if this should be ignored or if the error should be forwarded
|
// TODO: decide if this should be ignored or if the error should be forwarded
|
||||||
|
@ -78,6 +95,7 @@ final class InotifyEventDriverWatchers(Events : EventDriverEvents) : EventDriver
|
||||||
|
|
||||||
private void processEvents(WatcherID id)
|
private void processEvents(WatcherID id)
|
||||||
{
|
{
|
||||||
|
import std.path : buildPath, dirName;
|
||||||
import core.stdc.stdio : FILENAME_MAX;
|
import core.stdc.stdio : FILENAME_MAX;
|
||||||
import core.stdc.string : strlen;
|
import core.stdc.string : strlen;
|
||||||
|
|
||||||
|
@ -89,9 +107,16 @@ final class InotifyEventDriverWatchers(Events : EventDriverEvents) : EventDriver
|
||||||
break;
|
break;
|
||||||
assert(ret <= buf.length);
|
assert(ret <= buf.length);
|
||||||
|
|
||||||
|
auto w = m_watches[id];
|
||||||
|
|
||||||
auto rem = buf[0 .. ret];
|
auto rem = buf[0 .. ret];
|
||||||
while (rem.length > 0) {
|
while (rem.length > 0) {
|
||||||
auto ev = () @trusted { return cast(inotify_event*)rem.ptr; } ();
|
auto ev = () @trusted { return cast(inotify_event*)rem.ptr; } ();
|
||||||
|
rem = rem[inotify_event.sizeof + ev.len .. $];
|
||||||
|
|
||||||
|
// is the watch already deleted?
|
||||||
|
if (ev.mask & IN_IGNORED) continue;
|
||||||
|
|
||||||
FileChange ch;
|
FileChange ch;
|
||||||
if (ev.mask & (IN_CREATE|IN_MOVED_TO))
|
if (ev.mask & (IN_CREATE|IN_MOVED_TO))
|
||||||
ch.kind = FileChangeKind.added;
|
ch.kind = FileChangeKind.added;
|
||||||
|
@ -100,28 +125,44 @@ final class InotifyEventDriverWatchers(Events : EventDriverEvents) : EventDriver
|
||||||
else if (ev.mask & IN_MODIFY)
|
else if (ev.mask & IN_MODIFY)
|
||||||
ch.kind = FileChangeKind.modified;
|
ch.kind = FileChangeKind.modified;
|
||||||
|
|
||||||
|
if (ev.mask & IN_DELETE_SELF) {
|
||||||
|
() @trusted { inotify_rm_watch(cast(int)id, ev.wd); } ();
|
||||||
|
w.watcherPaths.remove(ev.wd);
|
||||||
|
continue;
|
||||||
|
} else if (ev.mask & IN_MOVE_SELF) {
|
||||||
|
// NOTE: the should have been updated by a previous IN_MOVED_TO
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto name = () @trusted { return ev.name.ptr[0 .. strlen(ev.name.ptr)]; } ();
|
auto name = () @trusted { return ev.name.ptr[0 .. strlen(ev.name.ptr)]; } ();
|
||||||
ch.directory = m_watches[id][ev.wd];
|
auto subdir = w.watcherPaths[ev.wd];
|
||||||
|
|
||||||
|
if (w.recursive && ev.mask & (IN_CREATE|IN_MOVED_TO) && ev.mask & IN_ISDIR) {
|
||||||
|
addWatch(id, w.basePath, subdir == "." ? name.idup : buildPath(subdir, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.baseDirectory = m_watches[id].basePath;
|
||||||
|
ch.directory = subdir;
|
||||||
ch.isDirectory = (ev.mask & IN_ISDIR) != 0;
|
ch.isDirectory = (ev.mask & IN_ISDIR) != 0;
|
||||||
ch.name = name;
|
ch.name = name;
|
||||||
addRef(id); // assure that the id doesn't get invalidated until after the callback
|
addRef(id); // assure that the id doesn't get invalidated until after the callback
|
||||||
auto cb = m_loop.m_fds[id].watcher.callback;
|
auto cb = m_loop.m_fds[id].watcher.callback;
|
||||||
cb(id, ch);
|
cb(id, ch);
|
||||||
if (!releaseRef(id)) return;
|
if (!releaseRef(id)) return;
|
||||||
|
|
||||||
rem = rem[inotify_event.sizeof + ev.len .. $];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool addWatch(WatcherID handle, string path)
|
private bool addWatch(WatcherID handle, string base_path, string path)
|
||||||
{
|
{
|
||||||
|
import std.path : buildPath;
|
||||||
import std.string : toStringz;
|
import std.string : toStringz;
|
||||||
|
|
||||||
enum EVENTS = IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
|
enum EVENTS = IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
|
||||||
IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO;
|
IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO;
|
||||||
immutable wd = () @trusted { return inotify_add_watch(cast(int)handle, path.toStringz, EVENTS); } ();
|
immutable wd = () @trusted { return inotify_add_watch(cast(int)handle, buildPath(base_path, path).toStringz, EVENTS); } ();
|
||||||
if (wd == -1) return false;
|
if (wd == -1) return false;
|
||||||
m_watches[handle][wd] = path;
|
m_watches[handle].watcherPaths[wd] = path;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue