aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndreas Politz2017-03-26 09:21:56 +0200
committerMichael Albinus2017-03-26 09:21:56 +0200
commit158bb8555dfefa50f6118be6794d0424cc52d291 (patch)
treee55be569b5898834d9a1e8b25586b1ddeb597587 /src
parent9278d904af13c3c083defdcbf5fa21260d4457c3 (diff)
downloademacs-158bb8555dfefa50f6118be6794d0424cc52d291.tar.gz
emacs-158bb8555dfefa50f6118be6794d0424cc52d291.zip
Fix issues regarding inotify file-notification
Remove special code handling the inotify back-end. * lisp/filenotify.el (file-notify--watch): New struct representing a file-watch. (file-notify-descriptors): Use the new struct as hash-value. (file-notify-handle-event): Check that event is a cons. (file-notify--rm-descriptor, file-notify--event-watched-file) (file-notify--event-file-name, file-notify--event-file1-name) (file-notify-callback, file-notify-add-watch) (file-notify-rm-watch, file-notify-valid-p): Use new struct. Remove special code handling inotify descriptors. Remove code handling multiple clients per descriptor. (file-notify--descriptor): Remove unused function. Let inotify-add-watch return a unique descriptor on every call, like every other back-end does (Bug#26126). Prevent multiple clients from interfering with each other, when watching a shared descriptor. * src/inotify.c (watch_list): Extend the format by including a id and the provided mask. (INOTIFY_DEFAULT_MASK): Default mask used for all clients. (make_watch_descriptor): Removed. (make_lispy_mask, lispy_mask_match_p): New functions. (inotifyevent_to_event): Match event against the mask provided by the client. (add_watch, remove_descriptor, remove_watch): New functions for managing the watch_list. (inotify_callback): Use the new functions. (Finotify_add_watch, Finotify_rm_watch): Remove deprecated flags from documentation. Add check for validity of provided descriptor. Use the new functions. Use the default mask. (INOTIFY_DEBUG): Add new debug conditional. (inotify-watch-list, inotify-allocated-p): New debug functions. (symbol_to_inotifymask, syms_of_inotify): Remove deprecated symbols. * test/lisp/filenotify-tests.el: (file-notify-test02-rm-watch): Remove expected failure for inotify.
Diffstat (limited to 'src')
-rw-r--r--src/inotify.c374
1 files changed, 235 insertions, 139 deletions
diff --git a/src/inotify.c b/src/inotify.c
index 61ef6153286..a084552adcb 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -41,23 +41,30 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
41#ifndef IN_ONLYDIR 41#ifndef IN_ONLYDIR
42# define IN_ONLYDIR 0 42# define IN_ONLYDIR 0
43#endif 43#endif
44#define INOTIFY_DEFAULT_MASK (IN_ALL_EVENTS|IN_EXCL_UNLINK)
44 45
45/* File handle for inotify. */ 46/* File handle for inotify. */
46static int inotifyfd = -1; 47static int inotifyfd = -1;
47 48
48/* Assoc list of files being watched. 49/* Alist of files being watched. We want the returned descriptor to
49 Format: (watch-descriptor name callback) 50 be unique for every watch, but inotify returns the same descriptor
51 for multiple calls to inotify_add_watch with the same file. In
52 order to solve this problem, we add a ID, uniquely identifying a
53 watch/file combination.
54
55 For the same reason, we also need to store the watch's mask and we
56 can't allow the following flags to be used.
57
58 IN_EXCL_UNLINK
59 IN_MASK_ADD
60 IN_ONESHOT
61 IN_ONLYDIR
62
63 Format: (descriptor . ((id filename callback mask) ...))
50 */ 64 */
51static Lisp_Object watch_list; 65static Lisp_Object watch_list;
52 66
53static Lisp_Object 67static Lisp_Object
54make_watch_descriptor (int wd)
55{
56 /* TODO replace this with a Misc Object! */
57 return make_number (wd);
58}
59
60static Lisp_Object
61mask_to_aspects (uint32_t mask) { 68mask_to_aspects (uint32_t mask) {
62 Lisp_Object aspects = Qnil; 69 Lisp_Object aspects = Qnil;
63 if (mask & IN_ACCESS) 70 if (mask & IN_ACCESS)
@@ -95,77 +102,6 @@ mask_to_aspects (uint32_t mask) {
95 return aspects; 102 return aspects;
96} 103}
97 104
98static Lisp_Object
99inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
100{
101 Lisp_Object name = Qnil;
102 if (ev->len > 0)
103 {
104 size_t const len = strlen (ev->name);
105 name = make_unibyte_string (ev->name, min (len, ev->len));
106 name = DECODE_FILE (name);
107 }
108 else
109 name = XCAR (XCDR (watch_object));
110
111 return list2 (list4 (make_watch_descriptor (ev->wd),
112 mask_to_aspects (ev->mask),
113 name,
114 make_number (ev->cookie)),
115 Fnth (make_number (2), watch_object));
116}
117
118/* This callback is called when the FD is available for read. The inotify
119 events are read from FD and converted into input_events. */
120static void
121inotify_callback (int fd, void *_)
122{
123 struct input_event event;
124 Lisp_Object watch_object;
125 int to_read;
126 char *buffer;
127 ssize_t n;
128 size_t i;
129
130 to_read = 0;
131 if (ioctl (fd, FIONREAD, &to_read) == -1)
132 report_file_notify_error ("Error while retrieving file system events",
133 Qnil);
134 buffer = xmalloc (to_read);
135 n = read (fd, buffer, to_read);
136 if (n < 0)
137 {
138 xfree (buffer);
139 report_file_notify_error ("Error while reading file system events", Qnil);
140 }
141
142 EVENT_INIT (event);
143 event.kind = FILE_NOTIFY_EVENT;
144
145 i = 0;
146 while (i < (size_t)n)
147 {
148 struct inotify_event *ev = (struct inotify_event *) &buffer[i];
149
150 watch_object = Fassoc (make_watch_descriptor (ev->wd), watch_list);
151 if (!NILP (watch_object))
152 {
153 event.arg = inotifyevent_to_event (watch_object, ev);
154
155 /* If event was removed automatically: Drop it from watch list. */
156 if (ev->mask & IN_IGNORED)
157 watch_list = Fdelete (watch_object, watch_list);
158
159 if (!NILP (event.arg))
160 kbd_buffer_store_event (&event);
161 }
162
163 i += sizeof (*ev) + ev->len;
164 }
165
166 xfree (buffer);
167}
168
169static uint32_t 105static uint32_t
170symbol_to_inotifymask (Lisp_Object symb) 106symbol_to_inotifymask (Lisp_Object symb)
171{ 107{
@@ -200,14 +136,6 @@ symbol_to_inotifymask (Lisp_Object symb)
200 136
201 else if (EQ (symb, Qdont_follow)) 137 else if (EQ (symb, Qdont_follow))
202 return IN_DONT_FOLLOW; 138 return IN_DONT_FOLLOW;
203 else if (EQ (symb, Qexcl_unlink))
204 return IN_EXCL_UNLINK;
205 else if (EQ (symb, Qmask_add))
206 return IN_MASK_ADD;
207 else if (EQ (symb, Qoneshot))
208 return IN_ONESHOT;
209 else if (EQ (symb, Qonlydir))
210 return IN_ONLYDIR;
211 139
212 else if (EQ (symb, Qt) || EQ (symb, Qall_events)) 140 else if (EQ (symb, Qt) || EQ (symb, Qall_events))
213 return IN_ALL_EVENTS; 141 return IN_ALL_EVENTS;
@@ -236,6 +164,174 @@ aspect_to_inotifymask (Lisp_Object aspect)
236 return symbol_to_inotifymask (aspect); 164 return symbol_to_inotifymask (aspect);
237} 165}
238 166
167static Lisp_Object
168make_lispy_mask (uint32_t mask)
169{
170 return Fcons (make_number (mask & 0xffff),
171 make_number (mask >> 16));
172}
173
174static bool
175lispy_mask_match_p (Lisp_Object mask, uint32_t other)
176{
177 return (XINT (XCAR (mask)) & other)
178 || ((XINT (XCDR (mask)) << 16) & other);
179}
180
181static Lisp_Object
182inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev)
183{
184 Lisp_Object name = Qnil;
185
186 if (! lispy_mask_match_p (Fnth (make_number (3), watch), ev->mask))
187 return Qnil;
188
189 if (ev->len > 0)
190 {
191 size_t const len = strlen (ev->name);
192 name = make_unibyte_string (ev->name, min (len, ev->len));
193 name = DECODE_FILE (name);
194 }
195 else
196 name = XCAR (XCDR (watch));
197
198 return list2 (list4 (Fcons (make_number (ev->wd), XCAR (watch)),
199 mask_to_aspects (ev->mask),
200 name,
201 make_number (ev->cookie)),
202 Fnth (make_number (2), watch));
203}
204
205/* Add a new watch to watch-descriptor WD watching FILENAME and using
206 CALLBACK. Returns a cons (DESCRIPTOR . ID) uniquely identifying the
207 new watch. */
208static Lisp_Object
209add_watch (int wd, Lisp_Object filename, Lisp_Object aspect, Lisp_Object callback)
210{
211 Lisp_Object descriptor = make_number (wd);
212 Lisp_Object elt = Fassoc (descriptor, watch_list);
213 Lisp_Object watches = Fcdr (elt);
214 Lisp_Object watch, watch_id;
215 Lisp_Object mask = make_lispy_mask (aspect_to_inotifymask (aspect));
216
217 int id = 0;
218
219 while (! NILP (watches))
220 {
221 id = max (id, 1 + XINT (XCAR (XCAR (watches))));
222 watches = XCDR (watches);
223 }
224
225 watch_id = make_number (id);
226 watch = list4 (watch_id, filename, callback, mask);
227
228 if (NILP (elt))
229 watch_list = Fcons (Fcons (descriptor, Fcons (watch, Qnil)),
230 watch_list);
231 else
232 XSETCDR (elt, Fcons (watch, XCDR (elt)));
233
234 return Fcons (descriptor, watch_id);
235}
236
237/* Remove all watches associated with descriptor. If INVALID_P is
238 true, the descriptor is already invalid, i.e. it received a
239 IN_IGNORED event. In this case skip calling inotify_rm_watch. */
240static void
241remove_descriptor (Lisp_Object descriptor, bool invalid_p)
242{
243 Lisp_Object elt = Fassoc (descriptor, watch_list);
244
245 if (! NILP (elt))
246 {
247 int wd = XINT (descriptor);
248
249 watch_list = Fdelete (elt, watch_list);
250 if (! invalid_p)
251 if (inotify_rm_watch (inotifyfd, wd) == -1)
252 report_file_notify_error ("Could not rm watch", descriptor);
253 }
254 /* Cleanup if no more files are watched. */
255 if (NILP (watch_list))
256 {
257 emacs_close (inotifyfd);
258 delete_read_fd (inotifyfd);
259 inotifyfd = -1;
260 }
261}
262
263/* Remove watch associated with (descriptor, id). */
264static void
265remove_watch (Lisp_Object descriptor, Lisp_Object id)
266{
267 Lisp_Object elt = Fassoc (descriptor, watch_list);
268
269 if (! NILP (elt))
270 {
271 Lisp_Object watch = Fassoc (id, XCDR (elt));
272
273 if (! NILP (watch))
274 XSETCDR (elt, Fdelete (watch, XCDR (elt)));
275
276 /* Remove the descriptor if noone is watching it. */
277 if (NILP (XCDR (elt)))
278 remove_descriptor (descriptor, false);
279 }
280}
281
282/* This callback is called when the FD is available for read. The inotify
283 events are read from FD and converted into input_events. */
284static void
285inotify_callback (int fd, void *_)
286{
287 struct input_event event;
288 int to_read;
289 char *buffer;
290 ssize_t n;
291 size_t i;
292
293 to_read = 0;
294 if (ioctl (fd, FIONREAD, &to_read) == -1)
295 report_file_notify_error ("Error while retrieving file system events",
296 Qnil);
297 buffer = xmalloc (to_read);
298 n = read (fd, buffer, to_read);
299 if (n < 0)
300 {
301 xfree (buffer);
302 report_file_notify_error ("Error while reading file system events", Qnil);
303 }
304
305 EVENT_INIT (event);
306 event.kind = FILE_NOTIFY_EVENT;
307
308 i = 0;
309 while (i < (size_t)n)
310 {
311 struct inotify_event *ev = (struct inotify_event *) &buffer[i];
312 Lisp_Object descriptor = make_number (ev->wd);
313 Lisp_Object elt = Fassoc (descriptor, watch_list);
314
315 if (! NILP (elt))
316 {
317 Lisp_Object watches = XCDR (elt);
318 while (! NILP (watches))
319 {
320 event.arg = inotifyevent_to_event (XCAR (watches), ev);
321 if (!NILP (event.arg))
322 kbd_buffer_store_event (&event);
323 watches = XCDR (watches);
324 }
325 /* If event was removed automatically: Drop it from watch list. */
326 if (ev->mask & IN_IGNORED)
327 remove_descriptor (descriptor, true);
328 }
329 i += sizeof (*ev) + ev->len;
330 }
331
332 xfree (buffer);
333}
334
239DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0, 335DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
240 doc: /* Add a watch for FILE-NAME to inotify. 336 doc: /* Add a watch for FILE-NAME to inotify.
241 337
@@ -264,10 +360,6 @@ close
264The following symbols can also be added to a list of aspects: 360The following symbols can also be added to a list of aspects:
265 361
266dont-follow 362dont-follow
267excl-unlink
268mask-add
269oneshot
270onlydir
271 363
272Watching a directory is not recursive. CALLBACK is passed a single argument 364Watching a directory is not recursive. CALLBACK is passed a single argument
273EVENT which contains an event structure of the format 365EVENT which contains an event structure of the format
@@ -286,22 +378,22 @@ unmount
286 378
287If a directory is watched then NAME is the name of file that caused the event. 379If a directory is watched then NAME is the name of file that caused the event.
288 380
289COOKIE is an object that can be compared using `equal' to identify two matching 381COOKIE is an object that can be compared using `equal' to identify two matchingt
290renames (moved-from and moved-to). 382renames (moved-from and moved-to).
291 383
292See inotify(7) and inotify_add_watch(2) for further information. The inotify fd 384See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
293is managed internally and there is no corresponding inotify_init. Use 385is managed internally and there is no corresponding inotify_init. Use
294`inotify-rm-watch' to remove a watch. 386`inotify-rm-watch' to remove a watch.
295 */) 387 */)
296 (Lisp_Object file_name, Lisp_Object aspect, Lisp_Object callback) 388 (Lisp_Object filename, Lisp_Object aspect, Lisp_Object callback)
297{ 389{
298 uint32_t mask;
299 Lisp_Object watch_object;
300 Lisp_Object encoded_file_name; 390 Lisp_Object encoded_file_name;
301 Lisp_Object watch_descriptor; 391 bool dont_follow = ! NILP (Fmemq (Qdont_follow, aspect));
302 int watchdesc = -1; 392 int wd = -1;
393 uint32_t mask = (INOTIFY_DEFAULT_MASK
394 | (dont_follow ? IN_DONT_FOLLOW : 0));
303 395
304 CHECK_STRING (file_name); 396 CHECK_STRING (filename);
305 397
306 if (inotifyfd < 0) 398 if (inotifyfd < 0)
307 { 399 {
@@ -312,24 +404,12 @@ is managed internally and there is no corresponding inotify_init. Use
312 add_read_fd (inotifyfd, &inotify_callback, NULL); 404 add_read_fd (inotifyfd, &inotify_callback, NULL);
313 } 405 }
314 406
315 mask = aspect_to_inotifymask (aspect); 407 encoded_file_name = ENCODE_FILE (filename);
316 encoded_file_name = ENCODE_FILE (file_name); 408 wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
317 watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask); 409 if (wd == -1)
318 if (watchdesc == -1) 410 report_file_notify_error ("Could not add watch for file", filename);
319 report_file_notify_error ("Could not add watch for file", file_name);
320
321 watch_descriptor = make_watch_descriptor (watchdesc);
322 411
323 /* Delete existing watch object. */ 412 return add_watch (wd, filename, aspect, callback);
324 watch_object = Fassoc (watch_descriptor, watch_list);
325 if (!NILP (watch_object))
326 watch_list = Fdelete (watch_object, watch_list);
327
328 /* Store watch object in watch list. */
329 watch_object = list3 (watch_descriptor, encoded_file_name, callback);
330 watch_list = Fcons (watch_object, watch_list);
331
332 return watch_descriptor;
333} 413}
334 414
335DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0, 415DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
@@ -338,27 +418,20 @@ DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
338WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'. 418WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
339 419
340See inotify_rm_watch(2) for more information. 420See inotify_rm_watch(2) for more information.
341 */) 421 */)
342 (Lisp_Object watch_descriptor) 422 (Lisp_Object watch_descriptor)
343{ 423{
344 Lisp_Object watch_object;
345 int wd = XINT (watch_descriptor);
346 424
347 if (inotify_rm_watch (inotifyfd, wd) == -1) 425 Lisp_Object descriptor, id;
348 report_file_notify_error ("Could not rm watch", watch_descriptor);
349 426
350 /* Remove watch descriptor from watch list. */ 427 if (! (CONSP (watch_descriptor)
351 watch_object = Fassoc (watch_descriptor, watch_list); 428 && INTEGERP (XCAR (watch_descriptor))
352 if (!NILP (watch_object)) 429 && INTEGERP (XCDR (watch_descriptor))))
353 watch_list = Fdelete (watch_object, watch_list); 430 report_file_notify_error ("Invalid descriptor ", watch_descriptor);
354 431
355 /* Cleanup if no more files are watched. */ 432 descriptor = XCAR (watch_descriptor);
356 if (NILP (watch_list)) 433 id = XCDR (watch_descriptor);
357 { 434 remove_watch (descriptor, id);
358 emacs_close (inotifyfd);
359 delete_read_fd (inotifyfd);
360 inotifyfd = -1;
361 }
362 435
363 return Qt; 436 return Qt;
364} 437}
@@ -374,10 +447,33 @@ reason. Removing the watch by calling `inotify-rm-watch' also makes
374it invalid. */) 447it invalid. */)
375 (Lisp_Object watch_descriptor) 448 (Lisp_Object watch_descriptor)
376{ 449{
377 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list); 450 Lisp_Object elt, watch;
378 return NILP (watch_object) ? Qnil : Qt; 451
452 if (! (CONSP (watch_descriptor)
453 && INTEGERP (XCAR (watch_descriptor))
454 && INTEGERP (XCDR (watch_descriptor))))
455 return Qnil;
456
457 elt = Fassoc (XCAR (watch_descriptor), watch_list);
458 watch = Fassoc (XCDR (watch_descriptor), XCDR (elt));
459
460 return ! NILP (watch) ? Qt : Qnil;
461}
462
463#ifdef INOTIFY_DEBUG
464DEFUN ("inotify-watch-list", Finotify_watch_list, Sinotify_watch_list, 0, 0, 0,
465 doc: /* Return a copy of the internal watch_list. */)
466{
467 return Fcopy_sequence (watch_list);
379} 468}
380 469
470DEFUN ("inotify-allocated-p", Finotify_allocated_p, Sinotify_allocated_p, 0, 0, 0,
471 doc: /* Return non-nil, if a inotify instance is allocated. */)
472{
473 return inotifyfd < 0 ? Qnil : Qt;
474}
475#endif
476
381void 477void
382syms_of_inotify (void) 478syms_of_inotify (void)
383{ 479{
@@ -400,10 +496,6 @@ syms_of_inotify (void)
400 DEFSYM (Qclose, "close"); /* IN_CLOSE */ 496 DEFSYM (Qclose, "close"); /* IN_CLOSE */
401 497
402 DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */ 498 DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */
403 DEFSYM (Qexcl_unlink, "excl-unlink"); /* IN_EXCL_UNLINK */
404 DEFSYM (Qmask_add, "mask-add"); /* IN_MASK_ADD */
405 DEFSYM (Qoneshot, "oneshot"); /* IN_ONESHOT */
406 DEFSYM (Qonlydir, "onlydir"); /* IN_ONLYDIR */
407 499
408 DEFSYM (Qignored, "ignored"); /* IN_IGNORED */ 500 DEFSYM (Qignored, "ignored"); /* IN_IGNORED */
409 DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */ 501 DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */
@@ -414,6 +506,10 @@ syms_of_inotify (void)
414 defsubr (&Sinotify_rm_watch); 506 defsubr (&Sinotify_rm_watch);
415 defsubr (&Sinotify_valid_p); 507 defsubr (&Sinotify_valid_p);
416 508
509#ifdef INOTIFY_DEBUG
510 defsubr (&Sinotify_watch_list);
511 defsubr (&Sinotify_allocated_p);
512#endif
417 staticpro (&watch_list); 513 staticpro (&watch_list);
418 514
419 Fprovide (intern_c_string ("inotify"), Qnil); 515 Fprovide (intern_c_string ("inotify"), Qnil);