unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: joakim@verona.se
To: "Rüdiger Sonderfeld" <ruediger@c-plusplus.de>
Cc: emacs-devel@gnu.org
Subject: Re: [PATCH] Support for filesystem watching (inotify)
Date: Sat, 04 Jun 2011 10:52:07 +0200	[thread overview]
Message-ID: <m362omou6g.fsf@verona.se> (raw)
In-Reply-To: <201106040034.15598.ruediger@c-plusplus.de> ("Rüdiger Sonderfeld"'s message of "Sat, 4 Jun 2011 00:34:15 +0200")

Rüdiger Sonderfeld <ruediger@c-plusplus.de> writes:

> Hello,
> I wrote a patch to support watching for file system events. It currently only works with inotify (Linux) but it could be extended to support kqueue 
> (*BSD, OS X) or Win32. But I wanted to get some feedback first. Watching for filesystem events would be very useful for, e.g., dired or magit's status 
> view.

Very interesting! Have you signed copyright papers and so on?

>
> Here is a quick example (expects a directory foo containing a file bar):
>
> (file-watch "foo" #'(lambda (path events) (message "FS-Event: %s %s") path events)) :all)
> (delete-file "foo/bar")
> (file-unwatch "foo")
>
> So please tell me what you think of this.
>
> Regards,
> Rüdiger
>
>
> [PATCH] Added basic file system watching support.
>
> It currently only works with inotify/Linux. It adds file-watch and file-unwatch functions.
> ---
>  configure.in    |   14 +++
>  src/Makefile.in |    2 +-
>  src/emacs.c     |    4 +
>  src/filewatch.c |  242 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/lisp.h      |    5 +
>  5 files changed, 266 insertions(+), 1 deletions(-)
>  create mode 100644 src/filewatch.c
>
> diff --git a/configure.in b/configure.in
> index 77deef8..3263876 100644
> --- a/configure.in
> +++ b/configure.in
> @@ -169,6 +169,7 @@ OPTION_DEFAULT_ON([dbus],[don't compile with D-Bus support])
>  OPTION_DEFAULT_ON([gconf],[don't compile with GConf support])
>  OPTION_DEFAULT_ON([selinux],[don't compile with SELinux support])
>  OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support])
> +OPTION_DEFAULT_ON([inotify],[don't compile with inotify (file-watch) support])
>  
>  ## For the times when you want to build Emacs but don't have
>  ## a suitable makeinfo, and can live without the manuals.
> @@ -1978,6 +1979,19 @@ fi
>  AC_SUBST(LIBGNUTLS_LIBS)
>  AC_SUBST(LIBGNUTLS_CFLAGS)
>  
> +dnl inotify is only available on GNU/Linux.
> +HAVE_INOTIFY=no
> +if test "${with_inotify}" = "yes"; then
> +   AC_CHECK_HEADERS(sys/inotify.h)
> +   if test "$ac_cv_header_sys_inotify_h" = yes ; then
> +     AC_CHECK_FUNCS(inotify_init1 inotify_add_watch inotify_rm_watch, HAVE_INOTIFY=yes)
> +   fi
> +fi
> +if test "${HAVE_INOTIFY}" = "yes"; then
> +   AC_DEFINE(HAVE_INOTIFY, [1], [Define to 1 to use inotify])
> +   AC_DEFINE(HAVE_FILEWATCH, [1], [Define to 1 to support filewatch])
> +fi
> +
>  dnl Do not put whitespace before the #include statements below.
>  dnl Older compilers (eg sunos4 cc) choke on it.
>  HAVE_XAW3D=no
> diff --git a/src/Makefile.in b/src/Makefile.in
> index e119596..0cd6d6e 100644
> --- a/src/Makefile.in
> +++ b/src/Makefile.in
> @@ -354,7 +354,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
>  	syntax.o $(UNEXEC_OBJ) bytecode.o \
>  	process.o gnutls.o callproc.o \
>  	region-cache.o sound.o atimer.o \
> -	doprnt.o intervals.o textprop.o composite.o xml.o \
> +	doprnt.o intervals.o textprop.o composite.o xml.o filewatch.o \
>  	$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ)
>  obj = $(base_obj) $(NS_OBJC_OBJ)
>  
> diff --git a/src/emacs.c b/src/emacs.c
> index 6bdd255..5ca5cda 100644
> --- a/src/emacs.c
> +++ b/src/emacs.c
> @@ -1550,6 +1550,10 @@ main (int argc, char **argv)
>        syms_of_gnutls ();
>  #endif
>  
> +#ifdef HAVE_FILEWATCH
> +      syms_of_filewatch ();
> +#endif /* HAVE_FILEWATCH */
> +
>  #ifdef HAVE_DBUS
>        syms_of_dbusbind ();
>  #endif /* HAVE_DBUS */
> diff --git a/src/filewatch.c b/src/filewatch.c
> new file mode 100644
> index 0000000..5b7c130
> --- /dev/null
> +++ b/src/filewatch.c
> @@ -0,0 +1,242 @@
> +/* Watching file system changes.
> +
> +Copyright (C) 2011
> +  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.
> +
> +GNU Emacs is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include <config.h>
> +
> +#ifdef HAVE_FILEWATCH
> +
> +#include <setjmp.h>
> +
> +#include "lisp.h"
> +#include "process.h"
> +
> +static Lisp_Object QCmodify, QCmove, QCattrib, QCdelete, QCfrom, QCto, QCall;
> +
> +#ifdef HAVE_INOTIFY
> +#include <sys/inotify.h>
> +#include <sys/ioctl.h>
> +
> +enum { uninitialized = -100 };
> +static int inotifyfd = uninitialized;
> +
> +// Assoc list of files being watched.
> +static Lisp_Object data;
> +
> +static void 
> +inotify_callback(int fd, void *_, int for_read) {
> +  eassert(for_read);
> +  eassert(data);
> +
> +  int to_read = 0;
> +  if(ioctl(fd, FIONREAD,  &to_read) == -1)
> +    report_file_error("ioctl(2) on inotify", Qnil);
> +  char *const buffer = xmalloc(to_read);
> +  ssize_t const n = read(fd, buffer, to_read);
> +  if(n < 0)
> +    report_file_error("read from inotify", Qnil);
> +  else if(n < to_read)
> +    {
> +      // TODO
> +      message1("n < to_read");
> +    }
> +
> +  size_t i = 0;
> +  while(i < (size_t)n)
> +    {
> +      struct inotify_event *ev = (struct inotify_event*)&buffer[i];
> +
> +      Lisp_Object callback = Fassoc(make_number(ev->wd), data);
> +      if(!NILP(callback))
> +        {
> +          Lisp_Object call[3];
> +          call[0] = Fcar(Fcdr(Fcdr(callback))); /* callback */
> +          call[1] = Fcar(Fcdr(callback));       /* path */
> +
> +          /* TODO check how to handle UTF-8 in file names:
> +             make_string_from_bytes NCHARS vs. NBYTES */
> +          /* ev->len seems to be an arbitrary large number
> +             and not the exact length of ev->name */
> +          size_t const len = strlen(ev->name);
> +          /* if a directory is watched name contains the name
> +             of the file that was changed */
> +          Lisp_Object name = make_string_from_bytes (ev->name, len, len);
> +
> +          Lisp_Object events = Qnil;
> +          if(ev->mask & (IN_MODIFY|IN_CREATE) )
> +            events = Fcons(Fcons(QCmodify, name), events);
> +          if(ev->mask & IN_MOVE_SELF)
> +            events = Fcons(Fcons(QCmove, name), events);
> +          if(ev->mask & IN_MOVED_FROM)
> +            events = Fcons(Fcons(QCmove, Fcons(QCfrom, Fcons(name, make_number(ev->cookie)))), events);
> +          if(ev->mask & IN_MOVED_TO)
> +            events = Fcons(Fcons(QCmove, Fcons(QCto, Fcons(name, make_number(ev->cookie)))), events);
> +          if(ev->mask & IN_ATTRIB)
> +            events = Fcons(Fcons(QCattrib, name), events);
> +          if(ev->mask & (IN_DELETE|IN_DELETE_SELF) )
> +            events = Fcons(Fcons(QCdelete, name), events);
> +      
> +          if(!NILP(events))
> +            {
> +              call[2] = events;
> +              Ffuncall(3, call);
> +            }
> +      
> +          if(ev->mask & IN_IGNORED)
> +            {
> +              /* Event was removed automatically: Drop it from data list. */
> +              message("File-watch: \"%s\" will be ignored", SSDATA(call[1]));
> +              data = Fdelete(callback, data);
> +            }
> +          if(ev->mask & IN_Q_OVERFLOW)
> +            message1("File watch: Inotify Queue Overflow!");
> +        }
> +
> +      i += sizeof(*ev) + ev->len;
> +    }
> +
> +  free(buffer);
> +}
> +
> +DEFUN ("file-watch", Ffile_watch, Sfile_watch, 3, MANY, 0,
> +       doc: /* Watch a file or directory.
> +
> +file-watch watches the file or directory given in PATH. If a change occurs
> +CALLBACK is called with PATH as first argument and a list of changes as second
> +argument. FLAGS can be
> +
> +:modify -- notify when a file is modified or created.
> +
> +:move -- notify when a file/directory is moved.
> +
> +:attrib -- notify when attributes change.
> +
> +:delete -- notify when a file/directory is deleted.
> +
> +:all -- notify for all of the above.
> +
> +Watching a directory is not recursive. CALLBACK receives the events as a list
> +with each list element being a list containing information about an event. The
> +first element is a flag symbol. If a directory is watched the second element is
> +the name of the file that changed. If a file is moved from or to the directory
> +the second element is either :from or :to and the third element is the file
> +name. A fourth element contains a numeric identifier (cookie) that can be used
> +to identify matching move operations if a file is moved inside the directory.
> +
> +Use `file-unwatch' to stop watching.
> +
> +usage: (file-watch PATH CALLBACK &rest FLAGS) */)
> +  (size_t nargs, Lisp_Object *args)
> +{
> +  if(nargs < 3)
> +    return Qnil;
> +
> +  CHECK_STRING(args[0]);
> +
> +  if(inotifyfd == uninitialized)
> +    {
> +      inotifyfd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
> +      if(inotifyfd == -1)
> +        report_file_error("Initializing file watching", Qnil);
> +      data = Qnil;
> +      add_read_fd(inotifyfd, &inotify_callback, &data);
> +    }
> +  uint32_t mask = 0;
> +  int i;
> +  for(i = 2; i < nargs; ++i)
> +    {
> +      if(EQ(args[i], QCmodify))
> +        mask |= IN_MODIFY | IN_CREATE;
> +      else if(EQ(args[i], QCmove))
> +        mask |= IN_MOVE_SELF | IN_MOVE;
> +      else if(EQ(args[i], QCattrib))
> +        mask |= IN_ATTRIB;
> +      else if(EQ(args[i], QCdelete))
> +        mask |= IN_DELETE_SELF | IN_DELETE;
> +      else if(EQ(args[i], QCall))
> +        mask |= IN_MODIFY | IN_CREATE | IN_MOVE_SELF | IN_MOVE | IN_ATTRIB
> +          | IN_DELETE_SELF | IN_DELETE;
> +      else /* TODO: should this be an error? */
> +        message("Unkown parameter %s (ignored)", SSDATA(Fprin1_to_string(args[i], Qnil)));
> +    }
> +  int watchdesc = inotify_add_watch(inotifyfd, SSDATA(args[0]), mask);
> +  if(watchdesc == -1) {
> +    report_file_error("Watching file", Qnil);
> +  }
> +  data = Fcons(Fcons(make_number(watchdesc), Flist(2, args)), data);
> +  return Qt;
> +}
> +
> +DEFUN ("file-unwatch", Ffile_unwatch, Sfile_unwatch, 1, 1, 0,
> +       doc: /* Stop watching a file or directory. */)
> +  (Lisp_Object path)
> +{
> +  if(inotifyfd == uninitialized)
> +    return Qnil;
> +  CHECK_STRING(path);
> +
> +  Lisp_Object x = data;
> +  Lisp_Object cell, path_data;
> +  while(!NILP(x))
> +    {
> +      cell = Fcar(x);
> +      x = Fcdr(x);
> +      path_data = Fcdr(cell);
> +      if(!NILP(path_data) && STRINGP(Fcar(path_data))
> +         && Fstring_equal(Fcar(path_data), path)
> +         && NUMBERP(Fcar(cell)))
> +        {
> +          int const magicno = XINT(Fcar(cell));
> +          data = Fdelete(cell, data);
> +          if(inotify_rm_watch(inotifyfd, magicno) == -1)
> +            report_file_error("Unwatch path", Qnil);
> +          return Qt;
> +        }
> +    }
> +  return Qnil;
> +}
> +
> +#else /* HAVE_INOTIFY */
> +#error "Filewatch defined but no watch mechanism (inotify) available"
> +#endif /* HAVE_INOTIFY */
> +
> +void
> +syms_of_filewatch (void)
> +{
> +  QCmodify = intern_c_string (":modify");
> +  staticpro (&QCmodify);
> +  QCmove = intern_c_string (":move");
> +  staticpro (&QCmove);
> +  QCattrib = intern_c_string (":attrib");
> +  staticpro (&QCattrib);
> +  QCdelete = intern_c_string (":delete");
> +  staticpro (&QCdelete);
> +  QCfrom = intern_c_string (":from");
> +  staticpro (&QCfrom);
> +  QCto = intern_c_string (":to");
> +  staticpro (&QCto);
> +  QCall = intern_c_string (":all");
> +  staticpro (&QCall);
> +
> +  defsubr (&Sfile_watch);
> +  defsubr (&Sfile_unwatch);
> +}
> +
> +
> +#endif /* HAVE_FILEWATCH */
> diff --git a/src/lisp.h b/src/lisp.h
> index 85838d1..3ed5281 100644
> --- a/src/lisp.h
> +++ b/src/lisp.h
> @@ -3405,6 +3405,11 @@ EXFUN (Fxw_display_color_p, 1);
>  EXFUN (Fx_focus_frame, 1);
>  #endif
>  
> +/* Defined in filewatch.c */
> +#ifdef HAVE_FILEWATCH
> +extern void syms_of_filewatch (void);
> +#endif
> +
>  /* Defined in xfaces.c */
>  extern Lisp_Object Qdefault, Qtool_bar, Qregion, Qfringe;
>  extern Lisp_Object Qheader_line, Qscroll_bar, Qcursor, Qborder, Qmouse, Qmenu;

-- 
Joakim Verona



  reply	other threads:[~2011-06-04  8:52 UTC|newest]

Thread overview: 125+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-03 22:34 [PATCH] Support for filesystem watching (inotify) Rüdiger Sonderfeld
2011-06-04  8:52 ` joakim [this message]
2011-06-04 16:40   ` Rüdiger Sonderfeld
2011-06-04 10:43 ` Jan Djärv
2011-06-04 23:36   ` [PATCH update2] " Rüdiger Sonderfeld
2011-06-05  5:45     ` Eli Zaretskii
2011-06-05  9:48       ` [PATCH update3] " Rüdiger Sonderfeld
2011-06-05  7:48     ` [PATCH update2] " Jan Djärv
2011-06-05  7:54     ` Andreas Schwab
2011-06-05  9:49       ` Rüdiger Sonderfeld
2011-06-05 15:59         ` John Yates
2011-06-05 16:14           ` Rüdiger Sonderfeld
2011-06-05 16:58             ` Eli Zaretskii
2011-06-06 18:56               ` Rüdiger Sonderfeld
2011-06-06 19:57                 ` Eli Zaretskii
2011-06-04 11:30 ` [PATCH] " Eli Zaretskii
2011-06-04 17:13   ` [PATCH updated] " Rüdiger Sonderfeld
2011-06-04 19:15     ` Eli Zaretskii
2011-06-04 20:10     ` Thien-Thi Nguyen
2011-06-04 23:43       ` Rüdiger Sonderfeld
2011-06-05  2:15         ` Thien-Thi Nguyen
2011-06-05  9:22           ` Štěpán Němec
2011-06-15 20:53             ` Johan Bockgård
2011-06-16  8:52               ` Inlined cl functions -- how to learn about them Štěpán Němec
2011-06-06 15:21       ` [PATCH updated] Support for filesystem watching (inotify) Stefan Monnier
2011-06-06 16:25         ` Rüdiger Sonderfeld
2011-06-06 17:11           ` Stefan Monnier
2011-06-06 20:16             ` Ted Zlatanov
2011-06-07 14:42               ` Stefan Monnier
2011-06-07 16:46                 ` Ted Zlatanov
2011-06-07 18:06                   ` Stefan Monnier
2011-06-07 18:26                     ` Ted Zlatanov
2011-06-24  0:50             ` Rüdiger Sonderfeld
2011-06-24 10:19               ` Ted Zlatanov
2011-06-24 12:18                 ` Ted Zlatanov
2011-07-06 13:36               ` Stefan Monnier
2011-07-06 15:54                 ` Paul Eggert
2011-07-06 18:30                   ` Stefan Monnier
2011-07-06 20:39                     ` Paul Eggert
2011-07-06 19:14                   ` wide-int crash [was Re: [PATCH updated] Support for filesystem watching (inotify)] Glenn Morris
2011-07-06 22:31                     ` Paul Eggert
2011-07-07 19:43               ` [PATCH updated] Support for filesystem watching (inotify) Stefan Monnier
2012-09-28 13:06                 ` [PATCH] Added basic file system watching support Rüdiger Sonderfeld
2012-09-28 14:13                   ` Stefan Monnier
2012-09-28 16:27                     ` Rüdiger Sonderfeld
2012-10-01  4:38                       ` Stefan Monnier
2012-10-01  7:24                         ` Eli Zaretskii
2012-10-01 14:09                         ` [PATCH] Added inotify support Rüdiger Sonderfeld
2012-10-01 16:27                           ` Stefan Monnier
2012-10-02 21:28                           ` Eli Zaretskii
2012-10-02 23:46                             ` Óscar Fuentes
2012-10-03  2:10                               ` Stefan Monnier
2012-10-03  3:54                                 ` Eli Zaretskii
2012-10-03 12:46                                   ` Stefan Monnier
2012-10-03 18:34                                     ` Eli Zaretskii
2012-10-03 18:47                                       ` Stefan Monnier
2012-10-03 19:08                                         ` Eli Zaretskii
2012-10-03 20:59                                           ` Stefan Monnier
2012-10-12 13:54                                             ` Eli Zaretskii
2012-10-14 14:55                                               ` Eli Zaretskii
2012-10-03 19:33                                       ` Óscar Fuentes
2012-10-05  7:40                                         ` Eli Zaretskii
2012-10-05 13:07                                           ` Óscar Fuentes
2012-10-05 15:19                                             ` Eli Zaretskii
2012-10-05 16:55                                 ` Nix
2012-10-05 17:15                                   ` Eli Zaretskii
2012-10-05 17:46                                     ` Nix
2012-10-05 18:22                                   ` Stefan Monnier
2012-10-05 18:30                                     ` Óscar Fuentes
2012-10-06 16:39                                     ` Nix
2012-10-06 17:01                                       ` Stefan Monnier
2012-10-06 18:51                                         ` Nix
2012-10-06 21:26                                           ` Stefan Monnier
2012-10-06 21:28                                             ` Nix
2012-10-07  7:38                                               ` Achim Gratz
2012-10-07 13:58                                                 ` Stefan Monnier
2012-10-07 14:54                                                   ` Achim Gratz
2012-10-07  8:24                                               ` Stephen J. Turnbull
2012-10-07 14:00                                                 ` Stefan Monnier
2012-10-07 14:28                                                   ` Óscar Fuentes
2012-10-07 14:38                                                     ` Stefan Monnier
2012-10-08  7:07                                                   ` Stephen J. Turnbull
2012-10-08  8:06                                                     ` Eli Zaretskii
2012-10-14 15:52                                           ` Jim Meyering
2012-10-06  7:04                                   ` Stephen J. Turnbull
2012-10-06  7:23                                     ` Andreas Schwab
2012-10-06 16:41                                       ` Nix
2012-10-07  8:09                                         ` Stephen J. Turnbull
2012-10-07 14:08                                           ` Stefan Monnier
2012-10-03  3:57                               ` Eli Zaretskii
2012-12-02 20:08                           ` Eli Zaretskii
2012-12-03 17:18                             ` Stefan Monnier
2012-12-10 14:16                               ` Eli Zaretskii
2012-12-10 14:47                                 ` Stefan Monnier
2012-12-10 11:52                           ` Eli Zaretskii
2012-12-10 12:11                             ` Rüdiger Sonderfeld
2012-12-11  7:43                             ` Michael Albinus
2012-12-11  8:20                               ` Eli Zaretskii
2012-12-11 16:36                                 ` Michael Albinus
2012-12-11 16:46                                   ` Rüdiger Sonderfeld
2012-12-11 20:17                                     ` Michael Albinus
2012-12-11  7:54                         ` [PATCH] Added basic file system watching support Michael Albinus
2012-12-11  8:12                           ` Eli Zaretskii
2012-12-11  8:19                             ` Michael Albinus
2012-12-11  8:29                               ` Eli Zaretskii
2012-12-11  8:44                                 ` Michael Albinus
2012-12-11  9:39                                   ` Eli Zaretskii
2012-12-11 10:15                                     ` Eli Zaretskii
2012-12-11 10:38                                       ` Michael Albinus
2012-12-11 10:24                                     ` Michael Albinus
2012-12-11 12:51                                       ` Eli Zaretskii
2012-12-11 13:17                                         ` Michael Albinus
2012-12-11 13:23                                           ` Eli Zaretskii
2012-12-12  1:09                                         ` Leo
2012-12-12  3:57                                           ` Eli Zaretskii
2013-01-07 11:33                                     ` Michael Albinus
2013-01-07 15:57                                       ` Eli Zaretskii
2013-01-07 16:00                                         ` Michael Albinus
2013-01-07 20:26                                           ` Stefan Monnier
2013-01-08  7:44                                             ` Michael Albinus
2011-06-06 15:14     ` [PATCH updated] Support for filesystem watching (inotify) Stefan Monnier
2011-06-06 16:21       ` Rüdiger Sonderfeld
2012-09-18 11:50 ` [PATCH] " Leo
2012-09-26 12:15   ` Rüdiger Sonderfeld
2012-09-26 17:52     ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m362omou6g.fsf@verona.se \
    --to=joakim@verona.se \
    --cc=emacs-devel@gnu.org \
    --cc=ruediger@c-plusplus.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).