]> mj.ucw.cz Git - checkmail.git/commitdiff
Add inotify
authorJiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
Sun, 18 Jul 2021 13:21:03 +0000 (15:21 +0200)
committerMartin Mares <mj@ucw.cz>
Fri, 21 Jan 2022 20:55:55 +0000 (21:55 +0100)
Makefile
cm.c
maint/check-compile

index ca9c60f4c1db23eca977a9fd8611a0993f6a907e..1022e722d8d62c2a45d845037702d3ace797869f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,9 @@ CONFIG_WIDE_CURSES=1
 # Define if you want XKB led controls and on-screen display via OSDD
 CONFIG_X11=1
 
+# Define if you want watching mail changes by inotify
+CONFIG_INOTIFY=1
+
 #DEBUG=-ggdb
 CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Winline $(DEBUG) -std=gnu99 -DVERSION=$(VERSION) -DYEAR=$(YEAR)
 
@@ -19,6 +22,10 @@ LDFLAGS+=-lX11
 CFLAGS+=-DCONFIG_X11=1
 endif
 
+ifeq ($(CONFIG_INOTIFY),1)
+CFLAGS+=-DCONFIG_INOTIFY=1
+endif
+
 VERSION=1.11
 YEAR=2018
 
diff --git a/cm.c b/cm.c
index bec8a4530e67d4676fa87049bcd7d5346b238d68..155a9208dce74f0d6b1ff4773ad628e4887706d1 100644 (file)
--- a/cm.c
+++ b/cm.c
@@ -22,6 +22,7 @@
 #include <sys/wait.h>
 #include <time.h>
 #include <unistd.h>
+#include <errno.h>
 
 #ifdef CONFIG_WIDE_CURSES
 #include <ncurses.h>
