chiark / gitweb /
Add innduct-forall
[innduct.git] / filemon.c
1 /*
2  *  innduct
3  *  tailing reliable realtime streaming feeder for inn
4  *  filemon.c - file monitoring (inotify, kqueue, poll, etc.)
5  *
6  *  Copyright (C) 2010 Ian Jackson <ijackson@chiark.greenend.org.uk>
7  * 
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.
12  * 
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.
17  * 
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/>.
20  *
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.)
25  */
26
27 #include "innduct.h"
28
29 /*---------- filemon implemented with inotify ----------*/
30
31 #if defined(HAVE_SYS_INOTIFY_H) && !defined(HAVE_FILEMON)
32 #define HAVE_FILEMON
33
34 #include <sys/inotify.h>
35
36 DEFLIST(Filemon_Perfile);
37
38 struct Filemon_Perfile {
39   ISNODE(Filemon_Perfile);
40   InputFile *ipf;
41   int wd;
42 };
43
44 static int filemon_inotify_fd;
45 static Filemon_PerfileList filemon_inotify_watches;
46
47 static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) {
48   pf->ipf= ipf;
49
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);
52
53   LIST_ADDHEAD(filemon_inotify_watches, pf);
54   dbg("filemon inotify: startfile %p wd=%d pf=%p", ipf, pf->wd, pf);
55 }
56
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);
62 }
63
64 static void *filemon_inotify_readable(oop_source *lp, int fd,
65                                       oop_event e, void *u) {
66   struct inotify_event iev;
67   InputFile *ipf;
68   for (;;) {
69     int r= read(filemon_inotify_fd, &iev, sizeof(iev));
70     if (r==-1) {
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",
75             r, (int)sizeof(iev));
76     }
77     Filemon_Perfile *pf;
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);
87     continue;
88     
89   found:
90     ipf= pf->ipf;
91     /*dbg("filemon inotify readable read %p wd=%d", ipf, iev.wd);*/
92     tailing_make_readable(ipf);
93   }
94   return OOP_CONTINUE;
95 }
96
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");
102     return 0;
103   }
104   xsetnonblock(filemon_inotify_fd, 1);
105   loop->on_fd(loop, filemon_inotify_fd, OOP_READ, filemon_inotify_readable, 0);
106
107   dbg("filemon inotify: init filemon_inotify_fd=%d", filemon_inotify_fd);
108   return 1;
109 }
110
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);
115   fprintf(f,"\n");
116   Filemon_Perfile *pf;
117   FOR_LIST_NODE(pf, filemon_inotify_watches)
118     fprintf(f," watching %p wd=%d pf=%p\n", pf->ipf, pf->wd, pf);
119 }
120
121 #endif /* HAVE_INOTIFY && !HAVE_FILEMON */
122
123 /*---------- filemon dummy implementation ----------*/
124
125 #if !defined(HAVE_FILEMON)
126
127 struct Filemon_Perfile { int dummy; };
128
129 int filemon_method_init(void) {
130   warn("filemon dummy: no filemon method compiled in");
131   return 0;
132 }
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"); }
136
137 #endif /* !HAVE_FILEMON */
138
139 /*---------- filemon generic interface ----------*/
140
141 void filemon_start(InputFile *ipf) {
142   assert(!ipf->filemon);
143
144   NEW(ipf->filemon);
145   filemon_method_startfile(ipf, ipf->filemon);
146 }
147
148 void filemon_stop(InputFile *ipf) {
149   if (!ipf->filemon) return;
150   filemon_method_stopfile(ipf, ipf->filemon);
151   free(ipf->filemon);
152   ipf->filemon= 0;
153 }