Add seq-set-equal-p to test for set equality
[emacs.git] / src / inotify.c
blob290701349ef1af4665eece8de4a1f689bdca4751
1 /* Inotify support for Emacs
3 Copyright (C) 2012-2017 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 <http://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
44 #define INOTIFY_DEFAULT_MASK (IN_ALL_EVENTS | IN_EXCL_UNLINK)
46 /* File handle for inotify. */
47 static int inotifyfd = -1;
49 /* Alist of files being watched. We want the returned descriptor to
50 be unique for every watch, but inotify returns the same descriptor
51 WD for multiple calls to inotify_add_watch with the same file.
52 Supply a nonnegative integer ID, so that WD and ID together
53 uniquely identify a watch/file combination.
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.
58 IN_EXCL_UNLINK
59 IN_MASK_ADD
60 IN_ONESHOT
61 IN_ONLYDIR
63 Each element of this list is of the form (DESCRIPTOR . WATCHES)
64 where no two DESCRIPTOR values are the same. DESCRIPTOR represents
65 the inotify watch descriptor and WATCHES is a list with elements of
66 the form (ID FILENAME CALLBACK MASK), where ID is the integer
67 described above, FILENAME names the file being watched, CALLBACK is
68 invoked when the event occurs, and MASK represents the aspects
69 being watched. The WATCHES list is sorted by ID. Although
70 DESCRIPTOR and MASK are ordinarily integers, they are conses when
71 representing integers outside of fixnum range. */
73 static Lisp_Object watch_list;
75 static Lisp_Object
76 mask_to_aspects (uint32_t mask)
78 Lisp_Object aspects = Qnil;
79 if (mask & IN_ACCESS)
80 aspects = Fcons (Qaccess, aspects);
81 if (mask & IN_ATTRIB)
82 aspects = Fcons (Qattrib, aspects);
83 if (mask & IN_CLOSE_WRITE)
84 aspects = Fcons (Qclose_write, aspects);
85 if (mask & IN_CLOSE_NOWRITE)
86 aspects = Fcons (Qclose_nowrite, aspects);
87 if (mask & IN_CREATE)
88 aspects = Fcons (Qcreate, aspects);
89 if (mask & IN_DELETE)
90 aspects = Fcons (Qdelete, aspects);
91 if (mask & IN_DELETE_SELF)
92 aspects = Fcons (Qdelete_self, aspects);
93 if (mask & IN_MODIFY)
94 aspects = Fcons (Qmodify, aspects);
95 if (mask & IN_MOVE_SELF)
96 aspects = Fcons (Qmove_self, aspects);
97 if (mask & IN_MOVED_FROM)
98 aspects = Fcons (Qmoved_from, aspects);
99 if (mask & IN_MOVED_TO)
100 aspects = Fcons (Qmoved_to, aspects);
101 if (mask & IN_OPEN)
102 aspects = Fcons (Qopen, aspects);
103 if (mask & IN_IGNORED)
104 aspects = Fcons (Qignored, aspects);
105 if (mask & IN_ISDIR)
106 aspects = Fcons (Qisdir, aspects);
107 if (mask & IN_Q_OVERFLOW)
108 aspects = Fcons (Qq_overflow, aspects);
109 if (mask & IN_UNMOUNT)
110 aspects = Fcons (Qunmount, aspects);
111 return aspects;
114 static uint32_t
115 symbol_to_inotifymask (Lisp_Object symb)
117 if (EQ (symb, Qaccess))
118 return IN_ACCESS;
119 else if (EQ (symb, Qattrib))
120 return IN_ATTRIB;
121 else if (EQ (symb, Qclose_write))
122 return IN_CLOSE_WRITE;
123 else if (EQ (symb, Qclose_nowrite))
124 return IN_CLOSE_NOWRITE;
125 else if (EQ (symb, Qcreate))
126 return IN_CREATE;
127 else if (EQ (symb, Qdelete))
128 return IN_DELETE;
129 else if (EQ (symb, Qdelete_self))
130 return IN_DELETE_SELF;
131 else if (EQ (symb, Qmodify))
132 return IN_MODIFY;
133 else if (EQ (symb, Qmove_self))
134 return IN_MOVE_SELF;
135 else if (EQ (symb, Qmoved_from))
136 return IN_MOVED_FROM;
137 else if (EQ (symb, Qmoved_to))
138 return IN_MOVED_TO;
139 else if (EQ (symb, Qopen))
140 return IN_OPEN;
141 else if (EQ (symb, Qmove))
142 return IN_MOVE;
143 else if (EQ (symb, Qclose))
144 return IN_CLOSE;
146 else if (EQ (symb, Qdont_follow))
147 return IN_DONT_FOLLOW;
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 CALLBACK. Returns a cons (DESCRIPTOR . ID) uniquely identifying the
202 new watch. */
203 static Lisp_Object
204 add_watch (int wd, Lisp_Object filename,
205 Lisp_Object aspect, 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 uint32_t imask = aspect_to_inotifymask (aspect);
211 Lisp_Object mask = INTEGER_TO_CONS (imask);
213 EMACS_INT id = 0;
214 if (NILP (tail))
216 tail = list1 (descriptor);
217 watch_list = Fcons (tail, watch_list);
219 else
221 /* Assign a watch ID that is not already in use, by looking
222 for a gap in the existing sorted list. */
223 for (; ! NILP (XCDR (tail)); tail = XCDR (tail), id++)
224 if (!EQ (XCAR (XCAR (XCDR (tail))), make_number (id)))
225 break;
226 if (MOST_POSITIVE_FIXNUM < id)
227 emacs_abort ();
230 /* Insert the newly-assigned ID into the previously-discovered gap,
231 which is possibly at the end of the list. Inserting it there
232 keeps the list sorted. */
233 watch_id = make_number (id);
234 watch = list4 (watch_id, filename, callback, mask);
235 XSETCDR (tail, Fcons (watch, XCDR (tail)));
237 return Fcons (descriptor, watch_id);
240 /* Find the watch list element (if any) matching DESCRIPTOR. Return
241 nil if not found. If found, return t if the first element matches
242 DESCRIPTOR; otherwise, return the cons whose cdr matches
243 DESCRIPTOR. This lets the caller easily remove the element
244 matching DESCRIPTOR without having to search for it again, and
245 without calling Fdelete (which might quit). */
247 static Lisp_Object
248 find_descriptor (Lisp_Object descriptor)
250 Lisp_Object tail, prevtail = Qt;
251 for (tail = watch_list; !NILP (tail); prevtail = tail, tail = XCDR (tail))
252 if (equal_no_quit (XCAR (XCAR (tail)), descriptor))
253 return prevtail;
254 return Qnil;
257 /* Remove all watches associated with the watch list element after
258 PREVTAIL, or after the first element if PREVTAIL is t. If INVALID_P
259 is true, the descriptor is already invalid, i.e., it received a
260 IN_IGNORED event. In this case skip calling inotify_rm_watch. */
261 static void
262 remove_descriptor (Lisp_Object prevtail, bool invalid_p)
264 Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list;
266 int inotify_errno = 0;
267 if (! invalid_p)
269 int wd;
270 CONS_TO_INTEGER (XCAR (XCAR (tail)), int, wd);
271 if (inotify_rm_watch (inotifyfd, wd) != 0)
272 inotify_errno = errno;
275 if (CONSP (prevtail))
276 XSETCDR (prevtail, XCDR (tail));
277 else
279 watch_list = XCDR (tail);
280 if (NILP (watch_list))
282 delete_read_fd (inotifyfd);
283 emacs_close (inotifyfd);
284 inotifyfd = -1;
288 if (inotify_errno != 0)
290 errno = inotify_errno;
291 report_file_notify_error ("Could not rm watch", XCAR (tail));
295 /* Remove watch associated with (descriptor, id). */
296 static void
297 remove_watch (Lisp_Object descriptor, Lisp_Object id)
299 Lisp_Object prevtail = find_descriptor (descriptor);
300 if (NILP (prevtail))
301 return;
303 Lisp_Object elt = XCAR (CONSP (prevtail) ? XCDR (prevtail) : watch_list);
304 for (Lisp_Object prev = elt; !NILP (XCDR (prev)); prev = XCDR (prev))
305 if (EQ (id, XCAR (XCAR (XCDR (prev)))))
307 XSETCDR (prev, XCDR (XCDR (prev)));
308 if (NILP (XCDR (elt)))
309 remove_descriptor (prevtail, false);
310 break;
314 /* This callback is called when the FD is available for read. The inotify
315 events are read from FD and converted into input_events. */
316 static void
317 inotify_callback (int fd, void *_)
319 int to_read;
320 if (ioctl (fd, FIONREAD, &to_read) < 0)
321 report_file_notify_error ("Error while retrieving file system events",
322 Qnil);
323 USE_SAFE_ALLOCA;
324 char *buffer = SAFE_ALLOCA (to_read);
325 ssize_t n = read (fd, buffer, to_read);
326 if (n < 0)
327 report_file_notify_error ("Error while reading file system events", Qnil);
329 struct input_event event;
330 EVENT_INIT (event);
331 event.kind = FILE_NOTIFY_EVENT;
333 for (ssize_t i = 0; i < n; )
335 struct inotify_event *ev = (struct inotify_event *) &buffer[i];
336 Lisp_Object descriptor = INTEGER_TO_CONS (ev->wd);
337 Lisp_Object prevtail = find_descriptor (descriptor);
339 if (! NILP (prevtail))
341 Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list;
342 for (Lisp_Object watches = XCDR (XCAR (tail)); ! NILP (watches);
343 watches = XCDR (watches))
345 event.arg = inotifyevent_to_event (XCAR (watches), ev);
346 if (!NILP (event.arg))
347 kbd_buffer_store_event (&event);
349 /* If event was removed automatically: Drop it from watch list. */
350 if (ev->mask & IN_IGNORED)
351 remove_descriptor (prevtail, true);
353 i += sizeof (*ev) + ev->len;
356 SAFE_FREE ();
359 DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
360 doc: /* Add a watch for FILE-NAME to inotify.
362 Return a watch descriptor. The watch will look for ASPECT events and
363 invoke CALLBACK when an event occurs.
365 ASPECT might be one of the following symbols or a list of those symbols:
367 access
368 attrib
369 close-write
370 close-nowrite
371 create
372 delete
373 delete-self
374 modify
375 move-self
376 moved-from
377 moved-to
378 open
380 all-events or t
381 move
382 close
384 The following symbols can also be added to a list of aspects:
386 dont-follow
388 Watching a directory is not recursive. CALLBACK is passed a single argument
389 EVENT which contains an event structure of the format
391 \(WATCH-DESCRIPTOR ASPECTS NAME COOKIE)
393 WATCH-DESCRIPTOR is the same object that was returned by this function. It can
394 be tested for equality using `equal'. ASPECTS describes the event. It is a
395 list of ASPECT symbols described above and can also contain one of the following
396 symbols
398 ignored
399 isdir
400 q-overflow
401 unmount
403 If a directory is watched then NAME is the name of file that caused the event.
405 COOKIE is an object that can be compared using `equal' to identify two matching
406 renames (moved-from and moved-to).
408 See inotify(7) and inotify_add_watch(2) for further information. The
409 inotify fd is managed internally and there is no corresponding
410 inotify_init. Use `inotify-rm-watch' to remove a watch.
412 Also note, that the following inotify bit-masks can not be used, due
413 to the fact that descriptors are shared across different callers.
415 IN_EXCL_UNLINK
416 IN_MASK_ADD
417 IN_ONESHOT
418 IN_ONLYDIR */)
419 (Lisp_Object filename, Lisp_Object aspect, Lisp_Object callback)
421 Lisp_Object encoded_file_name;
422 bool dont_follow = (CONSP (aspect)
423 ? ! NILP (Fmemq (Qdont_follow, aspect))
424 : EQ (Qdont_follow, aspect));
425 int wd = -1;
426 uint32_t mask = (INOTIFY_DEFAULT_MASK
427 | (dont_follow ? IN_DONT_FOLLOW : 0));
429 CHECK_STRING (filename);
431 if (inotifyfd < 0)
433 inotifyfd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
434 if (inotifyfd < 0)
435 report_file_notify_error ("File watching is not available", Qnil);
436 watch_list = Qnil;
437 add_read_fd (inotifyfd, &inotify_callback, NULL);
440 encoded_file_name = ENCODE_FILE (filename);
441 wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
442 if (wd < 0)
443 report_file_notify_error ("Could not add watch for file", filename);
445 return add_watch (wd, filename, aspect, callback);
448 static bool
449 valid_watch_descriptor (Lisp_Object wd)
451 return (CONSP (wd)
452 && (RANGED_INTEGERP (0, XCAR (wd), INT_MAX)
453 || (CONSP (XCAR (wd))
454 && RANGED_INTEGERP ((MOST_POSITIVE_FIXNUM >> 16) + 1,
455 XCAR (XCAR (wd)), INT_MAX >> 16)
456 && RANGED_INTEGERP (0, XCDR (XCAR (wd)), (1 << 16) - 1)))
457 && NATNUMP (XCDR (wd)));
460 DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
461 doc: /* Remove an existing WATCH-DESCRIPTOR.
463 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
465 See inotify_rm_watch(2) for more information. */)
466 (Lisp_Object watch_descriptor)
469 Lisp_Object descriptor, id;
471 if (! valid_watch_descriptor (watch_descriptor))
472 report_file_notify_error ("Invalid descriptor ", watch_descriptor);
474 descriptor = XCAR (watch_descriptor);
475 id = XCDR (watch_descriptor);
476 remove_watch (descriptor, id);
478 return Qt;
481 DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
482 doc: /* Check a watch specified by its WATCH-DESCRIPTOR.
484 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
486 A watch can become invalid if the file or directory it watches is
487 deleted, or if the watcher thread exits abnormally for any other
488 reason. Removing the watch by calling `inotify-rm-watch' also makes
489 it invalid. */)
490 (Lisp_Object watch_descriptor)
492 if (! valid_watch_descriptor (watch_descriptor))
493 return Qnil;
494 Lisp_Object tail = assoc_no_quit (XCAR (watch_descriptor), watch_list);
495 if (NILP (tail))
496 return Qnil;
497 Lisp_Object watch = assq_no_quit (XCDR (watch_descriptor), XCDR (tail));
498 return ! NILP (watch) ? Qt : Qnil;
501 #ifdef INOTIFY_DEBUG
502 DEFUN ("inotify-watch-list", Finotify_watch_list, Sinotify_watch_list, 0, 0, 0,
503 doc: /* Return a copy of the internal watch_list. */)
505 return Fcopy_sequence (watch_list);
508 DEFUN ("inotify-allocated-p", Finotify_allocated_p, Sinotify_allocated_p, 0, 0, 0,
509 doc: /* Return non-nil, if a inotify instance is allocated. */)
511 return inotifyfd < 0 ? Qnil : Qt;
513 #endif
515 void
516 syms_of_inotify (void)
518 DEFSYM (Qaccess, "access"); /* IN_ACCESS */
519 DEFSYM (Qattrib, "attrib"); /* IN_ATTRIB */
520 DEFSYM (Qclose_write, "close-write"); /* IN_CLOSE_WRITE */
521 DEFSYM (Qclose_nowrite, "close-nowrite");
522 /* IN_CLOSE_NOWRITE */
523 DEFSYM (Qcreate, "create"); /* IN_CREATE */
524 DEFSYM (Qdelete, "delete"); /* IN_DELETE */
525 DEFSYM (Qdelete_self, "delete-self"); /* IN_DELETE_SELF */
526 DEFSYM (Qmodify, "modify"); /* IN_MODIFY */
527 DEFSYM (Qmove_self, "move-self"); /* IN_MOVE_SELF */
528 DEFSYM (Qmoved_from, "moved-from"); /* IN_MOVED_FROM */
529 DEFSYM (Qmoved_to, "moved-to"); /* IN_MOVED_TO */
530 DEFSYM (Qopen, "open"); /* IN_OPEN */
532 DEFSYM (Qall_events, "all-events"); /* IN_ALL_EVENTS */
533 DEFSYM (Qmove, "move"); /* IN_MOVE */
534 DEFSYM (Qclose, "close"); /* IN_CLOSE */
536 DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */
538 DEFSYM (Qignored, "ignored"); /* IN_IGNORED */
539 DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */
540 DEFSYM (Qq_overflow, "q-overflow"); /* IN_Q_OVERFLOW */
541 DEFSYM (Qunmount, "unmount"); /* IN_UNMOUNT */
543 defsubr (&Sinotify_add_watch);
544 defsubr (&Sinotify_rm_watch);
545 defsubr (&Sinotify_valid_p);
547 #ifdef INOTIFY_DEBUG
548 defsubr (&Sinotify_watch_list);
549 defsubr (&Sinotify_allocated_p);
550 #endif
551 staticpro (&watch_list);
553 Fprovide (intern_c_string ("inotify"), Qnil);
556 #endif /* HAVE_INOTIFY */