@@ -79,8 +80,16 @@ static struct options global_options = {
 
 #define MDIR_MAX_NAME_LEN 128
 
+enum inotify_status
+{
+  INOTIFY_OFF,
+  INOTIFY_ON,
+  INOTIFY_ERROR,
+};
+
 struct mbox {
   cnode n;
+  enum inotify_status inotify_status;
   struct options o;
   char *name;
   char *path;
@@ -116,6 +125,12 @@ static clist osd_opts;
 static void redraw_line(int i);
 static void rethink_display(int notify);
 
+static int inotify_fd;
+static bool inotify_active;
+static void inotify_cm_init(void);
+static void inotify_add_mbox(struct mbox * b);
+static void inotify_delete_mbox(struct mbox * b);
+
 static void
 add_pattern(char *patt)
 {
@@ -217,6 +232,7 @@ new_mbox(char *path, char *name)
 {
   struct mbox *b = xmalloc(sizeof(*b));
   bzero(b, sizeof(*b));
+  b->inotify_status = INOTIFY_OFF;
   b->path = xstrdup(path);
   b->name = xstrdup(name);
   return b;
@@ -776,6 +792,14 @@ scan(int notify)
     }
 
   struct mbox *tmp;
+  if(inotify_active)
+  CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
+    {
+      if(b->inotify_status != INOTIFY_ON)
+       inotify_add_mbox(b);
+    }
+
+
   CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
     {
       if (b->seen)
@@ -783,6 +807,7 @@ scan(int notify)
       else
        {
          debug("Lost mailbox %s\n", b->name);
+         inotify_delete_mbox(b);
          del_mbox(b);
        }
     }
@@ -1539,6 +1564,142 @@ handle_incsearch(int ch)
   return 1;
 }
 
+static void
+without_inotify_wait(int timeout)
+{
+  timeout *= 10;
+  halfdelay((timeout > 255) ? 255 : timeout);
+}
+#ifdef CONFIG_INOTIFY
+
+#include <sys/inotify.h>
+
+static void
+inotify_cm_init(void)
+{
+  if (!inotify_active) return;
+  inotify_fd = inotify_init();
+  if (inotify_fd < 0)
+    die("Inotify init faild.\n");
+}
+
+static void
+inotify_add_mbox(struct mbox * b)
+{
+  if (!inotify_active) return;
+  b->inotify_status = INOTIFY_ON;
+  char *path = xmalloc(strlen(b->path)+100);
+  struct stat st;
+  if (stat(b->path, &st) < 0)
+    {
+      debug("Get stat of %s faild\n", b->path);
+      return;
+    }
+  if (S_ISREG(st.st_mode))
+    {
+      // Regular mailbox
+      sprintf(path, "%s", b->path);
+      if (inotify_add_watch(inotify_fd, path,  IN_MOVED_TO | IN_MOVED_FROM | IN_DELETE | IN_CLOSE_WRITE ) < 0)
+       {
+         debug("Inotify add %s faild\n", path);
+         b->inotify_status = INOTIFY_ERROR;
+       }
+      else
+         debug("Inotify add %s OK\n", path);
+    }
+  else if (S_ISDIR(st.st_mode))
+    {
+      // Maildir
+      char *watch_dir[] = {"cur", "new"};
+      for (int i=0; i < (int)(sizeof(watch_dir)/sizeof(*watch_dir)); i++)
+       {
+         sprintf(path, "%s/%s", b->path, watch_dir[i]);
+         if (inotify_add_watch(inotify_fd, path,  IN_MOVED_TO | IN_MOVED_FROM | IN_DELETE | IN_CLOSE_WRITE ) < 0)
+           {
+             debug("Inotify add %s faild\n", path);
+             b->inotify_status = INOTIFY_ERROR;
+           }
+       }
+    }
+  else
+    {
+      debug("neither file nor directory\n");
+      b->inotify_status = INOTIFY_ERROR;
+      return;
+    }
+  free(path);
+}
+
+static void
+inotify_delete_mbox(struct mbox * b)
+{
+  (void)b;
+  // TODO
+}
+
+static bool // true if inotify change detected
+inotify_wait(int timeout)
+{
+  if(!inotify_active)
+    {
+      without_inotify_wait(timeout);
+      return 0;
+    }
+
+  bool inotify_change = 0;
+
+  struct timeval tv;
+  tv.tv_sec = timeout;
+  tv.tv_usec = 0;
+  while (true)
+    {
+      fd_set rfds;
+      FD_ZERO(&rfds);
+      FD_SET(0, &rfds);
+      FD_SET(inotify_fd, &rfds);
+
+      debug("Select begin (timeout %d %d)\n", tv.tv_sec, tv.tv_usec);
+      int ret = select(1+inotify_fd, &rfds, NULL, NULL, &tv);
+      debug("Select end\n");
+      if (ret>0 && FD_ISSET(0, &rfds))
+       return inotify_change;
+      if (ret>0 && FD_ISSET(inotify_fd, &rfds))
+       {
+         debug("Read notify begin\n");
+         char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
+         ssize_t len = read(inotify_fd, buf, sizeof(buf));
+         if (len == -1 && errno != EAGAIN)
+             perror ("read inotify faild");
+         inotify_change = 1;
+         debug("Read notify end\n");
+       }
+      else
+       return inotify_change;
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+    }
+}
+
+#else
+
+static void
+inotify_cm_init(void){}
+
+static void
+inotify_add_mbox(struct mbox *){}
+
+static void
+inotify_delete_mbox(struct mbox *){}
+
+static bool
+inotify_wait(int timeout)
+{
+  without_inotify_wait(timeout);
+  return 0;
+}
+
+#endif
+
 #define STR2(c) #c
 #define STR(c) STR2(c)
 
@@ -1557,6 +1718,7 @@ Options:\n\
 -p <pri>\t\tSet minimum priority to show\n\
 -s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\n\
 -t\t\t\tLet TAB select the next mailbox with new mail, no matter what priority it has\n\
+-f\t\t\tEnable inotify (wait for file system changes)\n\
 \n\
 On-screen display replacement (in all OSD values and notification lines):\n\
 %%%%\t\t%%\n\
@@ -1670,7 +1832,7 @@ main(int argc, char **argv)
   clist_init(&osd_opts);
 
   int c;
-  while ((c = getopt(argc, argv, "c:dim:o:p:s:t")) >= 0)
+  while ((c = getopt(argc, argv, "c:dim:o:p:s:tif")) >= 0)
     switch (c)
       {
       case 'c':
@@ -1699,6 +1861,9 @@ main(int argc, char **argv)
       case 't':
        simple_tab = 1;
        break;
+      case 'f':
+       inotify_active = 1;
+       break;
       default:
        usage();
       }
@@ -1708,9 +1873,12 @@ main(int argc, char **argv)
   charset_init();
   term_init();
   x11_init();
+  inotify_cm_init();
   scan_and_redraw(0);
   next_active(0, 1);
 
+
+  int inotify_rescan = 0;
   int should_exit = 0;
 restart:
   while (!should_exit)
@@ -1719,13 +1887,21 @@ restart:
       int remains = last_scan_time + check_interval - now;
       if (force_refresh)
        scan_and_redraw(0);
-      if (remains <= 0)
-       scan_and_redraw(1);
+      if (remains <= 0 || inotify_rescan)
+       {
+         if (inotify_rescan) debug("Inotify rescan\n");
+         inotify_rescan = 0;
+         scan_and_redraw(1);
+       }
       else
        {
-         remains *= 10;
-         halfdelay((remains > 255) ? 255 : remains);
+         nodelay(stdscr,1);
          int ch = getch();
+         if (ch < 0)
+           {
+             inotify_rescan |= inotify_wait(remains);
+             ch = getch();
+           }
          if (ch < 0)
            continue;
          if (is_active && handle_incsearch(ch))
index f29d79ec43a1e52de0da03c3170db1e56aa5fd49..5b125a8d1a43504de7e5ebbeef2845ab9751935c 100755 (executable)
@@ -1,4 +1,5 @@
 #!/bin/sh
+make clean
 echo "### Normal compile ###"
 make
 make clean
@@ -7,3 +8,7 @@ make CONFIG_WIDE_CURSES=0
 make clean
 echo "### No X11 ###"
 make CONFIG_X11=0
+make clean
+echo "### No INOTIFY ###"
+make CONFIG_INOTIFY=0
+make clean