/* Inotify support for Emacs
-Copyright (C) 2012-2013 Free Software Foundation, Inc.
+Copyright (C) 2012-2016 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
#include "coding.h"
#include "process.h"
#include "keyboard.h"
-#include "character.h"
-#include "frame.h" /* Required for termhooks.h. */
#include "termhooks.h"
-static Lisp_Object Qaccess; /* IN_ACCESS */
-static Lisp_Object Qattrib; /* IN_ATTRIB */
-static Lisp_Object Qclose_write; /* IN_CLOSE_WRITE */
-static Lisp_Object Qclose_nowrite; /* IN_CLOSE_NOWRITE */
-static Lisp_Object Qcreate; /* IN_CREATE */
-static Lisp_Object Qdelete; /* IN_DELETE */
-static Lisp_Object Qdelete_self; /* IN_DELETE_SELF */
-static Lisp_Object Qmodify; /* IN_MODIFY */
-static Lisp_Object Qmove_self; /* IN_MOVE_SELF */
-static Lisp_Object Qmoved_from; /* IN_MOVED_FROM */
-static Lisp_Object Qmoved_to; /* IN_MOVED_TO */
-static Lisp_Object Qopen; /* IN_OPEN */
-
-static Lisp_Object Qall_events; /* IN_ALL_EVENTS */
-static Lisp_Object Qmove; /* IN_MOVE */
-static Lisp_Object Qclose; /* IN_CLOSE */
-
-static Lisp_Object Qdont_follow; /* IN_DONT_FOLLOW */
-static Lisp_Object Qexcl_unlink; /* IN_EXCL_UNLINK */
-static Lisp_Object Qmask_add; /* IN_MASK_ADD */
-static Lisp_Object Qoneshot; /* IN_ONESHOT */
-static Lisp_Object Qonlydir; /* IN_ONLYDIR */
-
-static Lisp_Object Qignored; /* IN_IGNORED */
-static Lisp_Object Qisdir; /* IN_ISDIR */
-static Lisp_Object Qq_overflow; /* IN_Q_OVERFLOW */
-static Lisp_Object Qunmount; /* IN_UNMOUNT */
-
+#include <errno.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
# define IN_ONLYDIR 0
#endif
-enum { uninitialized = -100 };
/* File handle for inotify. */
-static int inotifyfd = uninitialized;
+static int inotifyfd = -1;
/* Assoc list of files being watched.
- Format:
- (watch-descriptor . callback)
+ Format: (watch-descriptor name callback)
*/
static Lisp_Object watch_list;
name = make_unibyte_string (ev->name, min (len, ev->len));
name = DECODE_FILE (name);
}
+ else
+ name = XCAR (XCDR (watch_object));
return list2 (list4 (make_watch_descriptor (ev->wd),
mask_to_aspects (ev->mask),
- make_number (ev->cookie),
- name),
- XCDR (watch_object));
+ name,
+ make_number (ev->cookie)),
+ Fnth (make_number (2), watch_object));
}
/* This callback is called when the FD is available for read. The inotify
to_read = 0;
if (ioctl (fd, FIONREAD, &to_read) == -1)
- report_file_error ("Error while trying to retrieve file system events",
- Qnil);
+ report_file_notify_error ("Error while retrieving file system events",
+ Qnil);
buffer = xmalloc (to_read);
n = read (fd, buffer, to_read);
if (n < 0)
{
xfree (buffer);
- report_file_error ("Error while trying to read file system events",
- Qnil);
+ report_file_notify_error ("Error while reading file system events", Qnil);
}
EVENT_INIT (event);
else if (EQ (symb, Qt) || EQ (symb, Qall_events))
return IN_ALL_EVENTS;
else
- signal_error ("Unknown aspect", symb);
+ {
+ errno = EINVAL;
+ report_file_notify_error ("Unknown aspect", symb);
+ }
}
static uint32_t
DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
doc: /* Add a watch for FILE-NAME to inotify.
-A WATCH-DESCRIPTOR is returned on success. ASPECT might be one of the following
-symbols or a list of those symbols:
+Return a watch descriptor. The watch will look for ASPECT events and
+invoke CALLBACK when an event occurs.
+
+ASPECT might be one of the following symbols or a list of those symbols:
access
attrib
move
close
-The following symbols can also be added to a list of aspects
+The following symbols can also be added to a list of aspects:
dont-follow
excl-unlink
oneshot
onlydir
-Watching a directory is not recursive. CALLBACK gets called in case of an
-event. It gets passed a single argument EVENT which contains an event structure
-of the format
+Watching a directory is not recursive. CALLBACK is passed a single argument
+EVENT which contains an event structure of the format
-(WATCH-DESCRIPTOR ASPECTS COOKIE NAME)
+\(WATCH-DESCRIPTOR ASPECTS NAME COOKIE)
WATCH-DESCRIPTOR is the same object that was returned by this function. It can
be tested for equality using `equal'. ASPECTS describes the event. It is a
q-overflow
unmount
+If a directory is watched then NAME is the name of file that caused the event.
+
COOKIE is an object that can be compared using `equal' to identify two matching
renames (moved-from and moved-to).
-If a directory is watched then NAME is the name of file that caused the event.
-
See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
is managed internally and there is no corresponding inotify_init. Use
`inotify-rm-watch' to remove a watch.
CHECK_STRING (file_name);
- if (inotifyfd == uninitialized)
+ if (inotifyfd < 0)
{
inotifyfd = inotify_init1 (IN_NONBLOCK|IN_CLOEXEC);
- if (inotifyfd == -1)
- {
- inotifyfd = uninitialized;
- report_file_error ("File watching feature (inotify) is not available",
- Qnil);
- }
+ if (inotifyfd < 0)
+ report_file_notify_error ("File watching is not available", Qnil);
watch_list = Qnil;
add_read_fd (inotifyfd, &inotify_callback, NULL);
}
encoded_file_name = ENCODE_FILE (file_name);
watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
if (watchdesc == -1)
- report_file_error ("Could not add watch for file", Fcons (file_name, Qnil));
+ report_file_notify_error ("Could not add watch for file", file_name);
watch_descriptor = make_watch_descriptor (watchdesc);
- /* Delete existing watch object. */
+ /* Delete existing watch object. */
watch_object = Fassoc (watch_descriptor, watch_list);
if (!NILP (watch_object))
watch_list = Fdelete (watch_object, watch_list);
- /* Store watch object in watch list. */
- watch_object = Fcons (watch_descriptor, callback);
+ /* Store watch object in watch list. */
+ watch_object = list3 (watch_descriptor, encoded_file_name, callback);
watch_list = Fcons (watch_object, watch_list);
return watch_descriptor;
int wd = XINT (watch_descriptor);
if (inotify_rm_watch (inotifyfd, wd) == -1)
- report_file_error ("Could not rm watch", Fcons (watch_descriptor,
- Qnil));
+ report_file_notify_error ("Could not rm watch", watch_descriptor);
- /* Remove watch descriptor from watch list. */
+ /* Remove watch descriptor from watch list. */
watch_object = Fassoc (watch_descriptor, watch_list);
if (!NILP (watch_object))
watch_list = Fdelete (watch_object, watch_list);
- /* Cleanup if no more files are watched. */
+ /* Cleanup if no more files are watched. */
if (NILP (watch_list))
{
- close (inotifyfd);
+ emacs_close (inotifyfd);
delete_read_fd (inotifyfd);
- inotifyfd = uninitialized;
+ inotifyfd = -1;
}
return Qt;
}
+DEFUN ("inotify-valid-p", Finotify_valid_p, Sinotify_valid_p, 1, 1, 0,
+ doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
+
+WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
+
+A watch can become invalid if the file or directory it watches is
+deleted, or if the watcher thread exits abnormally for any other
+reason. Removing the watch by calling `inotify-rm-watch' also makes
+it invalid. */)
+ (Lisp_Object watch_descriptor)
+{
+ Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
+ return NILP (watch_object) ? Qnil : Qt;
+}
+
void
syms_of_inotify (void)
{
- DEFSYM (Qaccess, "access");
- DEFSYM (Qattrib, "attrib");
- DEFSYM (Qclose_write, "close-write");
+ DEFSYM (Qaccess, "access"); /* IN_ACCESS */
+ DEFSYM (Qattrib, "attrib"); /* IN_ATTRIB */
+ DEFSYM (Qclose_write, "close-write"); /* IN_CLOSE_WRITE */
DEFSYM (Qclose_nowrite, "close-nowrite");
- DEFSYM (Qcreate, "create");
- DEFSYM (Qdelete, "delete");
- DEFSYM (Qdelete_self, "delete-self");
- DEFSYM (Qmodify, "modify");
- DEFSYM (Qmove_self, "move-self");
- DEFSYM (Qmoved_from, "moved-from");
- DEFSYM (Qmoved_to, "moved-to");
- DEFSYM (Qopen, "open");
-
- DEFSYM (Qall_events, "all-events");
- DEFSYM (Qmove, "move");
- DEFSYM (Qclose, "close");
-
- DEFSYM (Qdont_follow, "dont-follow");
- DEFSYM (Qexcl_unlink, "excl-unlink");
- DEFSYM (Qmask_add, "mask-add");
- DEFSYM (Qoneshot, "oneshot");
- DEFSYM (Qonlydir, "onlydir");
-
- DEFSYM (Qignored, "ignored");
- DEFSYM (Qisdir, "isdir");
- DEFSYM (Qq_overflow, "q-overflow");
- DEFSYM (Qunmount, "unmount");
+ /* IN_CLOSE_NOWRITE */
+ DEFSYM (Qcreate, "create"); /* IN_CREATE */
+ DEFSYM (Qdelete, "delete"); /* IN_DELETE */
+ DEFSYM (Qdelete_self, "delete-self"); /* IN_DELETE_SELF */
+ DEFSYM (Qmodify, "modify"); /* IN_MODIFY */
+ DEFSYM (Qmove_self, "move-self"); /* IN_MOVE_SELF */
+ DEFSYM (Qmoved_from, "moved-from"); /* IN_MOVED_FROM */
+ DEFSYM (Qmoved_to, "moved-to"); /* IN_MOVED_TO */
+ DEFSYM (Qopen, "open"); /* IN_OPEN */
+
+ DEFSYM (Qall_events, "all-events"); /* IN_ALL_EVENTS */
+ DEFSYM (Qmove, "move"); /* IN_MOVE */
+ DEFSYM (Qclose, "close"); /* IN_CLOSE */
+
+ DEFSYM (Qdont_follow, "dont-follow"); /* IN_DONT_FOLLOW */
+ DEFSYM (Qexcl_unlink, "excl-unlink"); /* IN_EXCL_UNLINK */
+ DEFSYM (Qmask_add, "mask-add"); /* IN_MASK_ADD */
+ DEFSYM (Qoneshot, "oneshot"); /* IN_ONESHOT */
+ DEFSYM (Qonlydir, "onlydir"); /* IN_ONLYDIR */
+
+ DEFSYM (Qignored, "ignored"); /* IN_IGNORED */
+ DEFSYM (Qisdir, "isdir"); /* IN_ISDIR */
+ DEFSYM (Qq_overflow, "q-overflow"); /* IN_Q_OVERFLOW */
+ DEFSYM (Qunmount, "unmount"); /* IN_UNMOUNT */
defsubr (&Sinotify_add_watch);
defsubr (&Sinotify_rm_watch);
+ defsubr (&Sinotify_valid_p);
staticpro (&watch_list);