1 /* Inotify support for Emacs
3 Copyright (C) 2012-2013 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
10 (at 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 "character.h"
29 #include "frame.h" /* Required for termhooks.h. */
30 #include "termhooks.h"
32 static Lisp_Object Qaccess
; /* IN_ACCESS */
33 static Lisp_Object Qattrib
; /* IN_ATTRIB */
34 static Lisp_Object Qclose_write
; /* IN_CLOSE_WRITE */
35 static Lisp_Object Qclose_nowrite
; /* IN_CLOSE_NOWRITE */
36 static Lisp_Object Qcreate
; /* IN_CREATE */
37 static Lisp_Object Qdelete
; /* IN_DELETE */
38 static Lisp_Object Qdelete_self
; /* IN_DELETE_SELF */
39 static Lisp_Object Qmodify
; /* IN_MODIFY */
40 static Lisp_Object Qmove_self
; /* IN_MOVE_SELF */
41 static Lisp_Object Qmoved_from
; /* IN_MOVED_FROM */
42 static Lisp_Object Qmoved_to
; /* IN_MOVED_TO */
43 static Lisp_Object Qopen
; /* IN_OPEN */
45 static Lisp_Object Qall_events
; /* IN_ALL_EVENTS */
46 static Lisp_Object Qmove
; /* IN_MOVE */
47 static Lisp_Object Qclose
; /* IN_CLOSE */
49 static Lisp_Object Qdont_follow
; /* IN_DONT_FOLLOW */
50 static Lisp_Object Qexcl_unlink
; /* IN_EXCL_UNLINK */
51 static Lisp_Object Qmask_add
; /* IN_MASK_ADD */
52 static Lisp_Object Qoneshot
; /* IN_ONESHOT */
53 static Lisp_Object Qonlydir
; /* IN_ONLYDIR */
55 static Lisp_Object Qignored
; /* IN_IGNORED */
56 static Lisp_Object Qisdir
; /* IN_ISDIR */
57 static Lisp_Object Qq_overflow
; /* IN_Q_OVERFLOW */
58 static Lisp_Object Qunmount
; /* IN_UNMOUNT */
60 #include <sys/inotify.h>
61 #include <sys/ioctl.h>
63 /* Ignore bits that might be undefined on old GNU/Linux systems. */
64 #ifndef IN_EXCL_UNLINK
65 # define IN_EXCL_UNLINK 0
67 #ifndef IN_DONT_FOLLOW
68 # define IN_DONT_FOLLOW 0
74 enum { uninitialized
= -100 };
75 /* File handle for inotify. */
76 static int inotifyfd
= uninitialized
;
78 /* Assoc list of files being watched.
80 (watch-descriptor . callback)
82 static Lisp_Object watch_list
;
85 make_watch_descriptor (int wd
)
87 /* TODO replace this with a Misc Object! */
88 return make_number (wd
);
92 mask_to_aspects (uint32_t mask
) {
93 Lisp_Object aspects
= Qnil
;
95 aspects
= Fcons (Qaccess
, aspects
);
97 aspects
= Fcons (Qattrib
, aspects
);
98 if (mask
& IN_CLOSE_WRITE
)
99 aspects
= Fcons (Qclose_write
, aspects
);
100 if (mask
& IN_CLOSE_NOWRITE
)
101 aspects
= Fcons (Qclose_nowrite
, aspects
);
102 if (mask
& IN_CREATE
)
103 aspects
= Fcons (Qcreate
, aspects
);
104 if (mask
& IN_DELETE
)
105 aspects
= Fcons (Qdelete
, aspects
);
106 if (mask
& IN_DELETE_SELF
)
107 aspects
= Fcons (Qdelete_self
, aspects
);
108 if (mask
& IN_MODIFY
)
109 aspects
= Fcons (Qmodify
, aspects
);
110 if (mask
& IN_MOVE_SELF
)
111 aspects
= Fcons (Qmove_self
, aspects
);
112 if (mask
& IN_MOVED_FROM
)
113 aspects
= Fcons (Qmoved_from
, aspects
);
114 if (mask
& IN_MOVED_TO
)
115 aspects
= Fcons (Qmoved_to
, aspects
);
117 aspects
= Fcons (Qopen
, aspects
);
118 if (mask
& IN_IGNORED
)
119 aspects
= Fcons (Qignored
, aspects
);
121 aspects
= Fcons (Qisdir
, aspects
);
122 if (mask
& IN_Q_OVERFLOW
)
123 aspects
= Fcons (Qq_overflow
, aspects
);
124 if (mask
& IN_UNMOUNT
)
125 aspects
= Fcons (Qunmount
, aspects
);
130 inotifyevent_to_event (Lisp_Object watch_object
, struct inotify_event
const *ev
)
132 Lisp_Object name
= Qnil
;
135 size_t const len
= strlen (ev
->name
);
136 name
= make_unibyte_string (ev
->name
, min (len
, ev
->len
));
137 name
= DECODE_FILE (name
);
140 return list2 (list4 (make_watch_descriptor (ev
->wd
),
141 mask_to_aspects (ev
->mask
),
142 make_number (ev
->cookie
),
144 XCDR (watch_object
));
147 /* This callback is called when the FD is available for read. The inotify
148 events are read from FD and converted into input_events. */
150 inotify_callback (int fd
, void *_
)
152 struct input_event event
;
153 Lisp_Object watch_object
;
160 if (ioctl (fd
, FIONREAD
, &to_read
) == -1)
161 report_file_error ("Error while trying to retrieve file system events",
163 buffer
= xmalloc (to_read
);
164 n
= read (fd
, buffer
, to_read
);
168 report_file_error ("Error while trying to read file system events",
173 event
.kind
= FILE_NOTIFY_EVENT
;
176 while (i
< (size_t)n
)
178 struct inotify_event
*ev
= (struct inotify_event
*)&buffer
[i
];
180 watch_object
= Fassoc (make_watch_descriptor (ev
->wd
), watch_list
);
181 if (!NILP (watch_object
))
183 event
.arg
= inotifyevent_to_event (watch_object
, ev
);
185 /* If event was removed automatically: Drop it from watch list. */
186 if (ev
->mask
& IN_IGNORED
)
187 watch_list
= Fdelete (watch_object
, watch_list
);
189 if (!NILP (event
.arg
))
190 kbd_buffer_store_event (&event
);
193 i
+= sizeof (*ev
) + ev
->len
;
200 symbol_to_inotifymask (Lisp_Object symb
)
202 if (EQ (symb
, Qaccess
))
204 else if (EQ (symb
, Qattrib
))
206 else if (EQ (symb
, Qclose_write
))
207 return IN_CLOSE_WRITE
;
208 else if (EQ (symb
, Qclose_nowrite
))
209 return IN_CLOSE_NOWRITE
;
210 else if (EQ (symb
, Qcreate
))
212 else if (EQ (symb
, Qdelete
))
214 else if (EQ (symb
, Qdelete_self
))
215 return IN_DELETE_SELF
;
216 else if (EQ (symb
, Qmodify
))
218 else if (EQ (symb
, Qmove_self
))
220 else if (EQ (symb
, Qmoved_from
))
221 return IN_MOVED_FROM
;
222 else if (EQ (symb
, Qmoved_to
))
224 else if (EQ (symb
, Qopen
))
226 else if (EQ (symb
, Qmove
))
228 else if (EQ (symb
, Qclose
))
231 else if (EQ (symb
, Qdont_follow
))
232 return IN_DONT_FOLLOW
;
233 else if (EQ (symb
, Qexcl_unlink
))
234 return IN_EXCL_UNLINK
;
235 else if (EQ (symb
, Qmask_add
))
237 else if (EQ (symb
, Qoneshot
))
239 else if (EQ (symb
, Qonlydir
))
242 else if (EQ (symb
, Qt
) || EQ (symb
, Qall_events
))
243 return IN_ALL_EVENTS
;
245 signal_error ("Unknown aspect", symb
);
249 aspect_to_inotifymask (Lisp_Object aspect
)
253 Lisp_Object x
= aspect
;
257 mask
|= symbol_to_inotifymask (XCAR (x
));
263 return symbol_to_inotifymask (aspect
);
266 DEFUN ("inotify-add-watch", Finotify_add_watch
, Sinotify_add_watch
, 3, 3, 0,
267 doc
: /* Add a watch for FILE-NAME to inotify.
269 A WATCH-DESCRIPTOR is returned on success. ASPECT might be one of the following
270 symbols or a list of those symbols:
289 The following symbols can also be added to a list of aspects
297 Watching a directory is not recursive. CALLBACK gets called in case of an
298 event. It gets passed a single argument EVENT which contains an event structure
301 (WATCH-DESCRIPTOR ASPECTS COOKIE NAME)
303 WATCH-DESCRIPTOR is the same object that was returned by this function. It can
304 be tested for equality using `equal'. ASPECTS describes the event. It is a
305 list of ASPECT symbols described above and can also contain one of the following
313 COOKIE is an object that can be compared using `equal' to identify two matching
314 renames (moved-from and moved-to).
316 If a directory is watched then NAME is the name of file that caused the event.
318 See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
319 is managed internally and there is no corresponding inotify_init. Use
320 `inotify-rm-watch' to remove a watch.
322 (Lisp_Object file_name
, Lisp_Object aspect
, Lisp_Object callback
)
325 Lisp_Object watch_object
;
326 Lisp_Object encoded_file_name
;
327 Lisp_Object watch_descriptor
;
330 CHECK_STRING (file_name
);
332 if (inotifyfd
== uninitialized
)
334 inotifyfd
= inotify_init1 (IN_NONBLOCK
|IN_CLOEXEC
);
337 inotifyfd
= uninitialized
;
338 report_file_error ("File watching feature (inotify) is not available",
342 add_read_fd (inotifyfd
, &inotify_callback
, NULL
);
345 mask
= aspect_to_inotifymask (aspect
);
346 encoded_file_name
= ENCODE_FILE (file_name
);
347 watchdesc
= inotify_add_watch (inotifyfd
, SSDATA (encoded_file_name
), mask
);
349 report_file_error ("Could not add watch for file", Fcons (file_name
, Qnil
));
351 watch_descriptor
= make_watch_descriptor (watchdesc
);
353 /* Delete existing watch object. */
354 watch_object
= Fassoc (watch_descriptor
, watch_list
);
355 if (!NILP (watch_object
))
356 watch_list
= Fdelete (watch_object
, watch_list
);
358 /* Store watch object in watch list. */
359 watch_object
= Fcons (watch_descriptor
, callback
);
360 watch_list
= Fcons (watch_object
, watch_list
);
362 return watch_descriptor
;
365 DEFUN ("inotify-rm-watch", Finotify_rm_watch
, Sinotify_rm_watch
, 1, 1, 0,
366 doc
: /* Remove an existing WATCH-DESCRIPTOR.
368 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
370 See inotify_rm_watch(2) for more information.
372 (Lisp_Object watch_descriptor
)
374 Lisp_Object watch_object
;
375 int wd
= XINT (watch_descriptor
);
377 if (inotify_rm_watch (inotifyfd
, wd
) == -1)
378 report_file_error ("Could not rm watch", Fcons (watch_descriptor
,
381 /* Remove watch descriptor from watch list. */
382 watch_object
= Fassoc (watch_descriptor
, watch_list
);
383 if (!NILP (watch_object
))
384 watch_list
= Fdelete (watch_object
, watch_list
);
386 /* Cleanup if no more files are watched. */
387 if (NILP (watch_list
))
390 delete_read_fd (inotifyfd
);
391 inotifyfd
= uninitialized
;
398 syms_of_inotify (void)
400 DEFSYM (Qaccess
, "access");
401 DEFSYM (Qattrib
, "attrib");
402 DEFSYM (Qclose_write
, "close-write");
403 DEFSYM (Qclose_nowrite
, "close-nowrite");
404 DEFSYM (Qcreate
, "create");
405 DEFSYM (Qdelete
, "delete");
406 DEFSYM (Qdelete_self
, "delete-self");
407 DEFSYM (Qmodify
, "modify");
408 DEFSYM (Qmove_self
, "move-self");
409 DEFSYM (Qmoved_from
, "moved-from");
410 DEFSYM (Qmoved_to
, "moved-to");
411 DEFSYM (Qopen
, "open");
413 DEFSYM (Qall_events
, "all-events");
414 DEFSYM (Qmove
, "move");
415 DEFSYM (Qclose
, "close");
417 DEFSYM (Qdont_follow
, "dont-follow");
418 DEFSYM (Qexcl_unlink
, "excl-unlink");
419 DEFSYM (Qmask_add
, "mask-add");
420 DEFSYM (Qoneshot
, "oneshot");
421 DEFSYM (Qonlydir
, "onlydir");
423 DEFSYM (Qignored
, "ignored");
424 DEFSYM (Qisdir
, "isdir");
425 DEFSYM (Qq_overflow
, "q-overflow");
426 DEFSYM (Qunmount
, "unmount");
428 defsubr (&Sinotify_add_watch
);
429 defsubr (&Sinotify_rm_watch
);
431 staticpro (&watch_list
);
433 Fprovide (intern_c_string ("inotify"), Qnil
);
436 #endif /* HAVE_INOTIFY */