Improve responsiveness while in 'replace-buffer-contents'
[emacs.git] / src / inotify.c
blobe06cc97c6a719543ca50774c3e253204a3ceae88
1 /* Inotify support for Emacs
3 Copyright (C) 2012-2018 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #ifdef HAVE_INOTIFY
24 #include "lisp.h"
25 #include "coding.h"
26 #include "process.h"
27 #include "keyboard.h"
28 #include "termhooks.h"
30 #include <errno.h>
31 #include <sys/inotify.h>
32 #include <sys/ioctl.h>
34 /* Ignore bits that might be undefined on old GNU/Linux systems. */
35 #ifndef IN_EXCL_UNLINK
36 # define IN_EXCL_UNLINK 0
37 #endif
38 #ifndef IN_DONT_FOLLOW
39 # define IN_DONT_FOLLOW 0
40 #endif
41 #ifndef IN_ONLYDIR
42 # define IN_ONLYDIR 0
43 #endif
45 /* File handle for inotify. */
46 static int inotifyfd = -1;
48 /* Alist of files being watched. We want the returned descriptor to
49 be unique for every watch, but inotify returns the same descriptor
50 WD for multiple calls to inotify_add_watch with the same file.
51 Supply a nonnegative integer ID, so that WD and ID together
52 uniquely identify a watch/file combination.
54 For the same reason, we also need to store the watch's mask and we
55 can't allow the following flags to be used.
57 IN_EXCL_UNLINK
58 IN_MASK_ADD
59 IN_ONESHOT
61 Each element of this list is of the form (DESCRIPTOR . WATCHES)
62 where no two DESCRIPTOR values are the same. DESCRIPTOR represents
63 the inotify watch descriptor and WATCHES is a list with elements of
64 the form (ID FILENAME CALLBACK MASK), where ID is the integer
65 described above, FILENAME names the file being watched, CALLBACK is
66 invoked when the event occurs, and MASK represents the aspects
67 being watched. The WATCHES list is sorted by ID. Although
68 DESCRIPTOR and MASK are ordinarily integers, they are conses when
69 representing integers outside of fixnum range. */
71 static Lisp_Object watch_list;
73 static Lisp_Object
74 mask_to_aspects (uint32_t mask)
76 Lisp_Object aspects = Qnil;
77 if (mask & IN_ACCESS)
78 aspects = Fcons (Qaccess, aspects);
79 if (mask & IN_ATTRIB)
80 aspects = Fcons (Qattrib, aspects);
81 if (mask & IN_CLOSE_WRITE)
82 aspects = Fcons (Qclose_write, aspects);
83 if (mask & IN_CLOSE_NOWRITE)
84 aspects = Fcons (Qclose_nowrite, aspects);
85 if (mask & IN_CREATE)
86 aspects = Fcons (Qcreate, aspects);
87 if (mask & IN_DELETE)
88 aspects = Fcons (Qdelete, aspects);
89 if (mask & IN_DELETE_SELF)
90 aspects = Fcons (Qdelete_self, aspects);
91 if (mask & IN_MODIFY)
92 aspects = Fcons (Qmodify, aspects);
93 if (mask & IN_MOVE_SELF)
94 aspects = Fcons (Qmove_self, aspects);
95 if (mask & IN_MOVED_FROM)
96 aspects = Fcons (Qmoved_from, aspects);
97 if (mask & IN_MOVED_TO)
98 aspects = Fcons (Qmoved_to, aspects);
99 if (mask & IN_OPEN)
100 aspects = Fcons (Qopen, aspects);
101 if (mask & IN_IGNORED)
102 aspects = Fcons (Qignored, aspects);
103 if (mask & IN_ISDIR)
104 aspects = Fcons (Qisdir, aspects);
105 if (mask & IN_Q_OVERFLOW)
106 aspects = Fcons (Qq_overflow, aspects);
107 if (mask & IN_UNMOUNT)
108 aspects = Fcons (Qunmount, aspects);
109 return aspects;
112 static uint32_t
113 symbol_to_inotifymask (Lisp_Object symb)
115 if (EQ (symb, Qaccess))
116 return IN_ACCESS;
117 else if (EQ (symb, Qattrib))
118 return IN_ATTRIB;
119 else if (EQ (symb, Qclose_write))
120 return IN_CLOSE_WRITE;
121 else if (EQ (symb, Qclose_nowrite))
122 return IN_CLOSE_NOWRITE;
123 else if (EQ (symb, Qcreate))
124 return IN_CREATE;
125 else if (EQ (symb, Qdelete))
126 return IN_DELETE;
127 else if (EQ (symb, Qdelete_self))
128 return IN_DELETE_SELF;
129 else if (EQ (symb, Qmodify))
130 return IN_MODIFY;
131 else if (EQ (symb, Qmove_self))
132 return IN_MOVE_SELF;
133 else if (EQ (symb, Qmoved_from))
134 return IN_MOVED_FROM;
135 else if (EQ (symb, Qmoved_to))
136 return IN_MOVED_TO;
137 else if (EQ (symb, Qopen))
138 return IN_OPEN;
139 else if (EQ (symb, Qmove))
140 return IN_MOVE;
141 else if (EQ (symb, Qclose))
142 return IN_CLOSE;
144 else if (EQ (symb, Qdont_follow))
145 return IN_DONT_FOLLOW;
146 else if (EQ (symb, Qonlydir))
147 return IN_ONLYDIR;
149 else if (EQ (symb, Qt) || EQ (symb, Qall_events))
150 return IN_ALL_EVENTS;
151 else
153 errno = EINVAL;
154 report_file_notify_error ("Unknown aspect", symb);
158 static uint32_t
159 aspect_to_inotifymask (Lisp_Object aspect)
161 if (CONSP (aspect) || NILP (aspect))
163 Lisp_Object x = aspect;
164 uint32_t mask = 0;
165 FOR_EACH_TAIL (x)
166 mask |= symbol_to_inotifymask (XCAR (x));
167 CHECK_LIST_END (x, aspect);
168 return mask;
170 else
171 return symbol_to_inotifymask (aspect);
174 static Lisp_Object
175 inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev)
177 Lisp_Object name;
178 uint32_t mask;
179 CONS_TO_INTEGER (Fnth (make_number (3), watch), uint32_t, mask);
181 if (! (mask & ev->mask))
182 return Qnil;
184 if (ev->len > 0)
186 size_t const len = strlen (ev->name);
187 name = make_unibyte_string (ev->name, min (len, ev->len));
188 name = DECODE_FILE (name);
190 else
191 name = XCAR (XCDR (watch));
193 return list2 (list4 (Fcons (INTEGER_TO_CONS (ev->wd), XCAR (watch)),
194 mask_to_aspects (ev->mask),
195 name,
196 INTEGER_TO_CONS (ev->cookie)),
197 Fnth (make_number (2), watch));
200 /* Add a new watch to watch-descriptor WD watching FILENAME and using
201 IMASK and CALLBACK. Return a cons (DESCRIPTOR . ID) uniquely
202 identifying the new watch. */
203 static Lisp_Object
204 add_watch (int wd, Lisp_Object filename,
205 uint32_t imask, Lisp_Object callback)
207 Lisp_Object descriptor = INTEGER_TO_CONS (wd);
208 Lisp_Object tail = assoc_no_quit (descriptor, watch_list);
209 Lisp_Object watch, watch_id;
210 Lisp_Object mask = INTEGER_TO_CONS (imask);
212 EMACS_INT id = 0;
213 if (NILP (tail))
215 tail = list1 (descriptor);
216 watch_list = Fcons (tail, watch_list);
218 else
220 /* Assign a watch ID that is not already in use, by looking
221 for a gap in the existing sorted list. */
222 for (; ! NILP (XCDR (tail)); tail = XCDR (tail), id++)
223 if (!EQ (XCAR (XCAR (XCDR (tail))), make_number (id)))
224 break;
225 if (MOST_POSITIVE_FIXNUM < id)
226 emacs_abort ();
229 /* Insert the newly-assigned ID into the previously-discovered gap,
230 which is possibly at the end of the list. Inserting it there
231 keeps the list sorted. */
232 watch_id = make_number (id);
233 watch = list4 (watch_id, filename, callback, mask);
234 XSETCDR (tail, Fcons (watch, XCDR (tail)));
236 return Fcons (descriptor, watch_id);
239 /* Find the watch list element (if any) matching DESCRIPTOR. Return
240 nil if not found. If found, return t if the first element matches
241 DESCRIPTOR; otherwise, return the cons whose cdr matches
242 DESCRIPTOR. This lets the caller easily remove the element
243 matching DESCRIPTOR without having to search for it again, and
244 without calling Fdelete (which might quit). */
246 static Lisp_Object
247 find_descriptor (Lisp_Object descriptor)
249 Lisp_Object tail, prevtail = Qt;
250 for (tail = watch_list; !NILP (tail); prevtail = tail, tail = XCDR (tail))
251 if (equal_no_quit (XCAR (XCAR (tail)), descriptor))
252 return prevtail;
253 return Qnil;
256 /* Remove all watches associated with the watch list element after
257 PREVTAIL, or after the first element if PREVTAIL is t. If INVALID_P
258 is true, the descriptor is already invalid, i.e., it received a
259 IN_IGNORED event. In this case skip calling inotify_rm_watch. */
260 static void
261 remove_descriptor (Lisp_Object prevtail, bool invalid_p)
263 Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list;
265 int inotify_errno = 0;
266 if (! invalid_p)
268 int wd;
269 CONS_TO_INTEGER (XCAR (XCAR (tail)), int, wd);
270 if (inotify_rm_watch (inotifyfd, wd) != 0)
271 inotify_errno = errno;
274 if (CONSP (prevtail))
275 XSETCDR (prevtail, XCDR (tail));
276 else
278 watch_list = XCDR (tail);
279 if (NILP (watch_list))
281 delete_read_fd (inotifyfd);
282 emacs_close (inotifyfd);
283 inotifyfd = -1;
287 if (inotify_errno != 0)
289 errno = inotify_errno;
290 report_file_notify_error ("Could not rm watch", XCAR (tail));
294 /* Remove watch associated with (descriptor, id). */
295 static void
296 remove_watch (Lisp_Object descriptor, Lisp_Object id)
298 Lisp_Object prevtail = find_descriptor (descriptor);
299 if (NILP (prevtail))
300 return;
302 Lisp_Object elt = XCAR (CONSP (prevtail) ? XCDR (prevtail) : watch_list);
303 for (Lisp_Object prev = elt; !NILP (XCDR (prev)); prev = XCDR (prev))
304 if (EQ (id, XCAR (XCAR (XCDR (prev)))))
306 XSETCDR (prev, XCDR (XCDR (prev)));
307 if (NILP (XCDR (elt)))
308 remove_descriptor (prevtail, false);
309 break;
313 /* This callback is called when the FD is available for read. The inotify
314 events are read from FD and converted into input_events. */
315 static void
316 inotify_callback (int fd, void *_)
318 int to_read;
319 if (ioctl (fd, FIONREAD, &to_read) < 0)
320 report_file_notify_error ("Error while retrieving file system events",
321 Qnil);
322 USE_SAFE_ALLOCA;
323 char *buffer = SAFE_ALLOCA (to_read);
324 ssize_t n = read (fd, buffer, to_read);
325 if (n < 0)
326 report_file_notify_error ("Error while reading file system events", Qnil);
328 struct input_event event;
329 EVENT_INIT (event);
330 event.kind = FILE_NOTIFY_EVENT;
332 for (ssize_t i = 0; i < n; )
334 struct inotify_event *ev = (struct inotify_event *) &buffer[i];
335 Lisp_Object descriptor = INTEGER_TO_CONS (ev->wd);
336 Lisp_Object prevtail = find_descriptor (descriptor);
338 if (! NILP (prevtail))
340 Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list;
341 for (Lisp_Object watches = XCDR (XCAR (tail)); ! NILP (watches);
342 watches = XCDR (watches))
344 event.arg = inotifyevent_to_event (XCAR (watches), ev);
345 if (!NILP (event.arg))
346 kbd_buffer_store_event (&event);
348 /* If event was removed automatically: Drop it from watch list. */
349 if (ev->mask & IN_IGNORED)
350 remove_descriptor (prevtail, true);
352 i += sizeof (*ev) + ev->len;
355 SAFE_FREE ();
358 DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
359 doc: /* Add a watch for FILE-NAME to inotify.
361 Return a watch descriptor. The watch will look for ASPECT events and
362 invoke CALLBACK when an event occurs.
364 ASPECT might be one of the following symbols or a list of those symbols:
366 access
367 attrib
368 close-write
369 close-nowrite
370 create
371 delete
372 delete-self
373 modify
374 move-self
375 moved-from
376 moved-to
377 open
379 all-events or t
380 move
381 close
383 ASPECT can also contain the following symbols, which control whether
384 the watch descriptor will be created:
386 dont-follow
387 onlydir
389 Watching a directory is not recursive. CALLBACK is passed a single argument
390 EVENT which contains an event structure of the format
392 \(WATCH-DESCRIPTOR ASPECTS NAME COOKIE)
394 WATCH-DESCRIPTOR is the same object that was returned by this function. It can
395 be tested for equality using `equal'. ASPECTS describes the event. It is a
396 list of ASPECT symbols described above and can also contain one of the following
397 symbols
399 ignored
400 isdir
401 q-overflow
402 unmount
404 If a directory is watched then NAME is the name of file that caused the event.
406 COOKIE is an object that can be compared using `equal' to identify two matching
407 renames (moved-from and moved-to).
409 See inotify(7) and inotify_add_watch(2) for further information. The
410 inotify fd is managed internally and there is no corresponding
411 inotify_init. Use `inotify-rm-watch' to remove a watch.
413 The following inotify bit-masks cannot be used because descriptors are
414 shared across different callers.
416 IN_EXCL_UNLINK
417 IN_MASK_ADD
418 IN_ONESHOT */)
419 (Lisp_Object filename, Lisp_Object aspect, Lisp_Object callback)
421 Lisp_Object encoded_file_name;
422 int wd = -1;
423 uint32_t imask = aspect_to_inotifymask (aspect);
424 uint32_t mask = imask | IN_MASK_ADD | IN_EXCL_UNLINK;
426 CHECK_STRING (filename);
428 if (inotifyfd < 0)
430 inotifyfd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
431 if (inotifyfd < 0)
432 report_file_notify_error ("File watching is not available", Qnil);
433 watch_list = Qnil;
434 add_read_fd (inotifyfd, &inotify_callback, NULL);
437 encoded_file_name = ENCODE_FILE (filename);
438 wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
439 if (wd < 0)
440 report_file_notify_error ("Could not add watch for file", filename);
442 return add_watch (wd, filename, imask, callback);
445 static bool
446 valid_watch_descriptor (Lisp_Object wd)
448 return (CONSP (wd)
449 && (RANGED_INTEGERP (0, XCAR (wd), INT_MAX)
450 || (CONSP (XCAR (wd))
451 && RANGED_INTEGERP ((MOST_POSITIVE_FIXNUM >> 16) + 1,
452 XCAR (XCAR (wd)), INT_MAX >> 16)
453 && RANGED_INTEGERP (0, XCDR (XCAR (wd)), (1 << 16) - 1)))
454 && NATNUMP (XCDR (wd)));
457 DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
458 doc: /* Remove an existing WATCH-DESCRIPTOR.
460 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
462 See inotify_rm_watch(2) for more information. */)
463 (Lisp_Object watch_descriptor)
466 Lisp_Object descriptor, id;
468 if (! valid_watch_descriptor (watch_descriptor))
469 report_file_notify_error ("Invalid descriptor ", watch_descriptor);
471 descriptor = XCAR (watch_descriptor);
472 id = XCDR (watch_descriptor);
473 remove_watch (descriptor, id);
475 return Qt;
478 DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
479 doc: /* Check a watch specified by its WATCH-DESCRIPTOR.
481 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
483 A watch can become invalid if the file or directory it watches is
484 deleted, or if the watcher thread exits abnormally for any other
485 reason. Removing the watch by calling `inotify-rm-watch' also makes
486 it invalid. */)
487 (Lisp_Object watch_descriptor)
489 if (! valid_watch_descriptor (watch_descriptor))
490 return Qnil;
491 Lisp_Object tail = assoc_no_quit (XCAR (watch_descriptor), watch_list);
492 if (NILP (tail))
493 return Qnil;
494 Lisp_Object watch = assq_no_quit (XCDR (watch_descriptor), XCDR (tail));
495 return ! NILP (watch) ? Qt : Qnil;
498 #ifdef INOTIFY_DEBUG
499 DEFUN ("inotify-watch-list", Finotify_watch_list, Sinotify_watch_list, 0, 0, 0,
500 doc: /* Return a copy of the internal watch_list. */)
502 return Fcopy_sequence (watch_list);
505 DEFUN ("inotify-allocated-p", Finotify_allocated_p, Sinotify_allocated_p, 0, 0, 0,
506 doc: /* Return non-nil, if an inotify instance is allocated. */)
508 return inotifyfd < 0 ? Qnil : Qt;
510 #endif
512 void
513 syms_of_inotify (void)
515 DEFSYM (Qaccess, "access"); /* IN_ACCESS */
516 DEFSYM (Qattrib, "attrib"); /* IN_ATTRIB */
517 DEFSYM (Qclose_write, "close-write"); /* IN_CLOSE_WRITE */
518 DEFSYM (Qclose_nowrite, "close-nowrite");
519 /* IN_CLOSE_NOWRITE */
520 DEFSYM (Qcreate, "create"); /* IN_CREATE */
521 DEFSYM (Qdelete, "delete"); /* IN_DELETE */
522 DEFSYM (Qdelete_self, "delete-self"); /* IN_DELETE_SELF */
523 DEFSYM (Qmodify, "modify"); /* IN_MODIFY */
524 DEFSYM (Qmove_self, "move-self"); /* IN_MOVE_SELF */
525 DEFSYM (Qmoved_from, "moved-from"); /* IN_MOVED_FROM */
526 DEFSYM (Qmoved_to, "moved-to"); /* IN_MOVED_TO */
527 DEFSYM (Qopen, "open"); /* IN_OPEN */
529 DEFSYM (Qall_events, "all-events"); /* IN_ALL_EVENTS */
530 DEFSYM (Qmove, "move"); /* IN_MOVE */
531 DEFSYM (Qclose, "close"); /* IN_CLOSE */
533 DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */
534 DEFSYM (Qonlydir, "onlydir"); /* IN_ONLYDIR */
536 DEFSYM (Qignored, "ignored"); /* IN_IGNORED */
537 DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */
538 DEFSYM (Qq_overflow, "q-overflow"); /* IN_Q_OVERFLOW */
539 DEFSYM (Qunmount, "unmount"); /* IN_UNMOUNT */
541 defsubr (&Sinotify_add_watch);
542 defsubr (&Sinotify_rm_watch);
543 defsubr (&Sinotify_valid_p);
545 #ifdef INOTIFY_DEBUG
546 defsubr (&Sinotify_watch_list);
547 defsubr (&Sinotify_allocated_p);
548 #endif
549 staticpro (&watch_list);
551 Fprovide (intern_c_string ("inotify"), Qnil);
554 #endif /* HAVE_INOTIFY */