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