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/>. */
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
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.
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
;
76 mask_to_aspects (uint32_t mask
)
78 Lisp_Object aspects
= Qnil
;
80 aspects
= Fcons (Qaccess
, aspects
);
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
);
88 aspects
= Fcons (Qcreate
, aspects
);
90 aspects
= Fcons (Qdelete
, aspects
);
91 if (mask
& IN_DELETE_SELF
)
92 aspects
= Fcons (Qdelete_self
, aspects
);
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
);
102 aspects
= Fcons (Qopen
, aspects
);
103 if (mask
& IN_IGNORED
)
104 aspects
= Fcons (Qignored
, aspects
);
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
);
115 symbol_to_inotifymask (Lisp_Object symb
)
117 if (EQ (symb
, Qaccess
))
119 else if (EQ (symb
, Qattrib
))
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
))
127 else if (EQ (symb
, Qdelete
))
129 else if (EQ (symb
, Qdelete_self
))
130 return IN_DELETE_SELF
;
131 else if (EQ (symb
, Qmodify
))
133 else if (EQ (symb
, Qmove_self
))
135 else if (EQ (symb
, Qmoved_from
))
136 return IN_MOVED_FROM
;
137 else if (EQ (symb
, Qmoved_to
))
139 else if (EQ (symb
, Qopen
))
141 else if (EQ (symb
, Qmove
))
143 else if (EQ (symb
, Qclose
))
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
;
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 CALLBACK. Returns a cons (DESCRIPTOR . ID) uniquely identifying the
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
);
216 tail
= list1 (descriptor
);
217 watch_list
= Fcons (tail
, watch_list
);
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
)))
226 if (MOST_POSITIVE_FIXNUM
< id
)
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). */
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
))
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. */
262 remove_descriptor (Lisp_Object prevtail
, bool invalid_p
)
264 Lisp_Object tail
= CONSP (prevtail
) ? XCDR (prevtail
) : watch_list
;
266 int inotify_errno
= 0;
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
));
279 watch_list
= XCDR (tail
);
280 if (NILP (watch_list
))
282 delete_read_fd (inotifyfd
);
283 emacs_close (inotifyfd
);
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). */
297 remove_watch (Lisp_Object descriptor
, Lisp_Object id
)
299 Lisp_Object prevtail
= find_descriptor (descriptor
);
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);
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. */
317 inotify_callback (int fd
, void *_
)
320 if (ioctl (fd
, FIONREAD
, &to_read
) < 0)
321 report_file_notify_error ("Error while retrieving file system events",
324 char *buffer
= SAFE_ALLOCA (to_read
);
325 ssize_t n
= read (fd
, buffer
, to_read
);
327 report_file_notify_error ("Error while reading file system events", Qnil
);
329 struct input_event 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
;
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:
384 The following symbols can also be added to a list of aspects:
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
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.
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
));
426 uint32_t mask
= (INOTIFY_DEFAULT_MASK
427 | (dont_follow
? IN_DONT_FOLLOW
: 0));
429 CHECK_STRING (filename
);
433 inotifyfd
= inotify_init1 (IN_NONBLOCK
| IN_CLOEXEC
);
435 report_file_notify_error ("File watching is not available", 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
);
443 report_file_notify_error ("Could not add watch for file", filename
);
445 return add_watch (wd
, filename
, aspect
, callback
);
449 valid_watch_descriptor (Lisp_Object 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
);
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
490 (Lisp_Object watch_descriptor
)
492 if (! valid_watch_descriptor (watch_descriptor
))
494 Lisp_Object tail
= assoc_no_quit (XCAR (watch_descriptor
), watch_list
);
497 Lisp_Object watch
= assq_no_quit (XCDR (watch_descriptor
), XCDR (tail
));
498 return ! NILP (watch
) ? Qt
: Qnil
;
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
;
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
);
548 defsubr (&Sinotify_watch_list
);
549 defsubr (&Sinotify_allocated_p
);
551 staticpro (&watch_list
);
553 Fprovide (intern_c_string ("inotify"), Qnil
);
556 #endif /* HAVE_INOTIFY */