This source file includes following definitions.
- mask_to_aspects
- symbol_to_inotifymask
- aspect_to_inotifymask
- inotifyevent_to_event
- add_watch
- find_descriptor
- remove_descriptor
- remove_watch
- inotify_callback
- valid_watch_descriptor
- DEFUN
- DEFUN
- DEFUN
- DEFUN
- syms_of_inotify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <config.h>
21
22 #include "lisp.h"
23 #include "coding.h"
24 #include "process.h"
25 #include "keyboard.h"
26 #include "termhooks.h"
27
28 #include <errno.h>
29 #include <sys/inotify.h>
30 #include <sys/ioctl.h>
31
32
33 #ifndef IN_EXCL_UNLINK
34 # define IN_EXCL_UNLINK 0
35 #endif
36 #ifndef IN_DONT_FOLLOW
37 # define IN_DONT_FOLLOW 0
38 #endif
39 #ifndef IN_ONLYDIR
40 # define IN_ONLYDIR 0
41 #endif
42
43 #ifdef HAVE_ANDROID
44 #include "android.h"
45 #endif
46
47
48 static int inotifyfd = -1;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 static Lisp_Object watch_list;
74
75 static Lisp_Object
76 mask_to_aspects (uint32_t mask)
77 {
78 Lisp_Object aspects = Qnil;
79 if (mask & IN_ACCESS)
80 aspects = Fcons (Qaccess, aspects);
81 if (mask & IN_ATTRIB)
82 aspects = Fcons (Qattrib, aspects);
83 if (mask & IN_CLOSE_WRITE)
84 aspects = Fcons (Qclose_write, aspects);
85 if (mask & IN_CLOSE_NOWRITE)
86 aspects = Fcons (Qclose_nowrite, aspects);
87 if (mask & IN_CREATE)
88 aspects = Fcons (Qcreate, aspects);
89 if (mask & IN_DELETE)
90 aspects = Fcons (Qdelete, aspects);
91 if (mask & IN_DELETE_SELF)
92 aspects = Fcons (Qdelete_self, aspects);
93 if (mask & IN_MODIFY)
94 aspects = Fcons (Qmodify, aspects);
95 if (mask & IN_MOVE_SELF)
96 aspects = Fcons (Qmove_self, aspects);
97 if (mask & IN_MOVED_FROM)
98 aspects = Fcons (Qmoved_from, aspects);
99 if (mask & IN_MOVED_TO)
100 aspects = Fcons (Qmoved_to, aspects);
101 if (mask & IN_OPEN)
102 aspects = Fcons (Qopen, aspects);
103 if (mask & IN_IGNORED)
104 aspects = Fcons (Qignored, aspects);
105 if (mask & IN_ISDIR)
106 aspects = Fcons (Qisdir, aspects);
107 if (mask & IN_Q_OVERFLOW)
108 aspects = Fcons (Qq_overflow, aspects);
109 if (mask & IN_UNMOUNT)
110 aspects = Fcons (Qunmount, aspects);
111 return aspects;
112 }
113
114 static uint32_t
115 symbol_to_inotifymask (Lisp_Object symb)
116 {
117 if (EQ (symb, Qaccess))
118 return IN_ACCESS;
119 else if (EQ (symb, Qattrib))
120 return IN_ATTRIB;
121 else if (EQ (symb, Qclose_write))
122 return IN_CLOSE_WRITE;
123 else if (EQ (symb, Qclose_nowrite))
124 return IN_CLOSE_NOWRITE;
125 else if (EQ (symb, Qcreate))
126 return IN_CREATE;
127 else if (EQ (symb, Qdelete))
128 return IN_DELETE;
129 else if (EQ (symb, Qdelete_self))
130 return IN_DELETE_SELF;
131 else if (EQ (symb, Qmodify))
132 return IN_MODIFY;
133 else if (EQ (symb, Qmove_self))
134 return IN_MOVE_SELF;
135 else if (EQ (symb, Qmoved_from))
136 return IN_MOVED_FROM;
137 else if (EQ (symb, Qmoved_to))
138 return IN_MOVED_TO;
139 else if (EQ (symb, Qopen))
140 return IN_OPEN;
141 else if (EQ (symb, Qmove))
142 return IN_MOVE;
143 else if (EQ (symb, Qclose))
144 return IN_CLOSE;
145
146 else if (EQ (symb, Qdont_follow))
147 return IN_DONT_FOLLOW;
148 else if (EQ (symb, Qonlydir))
149 return IN_ONLYDIR;
150
151 else if (EQ (symb, Qt) || EQ (symb, Qall_events))
152 return IN_ALL_EVENTS;
153 else
154 {
155 errno = EINVAL;
156 report_file_notify_error ("Unknown aspect", symb);
157 }
158 }
159
160 static uint32_t
161 aspect_to_inotifymask (Lisp_Object aspect)
162 {
163 if (CONSP (aspect) || NILP (aspect))
164 {
165 Lisp_Object x = aspect;
166 uint32_t mask = 0;
167 FOR_EACH_TAIL (x)
168 mask |= symbol_to_inotifymask (XCAR (x));
169 CHECK_LIST_END (x, aspect);
170 return mask;
171 }
172 else
173 return symbol_to_inotifymask (aspect);
174 }
175
176 static Lisp_Object
177 inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev)
178 {
179 Lisp_Object name;
180 uint32_t mask;
181 CONS_TO_INTEGER (Fnth (make_fixnum (3), watch), uint32_t, mask);
182
183 if (! (mask & ev->mask))
184 return Qnil;
185
186 if (ev->len > 0)
187 {
188 name = make_unibyte_string (ev->name, strnlen (ev->name, ev->len));
189 name = DECODE_FILE (name);
190 }
191 else
192 name = XCAR (XCDR (watch));
193
194 return list2 (list4 (Fcons (INT_TO_INTEGER (ev->wd), XCAR (watch)),
195 mask_to_aspects (ev->mask),
196 name,
197 INT_TO_INTEGER (ev->cookie)),
198 Fnth (make_fixnum (2), watch));
199 }
200
201
202
203
204 static Lisp_Object
205 add_watch (int wd, Lisp_Object filename,
206 uint32_t imask, Lisp_Object callback)
207 {
208 Lisp_Object descriptor = INT_TO_INTEGER (wd);
209 Lisp_Object tail = assoc_no_quit (descriptor, watch_list);
210 Lisp_Object watch, watch_id;
211 Lisp_Object mask = INT_TO_INTEGER (imask);
212
213 EMACS_INT id = 0;
214 if (NILP (tail))
215 {
216 tail = list1 (descriptor);
217 watch_list = Fcons (tail, watch_list);
218 }
219 else
220 {
221
222
223 for (; ! NILP (XCDR (tail)); tail = XCDR (tail), id++)
224 if (!BASE_EQ (XCAR (XCAR (XCDR (tail))), make_fixnum (id)))
225 break;
226 if (MOST_POSITIVE_FIXNUM < id)
227 emacs_abort ();
228 }
229
230
231
232
233 watch_id = make_fixnum (id);
234 watch = list4 (watch_id, filename, callback, mask);
235 XSETCDR (tail, Fcons (watch, XCDR (tail)));
236
237 return Fcons (descriptor, watch_id);
238 }
239
240
241
242
243
244
245
246
247 static Lisp_Object
248 find_descriptor (Lisp_Object descriptor)
249 {
250 Lisp_Object tail, prevtail = Qt;
251 for (tail = watch_list; !NILP (tail); prevtail = tail, tail = XCDR (tail))
252 if (equal_no_quit (XCAR (XCAR (tail)), descriptor))
253 return prevtail;
254 return Qnil;
255 }
256
257
258
259
260
261 static void
262 remove_descriptor (Lisp_Object prevtail, bool invalid_p)
263 {
264 Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list;
265
266 int inotify_errno = 0;
267 if (! invalid_p)
268 {
269 int wd;
270 CONS_TO_INTEGER (XCAR (XCAR (tail)), int, wd);
271 if (inotify_rm_watch (inotifyfd, wd) != 0)
272 inotify_errno = errno;
273 }
274
275 if (CONSP (prevtail))
276 XSETCDR (prevtail, XCDR (tail));
277 else
278 {
279 watch_list = XCDR (tail);
280 if (NILP (watch_list))
281 {
282 delete_read_fd (inotifyfd);
283 emacs_close (inotifyfd);
284 inotifyfd = -1;
285 }
286 }
287
288 if (inotify_errno != 0)
289 {
290 errno = inotify_errno;
291 report_file_notify_error ("Could not rm watch", XCAR (tail));
292 }
293 }
294
295
296 static void
297 remove_watch (Lisp_Object descriptor, Lisp_Object id)
298 {
299 Lisp_Object prevtail = find_descriptor (descriptor);
300 if (NILP (prevtail))
301 return;
302
303 Lisp_Object elt = XCAR (CONSP (prevtail) ? XCDR (prevtail) : watch_list);
304 for (Lisp_Object prev = elt; !NILP (XCDR (prev)); prev = XCDR (prev))
305 if (EQ (id, XCAR (XCAR (XCDR (prev)))))
306 {
307 XSETCDR (prev, XCDR (XCDR (prev)));
308 if (NILP (XCDR (elt)))
309 remove_descriptor (prevtail, false);
310 break;
311 }
312 }
313
314
315
316 static void
317 inotify_callback (int fd, void *_)
318 {
319 int to_read;
320 if (ioctl (fd, FIONREAD, &to_read) < 0)
321 report_file_notify_error ("Error while retrieving file system events",
322 Qnil);
323 USE_SAFE_ALLOCA;
324 char *buffer = SAFE_ALLOCA (to_read);
325 ssize_t n = read (fd, buffer, to_read);
326 if (n < 0)
327 report_file_notify_error ("Error while reading file system events", Qnil);
328
329 struct input_event event;
330 EVENT_INIT (event);
331 event.kind = FILE_NOTIFY_EVENT;
332
333 for (ssize_t i = 0; i < n; )
334 {
335 struct inotify_event *ev = (struct inotify_event *) &buffer[i];
336 Lisp_Object descriptor = INT_TO_INTEGER (ev->wd);
337 Lisp_Object prevtail = find_descriptor (descriptor);
338
339 if (! NILP (prevtail))
340 {
341 Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list;
342 for (Lisp_Object watches = XCDR (XCAR (tail)); ! NILP (watches);
343 watches = XCDR (watches))
344 {
345 event.arg = inotifyevent_to_event (XCAR (watches), ev);
346 if (!NILP (event.arg))
347 kbd_buffer_store_event (&event);
348 }
349
350 if (ev->mask & IN_IGNORED)
351 remove_descriptor (prevtail, true);
352 }
353 i += sizeof (*ev) + ev->len;
354 }
355
356 SAFE_FREE ();
357 }
358
359 DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
360 doc:
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419 )
420 (Lisp_Object filename, Lisp_Object aspect, Lisp_Object callback)
421 {
422 Lisp_Object encoded_file_name;
423 int wd = -1;
424 uint32_t imask = aspect_to_inotifymask (aspect);
425 uint32_t mask = imask | IN_MASK_ADD | IN_EXCL_UNLINK;
426 char *name;
427
428 CHECK_STRING (filename);
429
430 if (inotifyfd < 0)
431 {
432 inotifyfd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
433 if (inotifyfd < 0)
434 report_file_notify_error ("File watching is not available", Qnil);
435 watch_list = Qnil;
436 add_read_fd (inotifyfd, &inotify_callback, NULL);
437 }
438
439 encoded_file_name = ENCODE_FILE (filename);
440 name = SSDATA (encoded_file_name);
441
442 #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
443
444
445
446
447 if (android_is_special_directory (name, "/assets")
448 || android_is_special_directory (name, "/content"))
449 return Qnil;
450 #endif
451
452 wd = inotify_add_watch (inotifyfd, name, mask);
453 if (wd < 0)
454 report_file_notify_error ("Could not add watch for file", filename);
455
456 return add_watch (wd, filename, imask, callback);
457 }
458
459 static bool
460 valid_watch_descriptor (Lisp_Object wd)
461 {
462 return (CONSP (wd)
463 && (RANGED_FIXNUMP (0, XCAR (wd), INT_MAX)
464 || (CONSP (XCAR (wd))
465 && RANGED_FIXNUMP ((MOST_POSITIVE_FIXNUM >> 16) + 1,
466 XCAR (XCAR (wd)), INT_MAX >> 16)
467 && RANGED_FIXNUMP (0, XCDR (XCAR (wd)), (1 << 16) - 1)))
468 && FIXNATP (XCDR (wd)));
469 }
470
471 DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
472 doc:
473
474
475
476 )
477 (Lisp_Object watch_descriptor)
478 {
479
480 Lisp_Object descriptor, id;
481
482 if (! valid_watch_descriptor (watch_descriptor))
483 report_file_notify_error ("Invalid descriptor ", watch_descriptor);
484
485 descriptor = XCAR (watch_descriptor);
486 id = XCDR (watch_descriptor);
487 remove_watch (descriptor, id);
488
489 return Qt;
490 }
491
492 DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
493 doc:
494
495
496
497
498
499
500 )
501 (Lisp_Object watch_descriptor)
502 {
503 if (! valid_watch_descriptor (watch_descriptor))
504 return Qnil;
505 Lisp_Object tail = assoc_no_quit (XCAR (watch_descriptor), watch_list);
506 if (NILP (tail))
507 return Qnil;
508 Lisp_Object watch = assq_no_quit (XCDR (watch_descriptor), XCDR (tail));
509 return ! NILP (watch) ? Qt : Qnil;
510 }
511
512 #ifdef INOTIFY_DEBUG
513 DEFUN ("inotify-watch-list", Finotify_watch_list, Sinotify_watch_list, 0, 0, 0,
514 doc: )
515 {
516 return Fcopy_sequence (watch_list);
517 }
518
519 DEFUN ("inotify-allocated-p", Finotify_allocated_p, Sinotify_allocated_p, 0, 0, 0,
520 doc: )
521 {
522 return inotifyfd < 0 ? Qnil : Qt;
523 }
524 #endif
525
526 void
527 syms_of_inotify (void)
528 {
529 DEFSYM (Qaccess, "access");
530 DEFSYM (Qattrib, "attrib");
531 DEFSYM (Qclose_write, "close-write");
532 DEFSYM (Qclose_nowrite, "close-nowrite");
533
534 DEFSYM (Qcreate, "create");
535 DEFSYM (Qdelete, "delete");
536 DEFSYM (Qdelete_self, "delete-self");
537 DEFSYM (Qmodify, "modify");
538 DEFSYM (Qmove_self, "move-self");
539 DEFSYM (Qmoved_from, "moved-from");
540 DEFSYM (Qmoved_to, "moved-to");
541 DEFSYM (Qopen, "open");
542
543 DEFSYM (Qall_events, "all-events");
544 DEFSYM (Qmove, "move");
545 DEFSYM (Qclose, "close");
546
547 DEFSYM (Qdont_follow, "dont-follow");
548 DEFSYM (Qonlydir, "onlydir");
549
550 #if 0
551
552 DEFSYM (Qignored, "ignored");
553 #endif
554 DEFSYM (Qisdir, "isdir");
555 DEFSYM (Qq_overflow, "q-overflow");
556 DEFSYM (Qunmount, "unmount");
557
558 defsubr (&Sinotify_add_watch);
559 defsubr (&Sinotify_rm_watch);
560 defsubr (&Sinotify_valid_p);
561
562 #ifdef INOTIFY_DEBUG
563 defsubr (&Sinotify_watch_list);
564 defsubr (&Sinotify_allocated_p);
565 #endif
566 staticpro (&watch_list);
567
568 Fprovide (intern_c_string ("inotify"), Qnil);
569 }