]> granicus.if.org Git - neomutt/commitdiff
add feature file monitoring with Linux inotify
authorgt_16_gitlab.com@innocircle.com <gt_16_gitlab.com@innocircle.com>
Sun, 3 Jun 2018 20:23:57 +0000 (20:23 +0000)
committerRichard Russon <rich@flatcap.org>
Mon, 3 Sep 2018 01:04:32 +0000 (02:04 +0100)
Makefile.autosetup
auto.def
curs_lib.c
curs_main.c
doc/manual.xml.head
keymap.c
mailbox.c
monitor.c [new file with mode: 0644]
monitor.h [new file with mode: 0644]
version.c

index 2f3f86442d7f4f388bd44489b325af14a1c30036..0eb31b612248c7c6ed9005a6805711d9fb229e7b 100644 (file)
@@ -83,6 +83,9 @@ NEOMUTTOBJS+= remailer.o
 @if USE_LUA
 NEOMUTTOBJS+=  mutt_lua.o
 @endif
+@if USE_INOTIFY
+NEOMUTTOBJS+=  monitor.o
+@endif
 CLEANFILES+=   $(NEOMUTT) $(NEOMUTTOBJS)
 ALLOBJS+=      $(NEOMUTTOBJS)
 
index a6c7ff2b6c2d829cc60af6ca60b8569c0cfc3f07..1c5b2bc4cb8fde11e2f40605e16be8e7573f1dda 100644 (file)
--- a/auto.def
+++ b/auto.def
@@ -34,6 +34,7 @@ options {
   docdir:path               => "Documentation root"
   with-lock:=fcntl          => "Select fcntl() or flock() to lock files"
   fmemopen=0                => "Use fmemopen() for temporary in-memory files"
+  inotify=1                 => "Disable file monitoring support (Linux only)"
   locales-fix=0             => "Enable locales fix"
   pgp=1                     => "Disable PGP support"
   smime=1                   => "Disable SMIME support"
@@ -101,7 +102,7 @@ if {1} {
   # Keep sorted, please.
   foreach opt {
     bdb doc everything fmemopen full-doc gdbm gnutls gpgme gss
-    homespool idn idn2 kyotocabinet lmdb locales-fix lua mixmaster nls
+    homespool idn idn2 inotify kyotocabinet lmdb locales-fix lua mixmaster nls
     notmuch pgp qdbm sasl smime ssl tokyocabinet
   } {
     define want-$opt [opt-bool $opt]
@@ -368,6 +369,16 @@ if {[get-define want-gpgme]} {
   define CRYPT_BACKEND_GPGME
 }
 
+###############################################################################
+# INOTIFY
+if {[get-define want-inotify]} {
+  if {[cc-check-functions inotify_init1 inotify_add_watch inotify_rm_watch]} {
+    if {[cc-check-includes sys/inotify.h]} {
+      define USE_INOTIFY
+    }
+  }
+}
+
 ###############################################################################
 # PGP
 if {[get-define want-pgp]} {
index 6fe908e645ca3d5c3db899a8a2d04df1fb6301cd..961928674e95dcf35b074c3e30f900fa911721a4 100644 (file)
@@ -61,6 +61,9 @@
 #ifdef USE_NOTMUCH
 #include "notmuch/mutt_notmuch.h"
 #endif
+#ifdef USE_INOTIFY
+#include "monitor.h"
+#endif
 
 /* These Config Variables are only used in curs_lib.c */
 bool MetaKey; ///< Config: Interpret 'ALT-x' as 'ESC-x'
@@ -149,7 +152,12 @@ struct Event mutt_getch(void)
   ch = KEY_RESIZE;
   while (ch == KEY_RESIZE)
 #endif /* KEY_RESIZE */
-    ch = getch();
+#ifdef USE_INOTIFY
+    if (mutt_monitor_poll() != 0)
+      ch = ERR;
+    else
+#endif
+      ch = getch();
   mutt_sig_allow_interrupt(0);
 
   if (SigInt)
index 07a6da303b058bfd84bcc86b6b8eadecabf45293..9e691d59ab27e366ffb4d114585d67aa7611f8ad 100644 (file)
@@ -85,6 +85,9 @@
 #ifdef ENABLE_NLS
 #include <libintl.h>
 #endif
+#ifdef USE_INOTIFY
+#include "monitor.h"
+#endif
 
 /* These Config Variables are only used in curs_main.c */
 bool ChangeFolderNext; ///< Config: Suggest the next folder, rather than the first when using '<change-folder>'
@@ -565,7 +568,9 @@ static int main_change_folder(struct Menu *menu, int op, char *buf,
   if (Context)
   {
     char *new_last_folder = NULL;
-
+#ifdef USE_INOTIFY
+    int monitor_remove_rc = mutt_monitor_remove(NULL);
+#endif
 #ifdef USE_COMPRESSED
     if (Context->compress_info && Context->realpath)
       new_last_folder = mutt_str_strdup(Context->realpath);
@@ -577,6 +582,10 @@ static int main_change_folder(struct Menu *menu, int op, char *buf,
     int check = mx_mbox_close(&Context, index_hint);
     if (check != 0)
     {
+#ifdef USE_INOTIFY
+      if (monitor_remove_rc == 0)
+        mutt_monitor_add(NULL);
+#endif
       if (check == MUTT_NEW_MAIL || check == MUTT_REOPENED)
         update_index(menu, Context, check, *oldcount, *index_hint);
 
@@ -608,6 +617,9 @@ static int main_change_folder(struct Menu *menu, int op, char *buf,
   if (Context)
   {
     menu->current = ci_first_message();
+#ifdef USE_INOTIFY
+    mutt_monitor_add(NULL);
+#endif
   }
   else
     menu->current = 0;
@@ -985,6 +997,9 @@ int mutt_index_menu(void)
     /* force the mailbox check after we enter the folder */
     mutt_mailbox_check(MUTT_MAILBOX_CHECK_FORCE);
   }
+#ifdef USE_INOTIFY
+  mutt_monitor_add(NULL);
+#endif
 
   if (((Sort & SORT_MASK) == SORT_THREADS) && CollapseAll)
   {
index 98467836decd99bb5b7b526e9af67fa6b0007c7b..7cf69d224f4e03bcdb722ef5eaf1cbc26c9f4818 100644 (file)
@@ -8830,6 +8830,35 @@ subjectrx '\[[^]]*\]:? *' '%L%R'
         </para>
       </sect2>
 
+      <sect2 id="new-mail-monitoring">
+        <title>Monitoring New Mail</title>
+        <para>
+          When the <emphasis>Inotify</emphasis>mechanism for monitoring of
+          files is supported (Linux only) and not disabled at compilation time,
+          Mutt immediately notifies about new mail for all folders configured
+          via the <link linkend="mailboxes"><command>mailboxes</command></link>
+          command.  Dependent on <link linkend="mailbox-formats">mailbox format</link>
+          also added <emphasis>old</emphasis>mails are tracked (not for Maildir).
+        </para>
+        <para>
+          No configuration variables are available. Trace output is given when
+          debugging is enabled via <link linkend="tab-commandline-options">command line option</link>
+          <literal>-d3</literal>. The lower level 2 only shows errors, the
+          higher level 5 all including raw Inotify events.
+        </para>
+        <note>
+          <para>
+            Getting events about new mail is limited to the capabilities of the
+            underlying mechanism.  <emphasis>inotify</emphasis>only reports
+            local changes, i. e. new mail notification works for mails
+            delivered by an agent on the same machine as mutt, but not when
+            delivered remotely on a network file system as nfs. also the
+            monitoring handles might fail in rare conditions, so you better
+            don't completely rely on this feature.
+          </para>
+        </note>
+      </sect2>
+
       <sect2 id="calc-mailbox-counts">
         <title>Calculating Mailbox Message Counts</title>
         <para>
index ccd4fb933bd6da8fd4f653305eb54fde00ddbe43..c9eebe28ab08cd4b6ed19eb0789355037b551454 100644 (file)
--- a/keymap.c
+++ b/keymap.c
@@ -43,6 +43,9 @@
 #ifdef USE_IMAP
 #include "imap/imap.h"
 #endif
+#ifdef USE_INOTIFY
+#include "monitor.h"
+#endif
 
 /**
  * Menus - Menu name lookup table
@@ -590,7 +593,11 @@ int km_dokey(int menu)
            * loop now.  Otherwise, continue to loop until reaching a total of
            * $timeout seconds.
            */
+#ifdef USE_INOTIFY
+          if (tmp.ch != -2 || SigWinch || MonitorFilesChanged)
+#else
           if (tmp.ch != -2 || SigWinch)
+#endif
             goto gotkey;
           i -= ImapKeepalive;
           imap_keepalive();
index 9408f5152c9b48f29557685b0af480555abd6059..16a9e6f1c1f5b44c794ad2fee1cfc16d184cf424 100644 (file)
--- a/mailbox.c
+++ b/mailbox.c
@@ -58,6 +58,9 @@
 #ifdef USE_POP
 #include "pop/pop.h"
 #endif
+#ifdef USE_INOTIFY
+#include "monitor.h"
+#endif
 
 /* These Config Variables are only used in mailbox.c */
 short MailCheck; ///< Config: Number of seconds before NeoMutt checks for new mail
@@ -697,6 +700,10 @@ int mutt_parse_mailboxes(struct Buffer *path, struct Buffer *s,
 
 #ifdef USE_SIDEBAR
     mutt_sb_notify_mailbox(b, true);
+#endif
+#ifdef USE_INOTIFY
+    b->magic = mx_path_probe(b->path, NULL);
+    mutt_monitor_add(b);
 #endif
   }
   return 0;
@@ -756,6 +763,9 @@ int mutt_parse_unmailboxes(struct Buffer *path, struct Buffer *s,
       {
 #ifdef USE_SIDEBAR
         mutt_sb_notify_mailbox(np->b, false);
+#endif
+#ifdef USE_INOTIFY
+        mutt_monitor_remove(np->b);
 #endif
         STAILQ_REMOVE(&AllMailboxes, np, MailboxNode, entries);
         mailbox_free(&np->b);
diff --git a/monitor.c b/monitor.c
new file mode 100644 (file)
index 0000000..22fd9d6
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,438 @@
+/**
+ * @file
+ * Monitor files for changes
+ *
+ * @authors
+ * Copyright (C) 2018 Gero Treuer <gero@70t.de>
+ *
+ * @copyright
+ * This program 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "mutt/mutt.h"
+#include "monitor.h"
+#include "context.h"
+#include "globals.h"
+#include "mailbox.h"
+#include "mutt_curses.h"
+#include "mx.h"
+
+int MonitorFilesChanged;
+
+struct Monitor
+{
+  struct Monitor *next;
+  char *mh_backup_path;
+  dev_t st_dev;
+  ino_t st_ino;
+  short magic;
+  int desc;
+};
+
+static int INotifyFd = -1;
+static struct Monitor *Monitor = NULL;
+static size_t PollFdsCount = 0;
+static size_t PollFdsLen = 0;
+static struct pollfd *PollFds;
+
+struct MonitorInfo
+{
+  short magic;
+  short isdir;
+  const char *path;
+  dev_t st_dev;
+  ino_t st_ino;
+  struct Monitor *monitor;
+  char path_buf[PATH_MAX]; /* access via path only (maybe not initialized) */
+};
+
+#define INOTIFY_MASK_DIR (IN_MOVED_TO | IN_ATTRIB | IN_CLOSE_WRITE | IN_ISDIR)
+#define INOTIFY_MASK_FILE IN_CLOSE_WRITE
+
+static void mutt_poll_fd_add(int fd, short events)
+{
+  int i = 0;
+  for (; i < PollFdsCount && PollFds[i].fd != fd; ++i)
+    ;
+
+  if (i == PollFdsCount)
+  {
+    if (PollFdsCount == PollFdsLen)
+    {
+      PollFdsLen += 2;
+      mutt_mem_realloc(&PollFds, PollFdsLen * sizeof(struct pollfd));
+    }
+    PollFdsCount++;
+    PollFds[i].fd = fd;
+    PollFds[i].events = events;
+  }
+  else
+    PollFds[i].events |= events;
+}
+
+static int mutt_poll_fd_remove(int fd)
+{
+  int i = 0, d;
+  for (i = 0; i < PollFdsCount && PollFds[i].fd != fd; ++i)
+    ;
+  if (i == PollFdsCount)
+    return -1;
+  d = PollFdsCount - i - 1;
+  if (d)
+    memmove(&PollFds[i], &PollFds[i + 1], d * sizeof(struct pollfd));
+  PollFdsCount--;
+  return 0;
+}
+
+static int monitor_init(void)
+{
+  if (INotifyFd == -1)
+  {
+    INotifyFd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+    if (INotifyFd == -1)
+    {
+      mutt_debug(2, "inotify_init1 failed, errno=%d %s\n", errno, strerror(errno));
+      return -1;
+    }
+    mutt_poll_fd_add(0, POLLIN);
+    mutt_poll_fd_add(INotifyFd, POLLIN);
+  }
+  return 0;
+}
+
+static void monitor_check_free(void)
+{
+  if (!Monitor && INotifyFd != -1)
+  {
+    mutt_poll_fd_remove(INotifyFd);
+    close(INotifyFd);
+    INotifyFd = -1;
+    MonitorFilesChanged = 0;
+  }
+}
+
+static struct Monitor *monitor_create(struct MonitorInfo *info, int descriptor)
+{
+  struct Monitor *monitor = mutt_mem_calloc(1, sizeof(struct Monitor));
+  monitor->magic = info->magic;
+  monitor->st_dev = info->st_dev;
+  monitor->st_ino = info->st_ino;
+  monitor->desc = descriptor;
+  monitor->next = Monitor;
+  if (info->magic == MUTT_MH)
+    monitor->mh_backup_path = mutt_str_strdup(info->path);
+
+  Monitor = monitor;
+
+  return monitor;
+}
+
+static void monitor_delete(struct Monitor *monitor)
+{
+  struct Monitor **ptr = &Monitor;
+
+  if (!monitor)
+    return;
+
+  while (true)
+  {
+    if (!*ptr)
+      return;
+    if (*ptr == monitor)
+      break;
+    ptr = &(*ptr)->next;
+  }
+
+  FREE(&monitor->mh_backup_path); /* __FREE_CHECKED__ */
+  monitor = monitor->next;
+  FREE(ptr); /* __FREE_CHECKED__ */
+  *ptr = monitor;
+}
+
+static int monitor_handle_ignore(int desc)
+{
+  int new_descr = -1;
+  struct Monitor *iter = Monitor;
+  struct stat sb;
+
+  while (iter && iter->desc != desc)
+    iter = iter->next;
+
+  if (iter)
+  {
+    if (iter->magic == MUTT_MH && stat(iter->mh_backup_path, &sb) == 0)
+    {
+      if ((new_descr = inotify_add_watch(INotifyFd, iter->mh_backup_path,
+                                         INOTIFY_MASK_FILE)) == -1)
+      {
+        mutt_debug(2, "inotify_add_watch failed for '%s', errno=%d %s\n",
+                   iter->mh_backup_path, errno, strerror(errno));
+      }
+      else
+      {
+        mutt_debug(3, "inotify_add_watch descriptor=%d for '%s'\n", desc, iter->mh_backup_path);
+        iter->st_dev = sb.st_dev;
+        iter->st_ino = sb.st_ino;
+        iter->desc = new_descr;
+      }
+    }
+    else
+    {
+      mutt_debug(3, "cleanup watch (implicitely removed) - descriptor=%d\n", desc);
+    }
+
+    if (new_descr == -1)
+    {
+      monitor_delete(iter);
+      monitor_check_free();
+    }
+  }
+
+  return new_descr;
+}
+
+#define EVENT_BUFLEN MAX(4096, sizeof(struct inotify_event) + NAME_MAX + 1)
+
+/* mutt_monitor_poll: Waits for I/O ready file descriptors or signals.
+ *
+ * return values:
+ *      -3   unknown/unexpected events: poll timeout / fds not handled by us
+ *      -2   monitor detected changes, no STDIN input
+ *      -1   error (see errno)
+ *       0   (1) input ready from STDIN, or (2) monitoring inactive -> no poll()
+ * MonitorFilesChanged also reflects changes to monitored files.
+ *
+ * Only STDIN and INotify file handles currently expected/supported.
+ * More would ask for common infrastructur (sockets?).
+ */
+int mutt_monitor_poll(void)
+{
+  int rc = 0, fds, i, inputReady;
+  char buf[EVENT_BUFLEN] __attribute__((aligned(__alignof__(struct inotify_event))));
+
+  MonitorFilesChanged = 0;
+
+  if (INotifyFd != -1)
+  {
+    fds = poll(PollFds, PollFdsLen, -1);
+
+    if (fds == -1)
+    {
+      rc = -1;
+      if (errno != EINTR)
+      {
+        mutt_debug(2, "poll() failed, errno=%d %s\n", errno, strerror(errno));
+      }
+    }
+    else
+    {
+      inputReady = 0;
+      for (i = 0; fds && i < PollFdsCount; ++i)
+      {
+        if (PollFds[i].revents)
+        {
+          fds--;
+          if (PollFds[i].fd == 0)
+          {
+            inputReady = 1;
+          }
+          else if (PollFds[i].fd == INotifyFd)
+          {
+            MonitorFilesChanged = 1;
+            mutt_debug(3, "file change(s) detected\n");
+            int len;
+            char *ptr = buf;
+            const struct inotify_event *event;
+
+            while (true)
+            {
+              len = read(INotifyFd, buf, sizeof(buf));
+              if (len == -1)
+              {
+                if (errno != EAGAIN)
+                  mutt_debug(2, "read inotify events failed, errno=%d %s\n",
+                             errno, strerror(errno));
+                break;
+              }
+
+              while (ptr < buf + len)
+              {
+                event = (const struct inotify_event *) ptr;
+                mutt_debug(5, "+ detail: descriptor=%d mask=0x%x\n", event->wd,
+                           event->mask);
+                if (event->mask & IN_IGNORED)
+                  monitor_handle_ignore(event->wd);
+                ptr += sizeof(struct inotify_event) + event->len;
+              }
+            }
+          }
+        }
+      }
+      if (!inputReady)
+        rc = MonitorFilesChanged ? -2 : -3;
+    }
+  }
+
+  return rc;
+}
+
+#define RESOLVERES_OK_NOTEXISTING 0
+#define RESOLVERES_OK_EXISTING 1
+#define RESOLVERES_FAIL_NOMAILBOX -3
+#define RESOLVERES_FAIL_NOMAGIC -2
+#define RESOLVERES_FAIL_STAT -1
+
+/* monitor_resolve: resolve monitor entry match by Mailbox, or - if NULL - by Context.
+ *
+ * return values:
+ *      >=0   mailbox is valid and locally accessible:
+ *              0: no monitor / 1: preexisting monitor
+ *       -3   no mailbox (MONITORINFO: no fields set)
+ *       -2   magic not set
+ *       -1   stat() failed (see errno; MONITORINFO fields: magic, isdir, path)
+ */
+static int monitor_resolve(struct MonitorInfo *info, struct Mailbox *mailbox)
+{
+  struct Monitor *iter;
+  char *fmt = NULL;
+  struct stat sb;
+
+  if (mailbox)
+  {
+    info->magic = mailbox->magic;
+    info->path = mailbox->realpath;
+  }
+  else if (Context)
+  {
+    info->magic = Context->magic;
+    info->path = Context->realpath;
+  }
+  else
+  {
+    return RESOLVERES_FAIL_NOMAILBOX;
+  }
+
+  if (!info->magic)
+  {
+    return RESOLVERES_FAIL_NOMAGIC;
+  }
+  else if (info->magic == MUTT_MAILDIR)
+  {
+    info->isdir = 1;
+    fmt = "%s/new";
+  }
+  else
+  {
+    info->isdir = 0;
+    if (info->magic == MUTT_MH)
+      fmt = "%s/.mh_sequences";
+  }
+
+  if (fmt)
+  {
+    snprintf(info->path_buf, sizeof(info->path_buf), fmt, info->path);
+    info->path = info->path_buf;
+  }
+  if (stat(info->path, &sb) != 0)
+    return RESOLVERES_FAIL_STAT;
+
+  iter = Monitor;
+  while (iter && (iter->st_ino != sb.st_ino || iter->st_dev != sb.st_dev))
+    iter = iter->next;
+
+  info->st_dev = sb.st_dev;
+  info->st_ino = sb.st_ino;
+  info->monitor = iter;
+
+  return iter ? RESOLVERES_OK_EXISTING : RESOLVERES_OK_NOTEXISTING;
+}
+
+/* mutt_monitor_add: add file monitor from Mailbox, or - if NULL - from Context.
+ *
+ * return values:
+ *       0   success: new or already existing monitor
+ *      -1   failed:  no mailbox, inaccessible file, create monitor/watcher failed
+ */
+int mutt_monitor_add(struct Mailbox *mailbox)
+{
+  struct MonitorInfo info;
+  uint32_t mask;
+  int desc;
+
+  desc = monitor_resolve(&info, mailbox);
+  if (desc != RESOLVERES_OK_NOTEXISTING)
+    return desc == RESOLVERES_OK_EXISTING ? 0 : -1;
+
+  mask = info.isdir ? INOTIFY_MASK_DIR : INOTIFY_MASK_FILE;
+  if ((INotifyFd == -1 && monitor_init() == -1) ||
+      (desc = inotify_add_watch(INotifyFd, info.path, mask)) == -1)
+  {
+    mutt_debug(2, "inotify_add_watch failed for '%s', errno=%d %s\n", info.path,
+               errno, strerror(errno));
+    return -1;
+  }
+
+  mutt_debug(3, "inotify_add_watch descriptor=%d for '%s'\n", desc, info.path);
+  monitor_create(&info, desc);
+  return 0;
+}
+
+/* mutt_monitor_remove: remove file monitor from Mailbox, or - if NULL - from Context.
+ *
+ * return values:
+ *       0   monitor removed (not shared)
+ *       1   monitor not removed (shared)
+ *       2   no monitor
+ */
+int mutt_monitor_remove(struct Mailbox *mailbox)
+{
+  struct MonitorInfo info, info2;
+
+  if (monitor_resolve(&info, mailbox) != RESOLVERES_OK_EXISTING)
+    return 2;
+
+  if (Context)
+  {
+    if (mailbox)
+    {
+      if (monitor_resolve(&info2, NULL) == RESOLVERES_OK_EXISTING &&
+          info.st_ino == info2.st_ino && info.st_dev == info2.st_dev)
+        return 1;
+    }
+    else
+    {
+      if (mutt_find_mailbox(Context->realpath))
+        return 1;
+    }
+  }
+
+  inotify_rm_watch(info.monitor->desc, INotifyFd);
+  mutt_debug(3, "inotify_rm_watch for '%s' descriptor=%d\n", info.path,
+             info.monitor->desc);
+
+  monitor_delete(info.monitor);
+  monitor_check_free();
+  return 0;
+}
diff --git a/monitor.h b/monitor.h
new file mode 100644 (file)
index 0000000..1159abe
--- /dev/null
+++ b/monitor.h
@@ -0,0 +1,34 @@
+/**
+ * @file
+ * Monitor files for changes
+ *
+ * @authors
+ * Copyright (C) 2018 Gero Treuer <gero@70t.de>
+ *
+ * @copyright
+ * This program 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MUTT_MONITOR_H
+#define _MUTT_MONITOR_H
+
+extern int MonitorFilesChanged;
+
+struct Mailbox;
+
+int mutt_monitor_add(struct Mailbox *b);
+int mutt_monitor_remove(struct Mailbox *b);
+int mutt_monitor_poll(void);
+
+#endif /* _MUTT_MONITOR_H */
index 0906b372e4b4a746ca5c44c9487d7838643044cc..4ac06e2f4985d365baaa149cbf3768ff068c2cf8 100644 (file)
--- a/version.c
+++ b/version.c
@@ -38,6 +38,9 @@
 #include "email/email.h"
 #include "mutt_curses.h"
 #include "ncrypt/crypt_gpgme.h"
+#ifdef USE_INOTIFY
+#include "monitor.h"
+#endif
 
 /* #include "protos.h" */
 const char *mutt_make_version(void);
@@ -212,6 +215,11 @@ static struct CompileOptions comp_opts[] = {
 #else
   { "idn", 0 },
 #endif
+#ifdef USE_INOTIFY
+  { "inotify", 1 },
+#else
+  { "inotify", 0 },
+#endif
 #ifdef LOCALES_HACK
   { "locales_hack", 1 },
 #else