2
2
#include <string.h>
3
3
#ifdef HAS_INOTIFY
4
4
#include <sys/inotify.h>
5
+ #elif HAS_KQUEUE
6
+ #include <sys/event.h>
7
+ #include <sys/types.h>
8
+ #undef EV_ERROR // Avoid clashing with libev's EV_ERROR
9
+ #include <fcntl.h> // For O_RDONLY
10
+ #include <sys/time.h> // For struct timespec
11
+ #include <unistd.h> // For open
5
12
#endif
6
13
7
14
#include <ev.h>
@@ -26,7 +33,7 @@ struct file_watch_registry {
26
33
struct watched_file * reg ;
27
34
};
28
35
29
- static void file_watch_ev_cb (EV_P_ struct ev_io * w , int revent attr_unused ) {
36
+ static void file_watch_ev_cb (EV_P attr_unused , struct ev_io * w , int revent attr_unused ) {
30
37
auto fwr = (struct file_watch_registry * )w ;
31
38
32
39
while (true) {
@@ -42,6 +49,19 @@ static void file_watch_ev_cb(EV_P_ struct ev_io *w, int revent attr_unused) {
42
49
break ;
43
50
}
44
51
wd = inotify_event .wd ;
52
+ #elif HAS_KQUEUE
53
+ struct kevent ev ;
54
+ struct timespec timeout = {0 };
55
+ int ret = kevent (fwr -> w .fd , NULL , 0 , & ev , 1 , & timeout );
56
+ if (ret <= 0 ) {
57
+ if (ret < 0 ) {
58
+ log_error ("Failed to get kevent: %s" , strerror (errno ));
59
+ }
60
+ break ;
61
+ }
62
+ wd = (int )ev .ident ;
63
+ #else
64
+ assert (false);
45
65
#endif
46
66
47
67
struct watched_file * wf = NULL ;
@@ -63,6 +83,12 @@ void *file_watch_init(EV_P) {
63
83
log_error ("inotify_init1 failed: %s" , strerror (errno ));
64
84
return NULL ;
65
85
}
86
+ #elif HAS_KQUEUE
87
+ fd = kqueue ();
88
+ if (fd < 0 ) {
89
+ log_error ("Failed to create kqueue: %s" , strerror (errno ));
90
+ return NULL ;
91
+ }
66
92
#else
67
93
log_info ("No file watching support found on the host system." );
68
94
return NULL ;
@@ -81,6 +107,12 @@ void file_watch_destroy(EV_P_ void *_fwr) {
81
107
82
108
HASH_ITER (hh , fwr -> reg , i , tmp ) {
83
109
HASH_DEL (fwr -> reg , i );
110
+ #ifdef HAS_KQUEUE
111
+ // kqueue watch descriptors are file descriptors of
112
+ // the files we are watching, so we need to close
113
+ // them
114
+ close (i -> wd );
115
+ #endif
84
116
free (i );
85
117
}
86
118
@@ -100,7 +132,39 @@ bool file_watch_add(void *_fwr, const char *filename, file_watch_cb_t cb, void *
100
132
log_error ("Failed to watch file \"%s\": %s" , filename , strerror (errno ));
101
133
return false;
102
134
}
135
+ #elif HAS_KQUEUE
136
+ wd = open (filename , O_RDONLY );
137
+ if (wd < 0 ) {
138
+ log_error ("Cannot open file \"%s\" for watching: %s" , filename ,
139
+ strerror (errno ));
140
+ return false;
141
+ }
142
+
143
+ uint32_t fflags = NOTE_DELETE | NOTE_RENAME | NOTE_REVOKE | NOTE_ATTRIB ;
144
+ // NOTE_CLOSE_WRITE is relatively new, so we cannot just use it
145
+ #ifdef NOTE_CLOSE_WRITE
146
+ fflags |= NOTE_CLOSE_WRITE ;
147
+ #else
148
+ // NOTE_WRITE will receive notification more frequent than necessary, so is less
149
+ // preferrable
150
+ fflags |= NOTE_WRITE ;
103
151
#endif
152
+ struct kevent ev = {
153
+ .ident = (unsigned int )wd , // the wd < 0 case is checked above
154
+ .filter = EVFILT_VNODE ,
155
+ .flags = EV_ADD | EV_CLEAR ,
156
+ .fflags = fflags ,
157
+ .data = 0 ,
158
+ .udata = NULL ,
159
+ };
160
+ if (kevent (fwr -> w .fd , & ev , 1 , NULL , 0 , NULL ) < 0 ) {
161
+ log_error ("Failed to register kevent: %s" , strerror (errno ));
162
+ close (wd );
163
+ return false;
164
+ }
165
+ #else
166
+ assert (false);
167
+ #endif // HAS_KQUEUE
104
168
105
169
auto w = ccalloc (1 , struct watched_file );
106
170
w -> wd = wd ;
0 commit comments