3 * tailing reliable realtime streaming feeder for inn
4 * filemon.c - file monitoring (inotify, kqueue, poll, etc.)
6 * Copyright (C) 2010 Ian Jackson <ijackson@chiark.greenend.org.uk>
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * (I believe that when you compile and link this as part of the inn2
22 * build, with the Makefile runes I have provided, all the libraries
23 * and files which end up included in innduct are licence-compatible
24 * with GPLv3. If not then please let me know. -Ian Jackson.)
29 /*---------- filemon implemented with inotify ----------*/
31 #if defined(HAVE_SYS_INOTIFY_H) && !defined(HAVE_FILEMON)
34 #include <sys/inotify.h>
36 DEFLIST(Filemon_Perfile);
38 struct Filemon_Perfile {
39 ISNODE(Filemon_Perfile);
44 static int filemon_inotify_fd;
45 static Filemon_PerfileList filemon_inotify_watches;
47 static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) {
50 pf->wd= inotify_add_watch(filemon_inotify_fd, ipf->path, IN_MODIFY);
51 if (pf->wd < 0) sysdie("filemon inotify: inotify_add_watch %s", ipf->path);
53 LIST_ADDHEAD(filemon_inotify_watches, pf);
54 dbg("filemon inotify: startfile %p wd=%d pf=%p", ipf, pf->wd, pf);
57 static void filemon_method_stopfile(InputFile *ipf, Filemon_Perfile *pf) {
58 dbg("filemon inotify: stopfile %p wd=%d pf=%p", ipf, pf->wd, pf);
59 int r= inotify_rm_watch(filemon_inotify_fd, pf->wd);
60 if (r) syscrash("filemon inotify: inotify_rm_watch");
61 LIST_REMOVE(filemon_inotify_watches, pf);
64 static void *filemon_inotify_readable(oop_source *lp, int fd,
65 oop_event e, void *u) {
66 struct inotify_event iev;
69 int r= read(filemon_inotify_fd, &iev, sizeof(iev));
71 if (isewouldblock(errno)) break;
72 syscrash("filemon inotify: read from inotify master");
73 } else if (r!=sizeof(iev)) {
74 crash("filemon inotify: read %d bytes when wanted struct of %d",
78 FOR_LIST_NODE(pf, filemon_inotify_watches)
79 if (pf->wd == iev.wd) goto found;
80 /* Linux seems to remember events and can produce them even after
81 * you've removed the watch. This means that we can't spot bugs
82 * where we lose track of our watches and have to just regard
83 * unexpected random watch events as normal. It's not a
84 * correctness problem as the watch is just a prod to read a file,
85 * which is harmless if it does not need to be read. */
86 dbg("filemon inotify: read event with unknown wd=%d", iev.wd);
91 /*dbg("filemon inotify readable read %p wd=%d", ipf, iev.wd);*/
92 tailing_make_readable(ipf);
97 int filemon_method_init(void) {
98 LIST_INIT(filemon_inotify_watches);
99 filemon_inotify_fd= inotify_init();
100 if (filemon_inotify_fd<0) {
101 syswarn("filemon inotify: inotify_init failed");
104 xsetnonblock(filemon_inotify_fd, 1);
105 loop->on_fd(loop, filemon_inotify_fd, OOP_READ, filemon_inotify_readable, 0);
107 dbg("filemon inotify: init filemon_inotify_fd=%d", filemon_inotify_fd);
111 void filemon_method_dump_info(FILE *f) {
112 fprintf(f,"inotify");
113 DUMPV("%d",,filemon_inotify_fd);
114 DUMPV("%d",filemon_inotify_watches.,count);
117 FOR_LIST_NODE(pf, filemon_inotify_watches)
118 fprintf(f," watching %p wd=%d pf=%p\n", pf->ipf, pf->wd, pf);
121 #endif /* HAVE_INOTIFY && !HAVE_FILEMON */
123 /*---------- filemon dummy implementation ----------*/
125 #if !defined(HAVE_FILEMON)
127 struct Filemon_Perfile { int dummy; };
129 int filemon_method_init(void) {
130 warn("filemon dummy: no filemon method compiled in");
133 static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) { }
134 static void filemon_method_stopfile(InputFile *ipf, Filemon_Perfile *pf) { }
135 void filemon_method_dump_info(FILE *f) { fprintf(f,"dummy\n"); }
137 #endif /* !HAVE_FILEMON */
139 /*---------- filemon generic interface ----------*/
141 void filemon_start(InputFile *ipf) {
142 assert(!ipf->filemon);
145 filemon_method_startfile(ipf, ipf->filemon);
148 void filemon_stop(InputFile *ipf) {
149 if (!ipf->filemon) return;
150 filemon_method_stopfile(ipf, ipf->filemon);