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/>. */
28 #include "termhooks.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
38 #ifndef IN_DONT_FOLLOW
39 # define IN_DONT_FOLLOW 0
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.
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
;
74 mask_to_aspects (uint32_t mask
)
76 Lisp_Object aspects
= Qnil
;
78 aspects
= Fcons (Qaccess
, aspects
);
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
);
86 aspects
= Fcons (Qcreate
, aspects
);
88 aspects
= Fcons (Qdelete
, aspects
);
89 if (mask
& IN_DELETE_SELF
)
90 aspects
= Fcons (Qdelete_self
, aspects
);
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
);
100 aspects
= Fcons (Qopen
, aspects
);
101 if (mask
& IN_IGNORED
)
102 aspects
= Fcons (Qignored
, aspects
);
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
);
113 symbol_to_inotifymask (Lisp_Object symb
)
115 if (EQ (symb
, Qaccess
))
117 else if (EQ (symb
, Qattrib
))
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
))
125 else if (EQ (symb
, Qdelete
))
127 else if (EQ (symb
, Qdelete_self
))
128 return IN_DELETE_SELF
;
129 else if (EQ (symb
, Qmodify
))
131 else if (EQ (symb
, Qmove_self
))
133 else if (EQ (symb
, Qmoved_from
))
134 return IN_MOVED_FROM
;
135 else if (EQ (symb
, Qmoved_to
))
137 else if (EQ (symb
, Qopen
))
139 else if (EQ (symb
, Qmove
))
141 else if (EQ (symb
, Qclose
))
144 else if (EQ (symb
, Qdont_follow
))
145 return IN_DONT_FOLLOW
;
146 else if (EQ (symb
, Qonlydir
))
149 else if (EQ (symb
, Qt
) || EQ (symb
, Qall_events
))
150 return IN_ALL_EVENTS
;
154 report_file_notify_error ("Unknown aspect", symb
);
159 aspect_to_inotifymask (Lisp_Object aspect
)
161 if (CONSP (aspect
) || NILP (aspect
))
163 Lisp_Object x
= aspect
;
166 mask
|= symbol_to_inotifymask (XCAR (x
));
167 CHECK_LIST_END (x
, aspect
);
171 return symbol_to_inotifymask (aspect
);
175 inotifyevent_to_event (Lisp_Object watch
, struct inotify_event
const *ev
)
179 CONS_TO_INTEGER (Fnth (make_number (3), watch
), uint32_t, mask
);
181 if (! (mask
& ev
->mask
))
186 size_t const len
= strlen (ev
->name
);
187 name
= make_unibyte_string (ev
->name
, min (len
, ev
->len
));
188 name
= DECODE_FILE (name
);
191 name
= XCAR (XCDR (watch
));
193 return list2 (list4 (Fcons (INTEGER_TO_CONS (ev
->wd
), XCAR (watch
)),
194 mask_to_aspects (ev
->mask
),
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. */
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
);
215 tail
= list1 (descriptor
);
216 watch_list
= Fcons (tail
, watch_list
);
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
)))
225 if (MOST_POSITIVE_FIXNUM
< id
)
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). */
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
))
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. */
261 remove_descriptor (Lisp_Object prevtail
, bool invalid_p
)
263 Lisp_Object tail
= CONSP (prevtail
) ? XCDR (prevtail
) : watch_list
;
265 int inotify_errno
= 0;
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
));
278 watch_list
= XCDR (tail
);
279 if (NILP (watch_list
))
281 delete_read_fd (inotifyfd
);
282 emacs_close (inotifyfd
);
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). */
296 remove_watch (Lisp_Object descriptor
, Lisp_Object id
)
298 Lisp_Object prevtail
= find_descriptor (descriptor
);
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);
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. */
316 inotify_callback (int fd
, void *_
)
319 if (ioctl (fd
, FIONREAD
, &to_read
) < 0)
320 report_file_notify_error ("Error while retrieving file system events",
323 char *buffer
= SAFE_ALLOCA (to_read
);
324 ssize_t n
= read (fd
, buffer
, to_read
);
326 report_file_notify_error ("Error while reading file system events", Qnil
);
328 struct input_event 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
;
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:
383 ASPECT can also contain the following symbols, which control whether
384 the watch descriptor will be created:
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
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.
419 (Lisp_Object filename
, Lisp_Object aspect
, Lisp_Object callback
)
421 Lisp_Object encoded_file_name
;
423 uint32_t imask
= aspect_to_inotifymask (aspect
);
424 uint32_t mask
= imask
| IN_MASK_ADD
| IN_EXCL_UNLINK
;
426 CHECK_STRING (filename
);
430 inotifyfd
= inotify_init1 (IN_NONBLOCK
| IN_CLOEXEC
);
432 report_file_notify_error ("File watching is not available", 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
);
440 report_file_notify_error ("Could not add watch for file", filename
);
442 return add_watch (wd
, filename
, imask
, callback
);
446 valid_watch_descriptor (Lisp_Object 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
);
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
487 (Lisp_Object watch_descriptor
)
489 if (! valid_watch_descriptor (watch_descriptor
))
491 Lisp_Object tail
= assoc_no_quit (XCAR (watch_descriptor
), watch_list
);
494 Lisp_Object watch
= assq_no_quit (XCDR (watch_descriptor
), XCDR (tail
));
495 return ! NILP (watch
) ? Qt
: Qnil
;
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 a inotify instance is allocated. */)
508 return inotifyfd
< 0 ? Qnil
: Qt
;
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
);
546 defsubr (&Sinotify_watch_list
);
547 defsubr (&Sinotify_allocated_p
);
549 staticpro (&watch_list
);
551 Fprovide (intern_c_string ("inotify"), Qnil
);
554 #endif /* HAVE_INOTIFY */