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