Fix problems caught with --enable-gcc-warnings
[emacs.git] / src / inotify.c
blobbe8c1dd7553f024af559d5ef2c781564ed92f07d
1 /* Inotify support for Emacs
3 Copyright (C) 2012-2015 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/>. */
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 "character.h"
29 #include "frame.h" /* Required for termhooks.h. */
30 #include "termhooks.h"
32 #include <errno.h>
33 #include <sys/inotify.h>
34 #include <sys/ioctl.h>
36 /* Ignore bits that might be undefined on old GNU/Linux systems. */
37 #ifndef IN_EXCL_UNLINK
38 # define IN_EXCL_UNLINK 0
39 #endif
40 #ifndef IN_DONT_FOLLOW
41 # define IN_DONT_FOLLOW 0
42 #endif
43 #ifndef IN_ONLYDIR
44 # define IN_ONLYDIR 0
45 #endif
47 /* File handle for inotify. */
48 static int inotifyfd = -1;
50 /* Assoc list of files being watched.
51 Format:
52 (watch-descriptor . callback)
54 static Lisp_Object watch_list;
56 static Lisp_Object
57 make_watch_descriptor (int wd)
59 /* TODO replace this with a Misc Object! */
60 return make_number (wd);
63 static Lisp_Object
64 mask_to_aspects (uint32_t mask) {
65 Lisp_Object aspects = Qnil;
66 if (mask & IN_ACCESS)
67 aspects = Fcons (Qaccess, aspects);
68 if (mask & IN_ATTRIB)
69 aspects = Fcons (Qattrib, aspects);
70 if (mask & IN_CLOSE_WRITE)
71 aspects = Fcons (Qclose_write, aspects);
72 if (mask & IN_CLOSE_NOWRITE)
73 aspects = Fcons (Qclose_nowrite, aspects);
74 if (mask & IN_CREATE)
75 aspects = Fcons (Qcreate, aspects);
76 if (mask & IN_DELETE)
77 aspects = Fcons (Qdelete, aspects);
78 if (mask & IN_DELETE_SELF)
79 aspects = Fcons (Qdelete_self, aspects);
80 if (mask & IN_MODIFY)
81 aspects = Fcons (Qmodify, aspects);
82 if (mask & IN_MOVE_SELF)
83 aspects = Fcons (Qmove_self, aspects);
84 if (mask & IN_MOVED_FROM)
85 aspects = Fcons (Qmoved_from, aspects);
86 if (mask & IN_MOVED_TO)
87 aspects = Fcons (Qmoved_to, aspects);
88 if (mask & IN_OPEN)
89 aspects = Fcons (Qopen, aspects);
90 if (mask & IN_IGNORED)
91 aspects = Fcons (Qignored, aspects);
92 if (mask & IN_ISDIR)
93 aspects = Fcons (Qisdir, aspects);
94 if (mask & IN_Q_OVERFLOW)
95 aspects = Fcons (Qq_overflow, aspects);
96 if (mask & IN_UNMOUNT)
97 aspects = Fcons (Qunmount, aspects);
98 return aspects;
101 static Lisp_Object
102 inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
104 Lisp_Object name = Qnil;
105 if (ev->len > 0)
107 size_t const len = strlen (ev->name);
108 name = make_unibyte_string (ev->name, min (len, ev->len));
109 name = DECODE_FILE (name);
112 return list2 (list4 (make_watch_descriptor (ev->wd),
113 mask_to_aspects (ev->mask),
114 name,
115 make_number (ev->cookie)),
116 XCDR (watch_object));
119 /* This callback is called when the FD is available for read. The inotify
120 events are read from FD and converted into input_events. */
121 static void
122 inotify_callback (int fd, void *_)
124 struct input_event event;
125 Lisp_Object watch_object;
126 int to_read;
127 char *buffer;
128 ssize_t n;
129 size_t i;
131 to_read = 0;
132 if (ioctl (fd, FIONREAD, &to_read) == -1)
133 report_file_notify_error ("Error while retrieving file system events",
134 Qnil);
135 buffer = xmalloc (to_read);
136 n = read (fd, buffer, to_read);
137 if (n < 0)
139 xfree (buffer);
140 report_file_notify_error ("Error while reading file system events", Qnil);
143 EVENT_INIT (event);
144 event.kind = FILE_NOTIFY_EVENT;
146 i = 0;
147 while (i < (size_t)n)
149 struct inotify_event *ev = (struct inotify_event*)&buffer[i];
151 watch_object = Fassoc (make_watch_descriptor (ev->wd), watch_list);
152 if (!NILP (watch_object))
154 event.arg = inotifyevent_to_event (watch_object, ev);
156 /* If event was removed automatically: Drop it from watch list. */
157 if (ev->mask & IN_IGNORED)
158 watch_list = Fdelete (watch_object, watch_list);
160 if (!NILP (event.arg))
161 kbd_buffer_store_event (&event);
164 i += sizeof (*ev) + ev->len;
167 xfree (buffer);
170 static uint32_t
171 symbol_to_inotifymask (Lisp_Object symb)
173 if (EQ (symb, Qaccess))
174 return IN_ACCESS;
175 else if (EQ (symb, Qattrib))
176 return IN_ATTRIB;
177 else if (EQ (symb, Qclose_write))
178 return IN_CLOSE_WRITE;
179 else if (EQ (symb, Qclose_nowrite))
180 return IN_CLOSE_NOWRITE;
181 else if (EQ (symb, Qcreate))
182 return IN_CREATE;
183 else if (EQ (symb, Qdelete))
184 return IN_DELETE;
185 else if (EQ (symb, Qdelete_self))
186 return IN_DELETE_SELF;
187 else if (EQ (symb, Qmodify))
188 return IN_MODIFY;
189 else if (EQ (symb, Qmove_self))
190 return IN_MOVE_SELF;
191 else if (EQ (symb, Qmoved_from))
192 return IN_MOVED_FROM;
193 else if (EQ (symb, Qmoved_to))
194 return IN_MOVED_TO;
195 else if (EQ (symb, Qopen))
196 return IN_OPEN;
197 else if (EQ (symb, Qmove))
198 return IN_MOVE;
199 else if (EQ (symb, Qclose))
200 return IN_CLOSE;
202 else if (EQ (symb, Qdont_follow))
203 return IN_DONT_FOLLOW;
204 else if (EQ (symb, Qexcl_unlink))
205 return IN_EXCL_UNLINK;
206 else if (EQ (symb, Qmask_add))
207 return IN_MASK_ADD;
208 else if (EQ (symb, Qoneshot))
209 return IN_ONESHOT;
210 else if (EQ (symb, Qonlydir))
211 return IN_ONLYDIR;
213 else if (EQ (symb, Qt) || EQ (symb, Qall_events))
214 return IN_ALL_EVENTS;
215 else
217 errno = EINVAL;
218 report_file_notify_error ("Unknown aspect", symb);
222 static uint32_t
223 aspect_to_inotifymask (Lisp_Object aspect)
225 if (CONSP (aspect))
227 Lisp_Object x = aspect;
228 uint32_t mask = 0;
229 while (CONSP (x))
231 mask |= symbol_to_inotifymask (XCAR (x));
232 x = XCDR (x);
234 return mask;
236 else
237 return symbol_to_inotifymask (aspect);
240 DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
241 doc: /* Add a watch for FILE-NAME to inotify.
243 Return a watch descriptor. The watch will look for ASPECT events and
244 invoke CALLBACK when an event occurs.
246 ASPECT might be one of the following symbols or a list of those symbols:
248 access
249 attrib
250 close-write
251 close-nowrite
252 create
253 delete
254 delete-self
255 modify
256 move-self
257 moved-from
258 moved-to
259 open
261 all-events or t
262 move
263 close
265 The following symbols can also be added to a list of aspects:
267 dont-follow
268 excl-unlink
269 mask-add
270 oneshot
271 onlydir
273 Watching a directory is not recursive. CALLBACK is passed a single argument
274 EVENT which contains an event structure of the format
276 (WATCH-DESCRIPTOR ASPECTS NAME COOKIE)
278 WATCH-DESCRIPTOR is the same object that was returned by this function. It can
279 be tested for equality using `equal'. ASPECTS describes the event. It is a
280 list of ASPECT symbols described above and can also contain one of the following
281 symbols
283 ignored
284 isdir
285 q-overflow
286 unmount
288 If a directory is watched then NAME is the name of file that caused the event.
290 COOKIE is an object that can be compared using `equal' to identify two matching
291 renames (moved-from and moved-to).
293 See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
294 is managed internally and there is no corresponding inotify_init. Use
295 `inotify-rm-watch' to remove a watch.
297 (Lisp_Object file_name, Lisp_Object aspect, Lisp_Object callback)
299 uint32_t mask;
300 Lisp_Object watch_object;
301 Lisp_Object encoded_file_name;
302 Lisp_Object watch_descriptor;
303 int watchdesc = -1;
305 CHECK_STRING (file_name);
307 if (inotifyfd < 0)
309 inotifyfd = inotify_init1 (IN_NONBLOCK|IN_CLOEXEC);
310 if (inotifyfd < 0)
311 report_file_notify_error ("File watching is not available", Qnil);
312 watch_list = Qnil;
313 add_read_fd (inotifyfd, &inotify_callback, NULL);
316 mask = aspect_to_inotifymask (aspect);
317 encoded_file_name = ENCODE_FILE (file_name);
318 watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
319 if (watchdesc == -1)
320 report_file_notify_error ("Could not add watch for file", file_name);
322 watch_descriptor = make_watch_descriptor (watchdesc);
324 /* Delete existing watch object. */
325 watch_object = Fassoc (watch_descriptor, watch_list);
326 if (!NILP (watch_object))
327 watch_list = Fdelete (watch_object, watch_list);
329 /* Store watch object in watch list. */
330 watch_object = Fcons (watch_descriptor, callback);
331 watch_list = Fcons (watch_object, watch_list);
333 return watch_descriptor;
336 DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
337 doc: /* Remove an existing WATCH-DESCRIPTOR.
339 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
341 See inotify_rm_watch(2) for more information.
343 (Lisp_Object watch_descriptor)
345 Lisp_Object watch_object;
346 int wd = XINT (watch_descriptor);
348 if (inotify_rm_watch (inotifyfd, wd) == -1)
349 report_file_notify_error ("Could not rm watch", watch_descriptor);
351 /* Remove watch descriptor from watch list. */
352 watch_object = Fassoc (watch_descriptor, watch_list);
353 if (!NILP (watch_object))
354 watch_list = Fdelete (watch_object, watch_list);
356 /* Cleanup if no more files are watched. */
357 if (NILP (watch_list))
359 emacs_close (inotifyfd);
360 delete_read_fd (inotifyfd);
361 inotifyfd = -1;
364 return Qt;
367 DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
368 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
370 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
372 A watch can become invalid if the file or directory it watches is
373 deleted, or if the watcher thread exits abnormally for any other
374 reason. Removing the watch by calling `inotify-rm-watch' also makes
375 it invalid. */)
376 (Lisp_Object watch_descriptor)
378 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
379 return NILP (watch_object) ? Qnil : Qt;
382 void
383 syms_of_inotify (void)
385 DEFSYM (Qaccess, "access"); /* IN_ACCESS */
386 DEFSYM (Qattrib, "attrib"); /* IN_ATTRIB */
387 DEFSYM (Qclose_write, "close-write"); /* IN_CLOSE_WRITE */
388 DEFSYM (Qclose_nowrite, "close-nowrite");
389 /* IN_CLOSE_NOWRITE */
390 DEFSYM (Qcreate, "create"); /* IN_CREATE */
391 DEFSYM (Qdelete, "delete"); /* IN_DELETE */
392 DEFSYM (Qdelete_self, "delete-self"); /* IN_DELETE_SELF */
393 DEFSYM (Qmodify, "modify"); /* IN_MODIFY */
394 DEFSYM (Qmove_self, "move-self"); /* IN_MOVE_SELF */
395 DEFSYM (Qmoved_from, "moved-from"); /* IN_MOVED_FROM */
396 DEFSYM (Qmoved_to, "moved-to"); /* IN_MOVED_TO */
397 DEFSYM (Qopen, "open"); /* IN_OPEN */
399 DEFSYM (Qall_events, "all-events"); /* IN_ALL_EVENTS */
400 DEFSYM (Qmove, "move"); /* IN_MOVE */
401 DEFSYM (Qclose, "close"); /* IN_CLOSE */
403 DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */
404 DEFSYM (Qexcl_unlink, "excl-unlink"); /* IN_EXCL_UNLINK */
405 DEFSYM (Qmask_add, "mask-add"); /* IN_MASK_ADD */
406 DEFSYM (Qoneshot, "oneshot"); /* IN_ONESHOT */
407 DEFSYM (Qonlydir, "onlydir"); /* IN_ONLYDIR */
409 DEFSYM (Qignored, "ignored"); /* IN_IGNORED */
410 DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */
411 DEFSYM (Qq_overflow, "q-overflow"); /* IN_Q_OVERFLOW */
412 DEFSYM (Qunmount, "unmount"); /* IN_UNMOUNT */
414 defsubr (&Sinotify_add_watch);
415 defsubr (&Sinotify_rm_watch);
416 defsubr (&Sinotify_valid_p);
418 staticpro (&watch_list);
420 Fprovide (intern_c_string ("inotify"), Qnil);
423 #endif /* HAVE_INOTIFY */