1 /* Filesystem notifications support with glib API.
2 Copyright (C) 2013-2015 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
23 #include <sys/types.h>
24 #include <sys/event.h>
31 /* File handle for kqueue. */
32 static int kqueuefd
= -1;
34 /* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */
35 static Lisp_Object watch_list
;
37 /* This is the callback function for arriving input on kqueuefd. It
38 shall create a Lisp event, and put it into Emacs input queue. */
40 kqueue_callback (int fd
, void *data
)
44 struct input_event event
;
45 Lisp_Object monitor_object
, watch_object
, file
, callback
, actions
;
48 int ret
= kevent (kqueuefd
, NULL
, 0, &kev
, 1, NULL
);
50 /* All events read. */
54 /* Determine file name and callback function. */
55 monitor_object
= make_number (kev
.ident
);
56 watch_object
= assq_no_quit (monitor_object
, watch_list
);
58 if (CONSP (watch_object
)) {
59 file
= XCAR (XCDR (watch_object
));
60 callback
= XCAR (XCDR (XCDR (XCDR (watch_object
))));
65 /* Determine event actions. */
67 if (kev
.fflags
& NOTE_DELETE
)
68 actions
= Fcons (Qdelete
, actions
);
69 if (kev
.fflags
& NOTE_WRITE
)
70 actions
= Fcons (Qwrite
, actions
);
71 if (kev
.fflags
& NOTE_EXTEND
)
72 actions
= Fcons (Qextend
, actions
);
73 if (kev
.fflags
& NOTE_ATTRIB
)
74 actions
= Fcons (Qattrib
, actions
);
75 if (kev
.fflags
& NOTE_LINK
)
76 actions
= Fcons (Qlink
, actions
);
77 if (kev
.fflags
& NOTE_RENAME
)
78 actions
= Fcons (Qrename
, actions
);
80 if (! NILP (actions
)) {
81 /* Construct an event. */
83 event
.kind
= FILE_NOTIFY_EVENT
;
84 event
.frame_or_window
= Qnil
;
85 event
.arg
= list2 (Fcons (monitor_object
,
86 Fcons (actions
, Fcons (file
, Qnil
))),
89 /* Store it into the input event queue. */
90 kbd_buffer_store_event (&event
);
93 /* Cancel monitor if file or directory is deleted. */
94 if (kev
.fflags
& (NOTE_DELETE
| NOTE_RENAME
))
95 Fkqueue_rm_watch (monitor_object
);
100 DEFUN ("kqueue-add-watch", Fkqueue_add_watch
, Skqueue_add_watch
, 3, 3, 0,
101 doc
: /* Add a watch for filesystem events pertaining to FILE.
103 This arranges for filesystem events pertaining to FILE to be reported
104 to Emacs. Use `kqueue-rm-watch' to cancel the watch.
106 Returned value is a descriptor for the added watch. If the file cannot be
107 watched for some reason, this function signals a `file-notify-error' error.
109 FLAGS is a list of events to be watched for. It can include the
112 `delete' -- FILE was deleted
113 `write' -- FILE has changed
114 `extend' -- FILE was extended
115 `attrib' -- a FILE attribute was changed
116 `link' -- a FILE's link count was changed
117 `rename' -- FILE was moved to FILE1
119 When any event happens, Emacs will call the CALLBACK function passing
120 it a single argument EVENT, which is of the form
122 (DESCRIPTOR ACTIONS FILE [FILE1])
124 DESCRIPTOR is the same object as the one returned by this function.
125 ACTIONS is a list of events.
127 FILE is the name of the file whose event is being reported. FILE1
128 will be reported only in case of the `rename' event. */)
129 (Lisp_Object file
, Lisp_Object flags
, Lisp_Object callback
)
131 Lisp_Object watch_object
;
136 /* Check parameters. */
138 file
= Fdirectory_file_name (Fexpand_file_name (file
, Qnil
));
139 if (NILP (Ffile_exists_p (file
)))
140 report_file_error ("File does not exist", file
);
142 /* TODO: Directories shall be supported as well. */
143 if (! NILP (Ffile_directory_p (file
)))
144 report_file_error ("Directory watching is not supported (yet)", file
);
148 if (! FUNCTIONP (callback
))
149 wrong_type_argument (Qinvalid_function
, callback
);
153 /* Create kqueue descriptor. */
154 kqueuefd
= kqueue ();
156 report_file_notify_error ("File watching is not available", Qnil
);
158 /* Start monitoring for possible I/O. */
159 add_read_fd (kqueuefd
, kqueue_callback
, NULL
); //data);
165 file
= ENCODE_FILE (file
);
166 fd
= emacs_open (SSDATA (file
), O_NONBLOCK
| O_BINARY
| O_RDONLY
, 0);
168 report_file_error ("File cannot be opened", file
);
170 /* Assemble filter flags */
171 if (! NILP (Fmember (Qdelete
, flags
))) fflags
|= NOTE_DELETE
;
172 if (! NILP (Fmember (Qwrite
, flags
))) fflags
|= NOTE_WRITE
;
173 if (! NILP (Fmember (Qextend
, flags
))) fflags
|= NOTE_EXTEND
;
174 if (! NILP (Fmember (Qattrib
, flags
))) fflags
|= NOTE_ATTRIB
;
175 if (! NILP (Fmember (Qlink
, flags
))) fflags
|= NOTE_LINK
;
176 if (! NILP (Fmember (Qrename
, flags
))) fflags
|= NOTE_RENAME
;
178 /* Register event. */
179 EV_SET (&ev
, fd
, EVFILT_VNODE
, EV_ADD
| EV_ENABLE
| EV_CLEAR
,
182 if (kevent (kqueuefd
, &ev
, 1, NULL
, 0, NULL
) < 0)
183 report_file_error ("Cannot watch file", file
);
185 /* Store watch object in watch list. */
186 Lisp_Object watch_descriptor
= make_number (fd
);
187 watch_object
= list4 (watch_descriptor
, file
, flags
, callback
);
188 watch_list
= Fcons (watch_object
, watch_list
);
190 return watch_descriptor
;
193 DEFUN ("kqueue-rm-watch", Fkqueue_rm_watch
, Skqueue_rm_watch
, 1, 1, 0,
194 doc
: /* Remove an existing WATCH-DESCRIPTOR.
196 WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'. */)
197 (Lisp_Object watch_descriptor
)
199 Lisp_Object watch_object
= assq_no_quit (watch_descriptor
, watch_list
);
201 if (! CONSP (watch_object
))
202 xsignal2 (Qfile_notify_error
, build_string ("Not a watch descriptor"),
205 eassert (INTEGERP (watch_descriptor
));
206 int fd
= XINT (watch_descriptor
);
210 /* Remove watch descriptor from watch list. */
211 watch_list
= Fdelq (watch_object
, watch_list
);
213 if (NILP (watch_list
) && (kqueuefd
>= 0)) {
214 delete_read_fd (kqueuefd
);
215 emacs_close (kqueuefd
);
222 DEFUN ("kqueue-valid-p", Fkqueue_valid_p
, Skqueue_valid_p
, 1, 1, 0,
223 doc
: /* "Check a watch specified by its WATCH-DESCRIPTOR.
225 WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'.
227 A watch can become invalid if the file or directory it watches is
228 deleted, or if the watcher thread exits abnormally for any other
229 reason. Removing the watch by calling `kqueue-rm-watch' also makes it
231 (Lisp_Object watch_descriptor
)
233 return NILP (assq_no_quit (watch_descriptor
, watch_list
)) ? Qnil
: Qt
;
238 globals_of_kqueue (void)
244 syms_of_kqueue (void)
246 defsubr (&Skqueue_add_watch
);
247 defsubr (&Skqueue_rm_watch
);
248 defsubr (&Skqueue_valid_p
);
251 DEFSYM (Qdelete
, "delete"); /* NOTE_DELETE */
252 DEFSYM (Qwrite
, "write"); /* NOTE_WRITE */
253 DEFSYM (Qextend
, "extend"); /* NOTE_EXTEND */
254 DEFSYM (Qattrib
, "attrib"); /* NOTE_ATTRIB */
255 DEFSYM (Qlink
, "link"); /* NOTE_LINK */
256 DEFSYM (Qrename
, "rename"); /* NOTE_RENAME */
258 staticpro (&watch_list
);
260 Fprovide (intern_c_string ("kqueue"), Qnil
);
263 #endif /* HAVE_KQUEUE */
266 * Implement watching directories.
267 * Add FILE1 in case of `rename'. */
270 * https://bugs.launchpad.net/ubuntu/+source/libkqueue/+bug/1514837
271 prevents tests on Ubuntu. */