ffloor etc. now accept only floats
[emacs.git] / src / inotify.c
blob61ef6153286e87113fc54d2b4640f01287cb967e
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/>. */
20 #include <config.h>
22 #ifdef HAVE_INOTIFY
24 #include "lisp.h"
25 #include "coding.h"
26 #include "process.h"
27 #include "keyboard.h"
28 #include "termhooks.h"
30 #include <errno.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
37 #endif
38 #ifndef IN_DONT_FOLLOW
39 # define IN_DONT_FOLLOW 0
40 #endif
41 #ifndef IN_ONLYDIR
42 # define IN_ONLYDIR 0
43 #endif
45 /* File handle for inotify. */
46 static int inotifyfd = -1;
48 /* Assoc list of files being watched.
49 Format: (watch-descriptor name callback)
51 static Lisp_Object watch_list;
53 static Lisp_Object
54 make_watch_descriptor (int wd)
56 /* TODO replace this with a Misc Object! */
57 return make_number (wd);
60 static Lisp_Object
61 mask_to_aspects (uint32_t mask) {
62 Lisp_Object aspects = Qnil;
63 if (mask & IN_ACCESS)
64 aspects = Fcons (Qaccess, aspects);
65 if (mask & IN_ATTRIB)
66 aspects = Fcons (Qattrib, aspects);
67 if (mask & IN_CLOSE_WRITE)
68 aspects = Fcons (Qclose_write, aspects);
69 if (mask & IN_CLOSE_NOWRITE)
70 aspects = Fcons (Qclose_nowrite, aspects);
71 if (mask & IN_CREATE)
72 aspects = Fcons (Qcreate, aspects);
73 if (mask & IN_DELETE)
74 aspects = Fcons (Qdelete, aspects);
75 if (mask & IN_DELETE_SELF)
76 aspects = Fcons (Qdelete_self, aspects);
77 if (mask & IN_MODIFY)
78 aspects = Fcons (Qmodify, aspects);
79 if (mask & IN_MOVE_SELF)
80 aspects = Fcons (Qmove_self, aspects);
81 if (mask & IN_MOVED_FROM)
82 aspects = Fcons (Qmoved_from, aspects);
83 if (mask & IN_MOVED_TO)
84 aspects = Fcons (Qmoved_to, aspects);
85 if (mask & IN_OPEN)
86 aspects = Fcons (Qopen, aspects);
87 if (mask & IN_IGNORED)
88 aspects = Fcons (Qignored, aspects);
89 if (mask & IN_ISDIR)
90 aspects = Fcons (Qisdir, aspects);
91 if (mask & IN_Q_OVERFLOW)
92 aspects = Fcons (Qq_overflow, aspects);
93 if (mask & IN_UNMOUNT)
94 aspects = Fcons (Qunmount, aspects);
95 return aspects;
98 static Lisp_Object
99 inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
101 Lisp_Object name = Qnil;
102 if (ev->len > 0)
104 size_t const len = strlen (ev->name);
105 name = make_unibyte_string (ev->name, min (len, ev->len));
106 name = DECODE_FILE (name);
108 else
109 name = XCAR (XCDR (watch_object));
111 return list2 (list4 (make_watch_descriptor (ev->wd),
112 mask_to_aspects (ev->mask),
113 name,
114 make_number (ev->cookie)),
115 Fnth (make_number (2), watch_object));
118 /* This callback is called when the FD is available for read. The inotify
119 events are read from FD and converted into input_events. */
120 static void
121 inotify_callback (int fd, void *_)
123 struct input_event event;
124 Lisp_Object watch_object;
125 int to_read;
126 char *buffer;
127 ssize_t n;
128 size_t i;
130 to_read = 0;
131 if (ioctl (fd, FIONREAD, &to_read) == -1)
132 report_file_notify_error ("Error while retrieving file system events",
133 Qnil);
134 buffer = xmalloc (to_read);
135 n = read (fd, buffer, to_read);
136 if (n < 0)
138 xfree (buffer);
139 report_file_notify_error ("Error while reading file system events", Qnil);
142 EVENT_INIT (event);
143 event.kind = FILE_NOTIFY_EVENT;
145 i = 0;
146 while (i < (size_t)n)
148 struct inotify_event *ev = (struct inotify_event *) &buffer[i];
150 watch_object = Fassoc (make_watch_descriptor (ev->wd), watch_list);
151 if (!NILP (watch_object))
153 event.arg = inotifyevent_to_event (watch_object, ev);
155 /* If event was removed automatically: Drop it from watch list. */
156 if (ev->mask & IN_IGNORED)
157 watch_list = Fdelete (watch_object, watch_list);
159 if (!NILP (event.arg))
160 kbd_buffer_store_event (&event);
163 i += sizeof (*ev) + ev->len;
166 xfree (buffer);
169 static uint32_t
170 symbol_to_inotifymask (Lisp_Object symb)
172 if (EQ (symb, Qaccess))
173 return IN_ACCESS;
174 else if (EQ (symb, Qattrib))
175 return IN_ATTRIB;
176 else if (EQ (symb, Qclose_write))
177 return IN_CLOSE_WRITE;
178 else if (EQ (symb, Qclose_nowrite))
179 return IN_CLOSE_NOWRITE;
180 else if (EQ (symb, Qcreate))
181 return IN_CREATE;
182 else if (EQ (symb, Qdelete))
183 return IN_DELETE;
184 else if (EQ (symb, Qdelete_self))
185 return IN_DELETE_SELF;
186 else if (EQ (symb, Qmodify))
187 return IN_MODIFY;
188 else if (EQ (symb, Qmove_self))
189 return IN_MOVE_SELF;
190 else if (EQ (symb, Qmoved_from))
191 return IN_MOVED_FROM;
192 else if (EQ (symb, Qmoved_to))
193 return IN_MOVED_TO;
194 else if (EQ (symb, Qopen))
195 return IN_OPEN;
196 else if (EQ (symb, Qmove))
197 return IN_MOVE;
198 else if (EQ (symb, Qclose))
199 return IN_CLOSE;
201 else if (EQ (symb, Qdont_follow))
202 return IN_DONT_FOLLOW;
203 else if (EQ (symb, Qexcl_unlink))
204 return IN_EXCL_UNLINK;
205 else if (EQ (symb, Qmask_add))
206 return IN_MASK_ADD;
207 else if (EQ (symb, Qoneshot))
208 return IN_ONESHOT;
209 else if (EQ (symb, Qonlydir))
210 return IN_ONLYDIR;
212 else if (EQ (symb, Qt) || EQ (symb, Qall_events))
213 return IN_ALL_EVENTS;
214 else
216 errno = EINVAL;
217 report_file_notify_error ("Unknown aspect", symb);
221 static uint32_t
222 aspect_to_inotifymask (Lisp_Object aspect)
224 if (CONSP (aspect))
226 Lisp_Object x = aspect;
227 uint32_t mask = 0;
228 while (CONSP (x))
230 mask |= symbol_to_inotifymask (XCAR (x));
231 x = XCDR (x);
233 return mask;
235 else
236 return symbol_to_inotifymask (aspect);
239 DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
240 doc: /* Add a watch for FILE-NAME to inotify.
242 Return a watch descriptor. The watch will look for ASPECT events and
243 invoke CALLBACK when an event occurs.
245 ASPECT might be one of the following symbols or a list of those symbols:
247 access
248 attrib
249 close-write
250 close-nowrite
251 create
252 delete
253 delete-self
254 modify
255 move-self
256 moved-from
257 moved-to
258 open
260 all-events or t
261 move
262 close
264 The following symbols can also be added to a list of aspects:
266 dont-follow
267 excl-unlink
268 mask-add
269 oneshot
270 onlydir
272 Watching a directory is not recursive. CALLBACK is passed a single argument
273 EVENT which contains an event structure of the format
275 \(WATCH-DESCRIPTOR ASPECTS NAME COOKIE)
277 WATCH-DESCRIPTOR is the same object that was returned by this function. It can
278 be tested for equality using `equal'. ASPECTS describes the event. It is a
279 list of ASPECT symbols described above and can also contain one of the following
280 symbols
282 ignored
283 isdir
284 q-overflow
285 unmount
287 If a directory is watched then NAME is the name of file that caused the event.
289 COOKIE is an object that can be compared using `equal' to identify two matching
290 renames (moved-from and moved-to).
292 See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
293 is managed internally and there is no corresponding inotify_init. Use
294 `inotify-rm-watch' to remove a watch.
296 (Lisp_Object file_name, Lisp_Object aspect, Lisp_Object callback)
298 uint32_t mask;
299 Lisp_Object watch_object;
300 Lisp_Object encoded_file_name;
301 Lisp_Object watch_descriptor;
302 int watchdesc = -1;
304 CHECK_STRING (file_name);
306 if (inotifyfd < 0)
308 inotifyfd = inotify_init1 (IN_NONBLOCK|IN_CLOEXEC);
309 if (inotifyfd < 0)
310 report_file_notify_error ("File watching is not available", Qnil);
311 watch_list = Qnil;
312 add_read_fd (inotifyfd, &inotify_callback, NULL);
315 mask = aspect_to_inotifymask (aspect);
316 encoded_file_name = ENCODE_FILE (file_name);
317 watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
318 if (watchdesc == -1)
319 report_file_notify_error ("Could not add watch for file", file_name);
321 watch_descriptor = make_watch_descriptor (watchdesc);
323 /* Delete existing watch object. */
324 watch_object = Fassoc (watch_descriptor, watch_list);
325 if (!NILP (watch_object))
326 watch_list = Fdelete (watch_object, watch_list);
328 /* Store watch object in watch list. */
329 watch_object = list3 (watch_descriptor, encoded_file_name, callback);
330 watch_list = Fcons (watch_object, watch_list);
332 return watch_descriptor;
335 DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
336 doc: /* Remove an existing WATCH-DESCRIPTOR.
338 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
340 See inotify_rm_watch(2) for more information.
342 (Lisp_Object watch_descriptor)
344 Lisp_Object watch_object;
345 int wd = XINT (watch_descriptor);
347 if (inotify_rm_watch (inotifyfd, wd) == -1)
348 report_file_notify_error ("Could not rm watch", watch_descriptor);
350 /* Remove watch descriptor from watch list. */
351 watch_object = Fassoc (watch_descriptor, watch_list);
352 if (!NILP (watch_object))
353 watch_list = Fdelete (watch_object, watch_list);
355 /* Cleanup if no more files are watched. */
356 if (NILP (watch_list))
358 emacs_close (inotifyfd);
359 delete_read_fd (inotifyfd);
360 inotifyfd = -1;
363 return Qt;
366 DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
367 doc: /* Check a watch specified by its WATCH-DESCRIPTOR.
369 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
371 A watch can become invalid if the file or directory it watches is
372 deleted, or if the watcher thread exits abnormally for any other
373 reason. Removing the watch by calling `inotify-rm-watch' also makes
374 it invalid. */)
375 (Lisp_Object watch_descriptor)
377 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
378 return NILP (watch_object) ? Qnil : Qt;
381 void
382 syms_of_inotify (void)
384 DEFSYM (Qaccess, "access"); /* IN_ACCESS */
385 DEFSYM (Qattrib, "attrib"); /* IN_ATTRIB */
386 DEFSYM (Qclose_write, "close-write"); /* IN_CLOSE_WRITE */
387 DEFSYM (Qclose_nowrite, "close-nowrite");
388 /* IN_CLOSE_NOWRITE */
389 DEFSYM (Qcreate, "create"); /* IN_CREATE */
390 DEFSYM (Qdelete, "delete"); /* IN_DELETE */
391 DEFSYM (Qdelete_self, "delete-self"); /* IN_DELETE_SELF */
392 DEFSYM (Qmodify, "modify"); /* IN_MODIFY */
393 DEFSYM (Qmove_self, "move-self"); /* IN_MOVE_SELF */
394 DEFSYM (Qmoved_from, "moved-from"); /* IN_MOVED_FROM */
395 DEFSYM (Qmoved_to, "moved-to"); /* IN_MOVED_TO */
396 DEFSYM (Qopen, "open"); /* IN_OPEN */
398 DEFSYM (Qall_events, "all-events"); /* IN_ALL_EVENTS */
399 DEFSYM (Qmove, "move"); /* IN_MOVE */
400 DEFSYM (Qclose, "close"); /* IN_CLOSE */
402 DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */
403 DEFSYM (Qexcl_unlink, "excl-unlink"); /* IN_EXCL_UNLINK */
404 DEFSYM (Qmask_add, "mask-add"); /* IN_MASK_ADD */
405 DEFSYM (Qoneshot, "oneshot"); /* IN_ONESHOT */
406 DEFSYM (Qonlydir, "onlydir"); /* IN_ONLYDIR */
408 DEFSYM (Qignored, "ignored"); /* IN_IGNORED */
409 DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */
410 DEFSYM (Qq_overflow, "q-overflow"); /* IN_Q_OVERFLOW */
411 DEFSYM (Qunmount, "unmount"); /* IN_UNMOUNT */
413 defsubr (&Sinotify_add_watch);
414 defsubr (&Sinotify_rm_watch);
415 defsubr (&Sinotify_valid_p);
417 staticpro (&watch_list);
419 Fprovide (intern_c_string ("inotify"), Qnil);
422 #endif /* HAVE_INOTIFY */