1 module ws.inotify; 2 3 4 import 5 core.sys.linux.sys.inotify, 6 core.sys.posix.sys.time, 7 core.sys.posix.unistd, 8 core.sys.posix.sys.select, 9 core.stdc.errno, 10 std.string, 11 std.stdio, 12 std.algorithm, 13 std.conv, 14 std.file, 15 ws.event; 16 17 18 version(Posix): 19 20 __gshared: 21 22 23 class Inotify { 24 25 enum EVENT_BUFFER_LENGTH = (inotify_event.sizeof + 16) * 1024; 26 27 static int inotify = -1; 28 static timeval timeOut; 29 static fd_set descriptorSet; 30 31 static Watcher[int] staticWatchers; 32 33 enum { 34 Add, 35 Remove, 36 Modify 37 } 38 39 40 shared static this(){ 41 if(inotify >= 0) 42 return; 43 inotify = inotify_init; 44 if(inotify < 0) 45 throw new Exception("Failed to initialize inotify: %s".format(errno)); 46 timeOut.tv_sec = 0; 47 timeOut.tv_usec = 0; 48 FD_ZERO(&descriptorSet); 49 } 50 51 52 static class Watcher { 53 this(){ 54 event = new Event!(string, string, int); 55 } 56 string directory; 57 Event!(string, string, int) event; 58 } 59 60 61 static void watch(string path, void delegate(string, string, int) event){ 62 assert(inotify >= 0); 63 if(!path.isDir) 64 throw new Exception("\"" ~ path ~ "\" is not a directory. Please don't watch single files, they need to be \"re-watched\" in almost all cases on change"); 65 foreach(wd, watcher; staticWatchers){ 66 if(watcher.directory == path){ 67 watcher.event ~= event; 68 return; 69 } 70 } 71 if(staticWatchers.values.find!(a => a.directory == path).length){ 72 return; 73 } 74 int wd = inotify_add_watch(inotify, path.toStringz, 75 IN_CLOSE_WRITE 76 | IN_MOVED_FROM 77 | IN_MOVED_TO 78 | IN_CREATE 79 | IN_DELETE 80 | IN_MASK_ADD); 81 if(wd < 0) 82 throw new Exception("inotify error in %s: %s".format(path, errno)); 83 auto watcher = new Watcher; 84 watcher.directory = path; 85 watcher.event ~= event; 86 staticWatchers[wd] = watcher; 87 } 88 89 static void update(){ 90 FD_SET(inotify, &descriptorSet); 91 int ret = select(inotify + 1, &descriptorSet, null, null, &timeOut); 92 if(ret < 0){ 93 perror("select"); 94 }else if(FD_ISSET(inotify, &descriptorSet)){ 95 ssize_t len, i = 0; 96 byte[EVENT_BUFFER_LENGTH] buff = 0; 97 len = read(inotify, buff.ptr, buff.length); 98 while(i < len){ 99 auto pevent = cast(inotify_event*)&buff[i]; 100 auto watcher = staticWatchers[pevent.wd]; 101 watcher.event( 102 watcher.directory, 103 (cast(char*)&pevent.name).to!string, 104 pevent.mask & IN_CLOSE_WRITE ? Modify 105 : pevent.mask & (IN_MOVED_TO | IN_CREATE) ? Add 106 : pevent.mask & (IN_MOVED_FROM | IN_DELETE) ? Remove 107 : -1); 108 i += inotify_event.sizeof + pevent.len; 109 } 110 } 111 112 } 113 114 } 115