chiark / gitweb /
Adjust INNLOGSET to have debug instead of trace, as current libinn2 does
[innduct.git] / filemon.c
index 9a362dbd7dfe0ca11edb950dccfd405c16eed377..a293fe85e7d3e0bfd1a4ab9b04ec0a299f142c1d 100644 (file)
--- a/filemon.c
+++ b/filemon.c
@@ -1,3 +1,31 @@
+/*
+ *  innduct
+ *  tailing reliable realtime streaming feeder for inn
+ *  filemon.c - file monitoring (inotify, kqueue, poll, etc.)
+ *
+ *  Copyright (C) 2010 Ian Jackson <ijackson@chiark.greenend.org.uk>
+ * 
+ *  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 3 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/>.
+ *
+ *  (I believe that when you compile and link this as part of the inn2
+ *  build, with the Makefile runes I have provided, all the libraries
+ *  and files which end up included in innduct are licence-compatible
+ *  with GPLv3.  If not then please let me know.  -Ian Jackson.)
+ */
+
+#include "innduct.h"
+
 /*---------- filemon implemented with inotify ----------*/
 
 #if defined(HAVE_SYS_INOTIFY_H) && !defined(HAVE_FILEMON)
 
 #include <sys/inotify.h>
 
-static int filemon_inotify_fd;
-static int filemon_inotify_wdmax;
-static InputFile **filemon_inotify_wd2ipf;
+DEFLIST(Filemon_Perfile);
 
 struct Filemon_Perfile {
+  ISNODE(Filemon_Perfile);
+  InputFile *ipf;
   int wd;
 };
 
-static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) {
-  int wd= inotify_add_watch(filemon_inotify_fd, ipf->path, IN_MODIFY);
-  if (wd < 0) sysdie("inotify_add_watch %s", ipf->path);
-
-  if (wd >= filemon_inotify_wdmax) {
-    int newmax= wd+2;
-    filemon_inotify_wd2ipf= xrealloc(filemon_inotify_wd2ipf,
-                                sizeof(*filemon_inotify_wd2ipf) * newmax);
-    memset(filemon_inotify_wd2ipf + filemon_inotify_wdmax, 0,
-          sizeof(*filemon_inotify_wd2ipf) * (newmax - filemon_inotify_wdmax));
-    filemon_inotify_wdmax= newmax;
-  }
+static int filemon_inotify_fd;
+static Filemon_PerfileList filemon_inotify_watches;
 
-  assert(!filemon_inotify_wd2ipf[wd]);
-  filemon_inotify_wd2ipf[wd]= ipf;
+static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) {
+  pf->ipf= ipf;
 
-  dbg("filemon inotify startfile %p wd=%d wdmax=%d",
-      ipf, wd, filemon_inotify_wdmax);
+  pf->wd= inotify_add_watch(filemon_inotify_fd, ipf->path, IN_MODIFY);
+  if (pf->wd < 0) sysdie("filemon inotify: inotify_add_watch %s", ipf->path);
 
-  pf->wd= wd;
+  LIST_ADDHEAD(filemon_inotify_watches, pf);
+  dbg("filemon inotify: startfile %p wd=%d pf=%p", ipf, pf->wd, pf);
 }
 
 static void filemon_method_stopfile(InputFile *ipf, Filemon_Perfile *pf) {
-  int wd= pf->wd;
-  dbg("filemon inotify stopfile %p wd=%d", ipf, wd);
-  int r= inotify_rm_watch(filemon_inotify_fd, wd);
-  if (r) syscrash("inotify_rm_watch");
-  filemon_inotify_wd2ipf[wd]= 0;
+  dbg("filemon inotify: stopfile %p wd=%d pf=%p", ipf, pf->wd, pf);
+  int r= inotify_rm_watch(filemon_inotify_fd, pf->wd);
+  if (r) syscrash("filemon inotify: inotify_rm_watch");
+  LIST_REMOVE(filemon_inotify_watches, pf);
 }
 
 static void *filemon_inotify_readable(oop_source *lp, int fd,
                                      oop_event e, void *u) {
   struct inotify_event iev;
+  InputFile *ipf;
   for (;;) {
     int r= read(filemon_inotify_fd, &iev, sizeof(iev));
     if (r==-1) {
       if (isewouldblock(errno)) break;
-      syscrash("read from inotify master");
-    } else if (r==sizeof(iev)) {
-      assert(iev.wd >= 0 && iev.wd < filemon_inotify_wdmax);
-    } else {
-      crash("inotify read %d bytes wanted struct of %d", r, (int)sizeof(iev));
+      syscrash("filemon inotify: read from inotify master");
+    } else if (r!=sizeof(iev)) {
+      crash("filemon inotify: read %d bytes when wanted struct of %d",
+           r, (int)sizeof(iev));
     }
-    InputFile *ipf= filemon_inotify_wd2ipf[iev.wd];
+    Filemon_Perfile *pf;
+    FOR_LIST_NODE(pf, filemon_inotify_watches)
+      if (pf->wd == iev.wd) goto found;
+    /* Linux seems to remember events and can produce them even after
+     * you've removed the watch.  This means that we can't spot bugs
+     * where we lose track of our watches and have to just regard
+     * unexpected random watch events as normal.  It's not a
+     * correctness problem as the watch is just a prod to read a file,
+     * which is harmless if it does not need to be read. */
+    dbg("filemon inotify: read event with unknown wd=%d", iev.wd);
+    continue;
+    
+  found:
+    ipf= pf->ipf;
     /*dbg("filemon inotify readable read %p wd=%d", ipf, iev.wd);*/
     tailing_make_readable(ipf);
   }
   return OOP_CONTINUE;
 }
 
