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