X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=innduct.git;a=blobdiff_plain;f=filemon.c;h=a293fe85e7d3e0bfd1a4ab9b04ec0a299f142c1d;hp=9a362dbd7dfe0ca11edb950dccfd405c16eed377;hb=229c6d070b1ddb2cebb0a3f12bc71713795cd627;hpb=f4aee95c41a0d6231d115386b8fbb23f6b8e349a diff --git a/filemon.c b/filemon.c index 9a362db..a293fe8 100644 --- 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 + * + * 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 . + * + * (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) @@ -5,84 +33,89 @@ #include -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; iipf, 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; } -