-static int filemon_method_init(void) {
+int filemon_method_init(void) {
+  LIST_INIT(filemon_inotify_watches);
   filemon_inotify_fd= inotify_init();
   if (filemon_inotify_fd<0) {
-    syswarn("filemon/inotify: inotify_init failed");
+    syswarn("filemon inotify: inotify_init failed");
     return 0;
   }
   xsetnonblock(filemon_inotify_fd, 1);
   loop->on_fd(loop, filemon_inotify_fd, OOP_READ, filemon_inotify_readable, 0);
 
-  dbg("filemon inotify init filemon_inotify_fd=%d", filemon_inotify_fd);
+  dbg("filemon inotify: init filemon_inotify_fd=%d", filemon_inotify_fd);
   return 1;
 }
 
-static void filemon_method_dump_info(FILE *f) {
-  int i;
+void filemon_method_dump_info(FILE *f) {
   fprintf(f,"inotify");
   DUMPV("%d",,filemon_inotify_fd);
-  DUMPV("%d",,filemon_inotify_wdmax);
-  for (i=0; i<filemon_inotify_wdmax; i++)
-    fprintf(f," wd2ipf[%d]=%p\n", i, filemon_inotify_wd2ipf[i]);
+  DUMPV("%d",filemon_inotify_watches.,count);
+  fprintf(f,"\n");
+  Filemon_Perfile *pf;
+  FOR_LIST_NODE(pf, filemon_inotify_watches)
+    fprintf(f," watching %p wd=%d pf=%p\n", pf->ipf, pf->wd, pf);
 }
 
 #endif /* HAVE_INOTIFY && !HAVE_FILEMON */
@@ -93,29 +126,28 @@ static void filemon_method_dump_info(FILE *f) {
 
 struct Filemon_Perfile { int dummy; };
 
-static int filemon_method_init(void) {
-  warn("filemon/dummy: no filemon method compiled in");
+int filemon_method_init(void) {
+  warn("filemon dummy: no filemon method compiled in");
   return 0;
 }
 static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) { }
 static void filemon_method_stopfile(InputFile *ipf, Filemon_Perfile *pf) { }
-static void filemon_method_dump_info(FILE *f) { fprintf(f,"dummy\n"); }
+void filemon_method_dump_info(FILE *f) { fprintf(f,"dummy\n"); }
 
 #endif /* !HAVE_FILEMON */
 
 /*---------- filemon generic interface ----------*/
 
-static void filemon_start(InputFile *ipf) {
+void filemon_start(InputFile *ipf) {
   assert(!ipf->filemon);
 
   NEW(ipf->filemon);
   filemon_method_startfile(ipf, ipf->filemon);
 }
 
-static void filemon_stop(InputFile *ipf) {
+void filemon_stop(InputFile *ipf) {
   if (!ipf->filemon) return;
   filemon_method_stopfile(ipf, ipf->filemon);
   free(ipf->filemon);
   ipf->filemon= 0;
 }
-