]> code.delx.au - gnu-emacs/blob - src/inotify.c
Add a note how to use `tramp-own-remote-path'
[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 (at
10 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: (watch-descriptor name callback)
50 */
51 static Lisp_Object watch_list;
52
53 static Lisp_Object
54 make_watch_descriptor (int wd)
55 {
56 /* TODO replace this with a Misc Object! */
57 return make_number (wd);
58 }
59
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;
96 }
97
98 static Lisp_Object
99 inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
100 {
101 Lisp_Object name = Qnil;
102 if (ev->len > 0)
103 {
104 size_t const len = strlen (ev->name);
105 name = make_unibyte_string (ev->name, min (len, ev->len));
106 name = DECODE_FILE (name);
107 }
108 else
109 name = XCAR (XCDR (watch_object));
110
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));
116 }
117
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 *_)
122 {
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;
129
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)
137 {
138 xfree (buffer);
139 report_file_notify_error ("Error while reading file system events", Qnil);
140 }
141
142 EVENT_INIT (event);
143 event.kind = FILE_NOTIFY_EVENT;
144
145 i = 0;
146 while (i < (size_t)n)
147 {
148 struct inotify_event *ev = (struct inotify_event*)&buffer[i];
149
150 watch_object = Fassoc (make_watch_descriptor (ev->wd), watch_list);
151 if (!NILP (watch_object))
152 {
153 event.arg = inotifyevent_to_event (watch_object, ev);
154
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);
158
159 if (!NILP (event.arg))
160 kbd_buffer_store_event (&event);
161 }
162
163 i += sizeof (*ev) + ev->len;
164 }
165
166 xfree (buffer);
167 }
168
169 static uint32_t
170 symbol_to_inotifymask (Lisp_Object symb)
171 {
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;
200
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;
211
212 else if (EQ (symb, Qt) || EQ (symb, Qall_events))
213 return IN_ALL_EVENTS;
214 else
215 {
216 errno = EINVAL;
217 report_file_notify_error ("Unknown aspect", symb);
218 }
219 }
220
221 static uint32_t
222 aspect_to_inotifymask (Lisp_Object aspect)
223 {
224 if (CONSP (aspect))
225 {
226 Lisp_Object x = aspect;
227 uint32_t mask = 0;
228 while (CONSP (x))
229 {
230 mask |= symbol_to_inotifymask (XCAR (x));
231 x = XCDR (x);
232 }
233 return mask;
234 }
235 else
236 return symbol_to_inotifymask (aspect);
237 }
238
239 DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
240 doc: /* Add a watch for FILE-NAME to inotify.
241
242 Return a watch descriptor. The watch will look for ASPECT events and
243 invoke CALLBACK when an event occurs.
244
245 ASPECT might be one of the following symbols or a list of those symbols:
246
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
259
260 all-events or t
261 move
262 close
263
264 The following symbols can also be added to a list of aspects:
265
266 dont-follow
267 excl-unlink
268 mask-add
269 oneshot
270 onlydir
271
272 Watching a directory is not recursive. CALLBACK is passed a single argument
273 EVENT which contains an event structure of the format
274
275 \(WATCH-DESCRIPTOR ASPECTS NAME COOKIE)
276
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
281
282 ignored
283 isdir
284 q-overflow
285 unmount
286
287 If a directory is watched then NAME is the name of file that caused the event.
288
289 COOKIE is an object that can be compared using `equal' to identify two matching
290 renames (moved-from and moved-to).
291
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.
295 */)
296 (Lisp_Object file_name, Lisp_Object aspect, Lisp_Object callback)
297 {
298 uint32_t mask;
299 Lisp_Object watch_object;
300 Lisp_Object encoded_file_name;
301 Lisp_Object watch_descriptor;
302 int watchdesc = -1;
303
304 CHECK_STRING (file_name);
305
306 if (inotifyfd < 0)
307 {
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);
313 }
314
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);
320
321 watch_descriptor = make_watch_descriptor (watchdesc);
322
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);
327
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);
331
332 return watch_descriptor;
333 }
334
335 DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
336 doc: /* Remove an existing WATCH-DESCRIPTOR.
337
338 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
339
340 See inotify_rm_watch(2) for more information.
341 */)
342 (Lisp_Object watch_descriptor)
343 {
344 Lisp_Object watch_object;
345 int wd = XINT (watch_descriptor);
346
347 if (inotify_rm_watch (inotifyfd, wd) == -1)
348 report_file_notify_error ("Could not rm watch", watch_descriptor);
349
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);
354
355 /* Cleanup if no more files are watched. */
356 if (NILP (watch_list))
357 {
358 emacs_close (inotifyfd);
359 delete_read_fd (inotifyfd);
360 inotifyfd = -1;
361 }
362
363 return Qt;
364 }
365
366 DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
367 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
368
369 WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
370
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)
376 {
377 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
378 return NILP (watch_object) ? Qnil : Qt;
379 }
380
381 void
382 syms_of_inotify (void)
383 {
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 */
397
398 DEFSYM (Qall_events, "all-events"); /* IN_ALL_EVENTS */
399 DEFSYM (Qmove, "move"); /* IN_MOVE */
400 DEFSYM (Qclose, "close"); /* IN_CLOSE */
401
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 */
407
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 */
412
413 defsubr (&Sinotify_add_watch);
414 defsubr (&Sinotify_rm_watch);
415 defsubr (&Sinotify_valid_p);
416
417 staticpro (&watch_list);
418
419 Fprovide (intern_c_string ("inotify"), Qnil);
420 }
421
422 #endif /* HAVE_INOTIFY */