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/event.h>
30 /* File handle for kqueue. */
31 static int kqueuefd
= -1;
33 /* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */
34 static Lisp_Object watch_list
;
36 /* This is the callback function for arriving input on kqueuefd. It
37 shall create a Lisp event, and put it into Emacs input queue. */
39 kqueue_callback (int fd
, void *data
)
43 struct input_event event
;
44 Lisp_Object monitor_object
, watch_object
, name
, callback
, actions
;
46 static const struct timespec nullts
= { 0, 0 };
47 int ret
= kevent (kqueuefd
, NULL
, 0, &kev
, 1, NULL
);
49 /* All events read. */
53 /* Determine file name and callback function. */
54 monitor_object
= make_number (kev
.ident
);
55 watch_object
= assq_no_quit (monitor_object
, watch_list
);
57 if (CONSP (watch_object
)) {
58 name
= XCAR (XCDR (watch_object
));
59 callback
= XCAR (XCDR (XCDR (XCDR (watch_object
))));
64 /* Determine event actions. */
66 if (kev
.fflags
& NOTE_DELETE
)
67 actions
= Fcons (Qdelete
, actions
);
68 if (kev
.fflags
& NOTE_WRITE
)
69 actions
= Fcons (Qwrite
, actions
);
70 if (kev
.fflags
& NOTE_EXTEND
)
71 actions
= Fcons (Qextend
, actions
);
72 if (kev
.fflags
& NOTE_ATTRIB
)
73 actions
= Fcons (Qattrib
, actions
);
74 if (kev
.fflags
& NOTE_LINK
)
75 actions
= Fcons (Qlink
, actions
);
76 if (kev
.fflags
& NOTE_RENAME
)
77 actions
= Fcons (Qrename
, actions
);
79 if (!NILP (actions
)) {
80 /* Construct an event. */
82 event
.kind
= FILE_NOTIFY_EVENT
;
83 event
.frame_or_window
= Qnil
;
84 event
.arg
= list2 (Fcons (monitor_object
,
85 Fcons (actions
, Fcons (name
, Qnil
))),
88 /* Store it into the input event queue. */
89 kbd_buffer_store_event (&event
);
92 /* Cancel monitor if file or directory is deleted. */
93 /* TODO: Implement it. */
98 DEFUN ("kqueue-add-watch", Fkqueue_add_watch
, Skqueue_add_watch
, 3, 3, 0,
99 doc
: /* Add a watch for filesystem events pertaining to FILE.
101 This arranges for filesystem events pertaining to FILE to be reported
102 to Emacs. Use `kqueue-rm-watch' to cancel the watch.
104 Value is a descriptor for the added watch. If the file cannot be
105 watched for some reason, this function signals a `file-notify-error' error.
107 FLAGS is a list of events to be watched for. It can include the
110 `delete' -- FILE was deleted
111 `write' -- FILE has changed
112 `extend' -- FILE was extended
113 `attrib' -- a FILE attribute was changed
114 `link' -- a FILE's link count was changed
115 `rename' -- FILE was moved to FILE1
117 When any event happens, Emacs will call the CALLBACK function passing
118 it a single argument EVENT, which is of the form
120 (DESCRIPTOR ACTIONS FILE [FILE1])
122 DESCRIPTOR is the same object as the one returned by this function.
123 ACTIONS is a list of events.
125 FILE is the name of the file whose event is being reported. FILE1
126 will be reported only in case of the `rename' event. */)
127 (Lisp_Object file
, Lisp_Object flags
, Lisp_Object callback
)
129 Lisp_Object watch_object
;
134 /* Check parameters. */
136 file
= Fdirectory_file_name (Fexpand_file_name (file
, Qnil
));
137 if (NILP (Ffile_exists_p (file
)))
138 report_file_error ("File does not exist", file
);
140 /* TODO: Directories shall be supported as well. */
141 if (!NILP (Ffile_directory_p (file
)))
142 report_file_error ("Directory watching is not supported (yet)", file
);
146 if (!FUNCTIONP (callback
))
147 wrong_type_argument (Qinvalid_function
, callback
);
151 /* Create kqueue descriptor. */
152 kqueuefd
= kqueue ();
154 report_file_notify_error ("File watching is not available", Qnil
);
156 /* Start monitoring for possible I/O. */
157 add_read_fd (kqueuefd
, kqueue_callback
, NULL
); //data);
163 file
= ENCODE_FILE (file
);
164 fd
= emacs_open (SSDATA (file
), O_NONBLOCK
| O_BINARY
| O_RDONLY
, 0);
166 report_file_error ("File cannot be opened", file
);
168 /* Assemble filter flags */
169 if (!NILP (Fmember (Qdelete
, flags
))) fflags
|= NOTE_DELETE
;
170 if (!NILP (Fmember (Qwrite
, flags
))) fflags
|= NOTE_WRITE
;
171 if (!NILP (Fmember (Qextend
, flags
))) fflags
|= NOTE_EXTEND
;
172 if (!NILP (Fmember (Qattrib
, flags
))) fflags
|= NOTE_ATTRIB
;
173 if (!NILP (Fmember (Qlink
, flags
))) fflags
|= NOTE_LINK
;
174 if (!NILP (Fmember (Qrename
, flags
))) fflags
|= NOTE_RENAME
;
176 /* Register event. */
177 EV_SET (&ev
, fd
, EVFILT_VNODE
, EV_ADD
| EV_ENABLE
| EV_CLEAR
,
180 if (kevent (kqueuefd
, &ev
, 1, NULL
, 0, NULL
) < 0)
181 report_file_error ("Cannot watch file", file
);
183 /* Store watch object in watch list. */
184 Lisp_Object watch_descriptor
= make_number (fd
);
185 watch_object
= list4 (watch_descriptor
, file
, flags
, callback
);
186 watch_list
= Fcons (watch_object
, watch_list
);
188 return watch_descriptor
;
192 DEFUN ("kqueue-rm-watch", Fkqueue_rm_watch
, Skqueue_rm_watch
, 1, 1, 0,
193 doc
: /* Remove an existing WATCH-DESCRIPTOR.
195 WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'. */)
196 (Lisp_Object watch_descriptor
)
198 Lisp_Object watch_object
= assq_no_quit (watch_descriptor
, watch_list
);
200 if (! CONSP (watch_object
))
201 xsignal2 (Qfile_notify_error
, build_string ("Not a watch descriptor"),
204 eassert (INTEGERP (watch_descriptor
));
205 GFileMonitor
*monitor
= XINTPTR (watch_descriptor
);
206 if (!g_file_monitor_is_cancelled (monitor
) &&
207 !g_file_monitor_cancel (monitor
))
208 xsignal2 (Qfile_notify_error
, build_string ("Could not rm watch"),
211 /* Remove watch descriptor from watch list. */
212 watch_list
= Fdelq (watch_object
, watch_list
);
215 g_object_unref (monitor
);
220 DEFUN ("gfile-valid-p", Fgfile_valid_p
, Sgfile_valid_p
, 1, 1, 0,
221 doc
: /* "Check a watch specified by its WATCH-DESCRIPTOR.
223 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
225 A watch can become invalid if the file or directory it watches is
226 deleted, or if the watcher thread exits abnormally for any other
227 reason. Removing the watch by calling `gfile-rm-watch' also makes it
229 (Lisp_Object watch_descriptor
)
231 Lisp_Object watch_object
= Fassoc (watch_descriptor
, watch_list
);
232 if (NILP (watch_object
))
236 GFileMonitor
*monitor
= XINTPTR (watch_descriptor
);
237 return g_file_monitor_is_cancelled (monitor
) ? Qnil
: Qt
;
244 globals_of_kqueue (void)
250 syms_of_kqueue (void)
252 defsubr (&Skqueue_add_watch
);
253 // defsubr (&Skqueue_rm_watch);
254 // defsubr (&Skqueue_valid_p);
257 DEFSYM (Qdelete
, "delete"); /* NOTE_DELETE */
258 DEFSYM (Qwrite
, "write"); /* NOTE_WRITE */
259 DEFSYM (Qextend
, "extend"); /* NOTE_EXTEND */
260 DEFSYM (Qattrib
, "attrib"); /* NOTE_ATTRIB */
261 DEFSYM (Qlink
, "link"); /* NOTE_LINK */
262 DEFSYM (Qrename
, "rename"); /* NOTE_RENAME */
264 staticpro (&watch_list
);
266 Fprovide (intern_c_string ("kqueue"), Qnil
);
269 #endif /* HAVE_KQUEUE */