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