+/*
+ * 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("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);
+ 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("inotify_rm_watch");
- filemon_inotify_wd2ipf[wd]= 0;
+ 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 {
+ } else if (r!=sizeof(iev)) {
crash("inotify read %d bytes 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("inotify read event on 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");
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 */
struct Filemon_Perfile { int dummy; };
-static int filemon_method_init(void) {
+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;
}
-