chiark / gitweb /
changelog: Finalise 2.2
[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 Ian Jackson <ijackson@chiark.greenend.org.uk>
7  *  and contributors; see LICENCE.txt.
8  *  SPDX-License-Identifier: GPL-3.0-or-later
9  */
10
11 #include "innduct.h"
12
13 /*---------- filemon implemented with inotify ----------*/
14
15 #if defined(HAVE_SYS_INOTIFY_H) && !defined(HAVE_FILEMON)
16 #define HAVE_FILEMON
17
18 #include <sys/inotify.h>
19
20 DEFLIST(Filemon_Perfile);
21
22 struct Filemon_Perfile {
23   ISNODE(Filemon_Perfile);
24   InputFile *ipf;
25   int wd;
26 };
27
28 static int filemon_inotify_fd;
29 static Filemon_PerfileList filemon_inotify_watches;
30
31 static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) {
32   pf->ipf= ipf;
33
34   pf->wd= inotify_add_watch(filemon_inotify_fd, ipf->path, IN_MODIFY);
35   if (pf->wd < 0) sysdie("filemon inotify: inotify_add_watch %s", ipf->path);
36
37   LIST_ADDHEAD(filemon_inotify_watches, pf);
38   dbg("filemon inotify: startfile %p wd=%d pf=%p", ipf, pf->wd, pf);
39 }
40
41 static void filemon_method_stopfile(InputFile *ipf, Filemon_Perfile *pf) {
42   dbg("filemon inotify: stopfile %p wd=%d pf=%p", ipf, pf->wd, pf);
43   int r= inotify_rm_watch(filemon_inotify_fd, pf->wd);
44   if (r) syscrash("filemon inotify: inotify_rm_watch");
45   LIST_REMOVE(filemon_inotify_watches, pf);
46 }
47
48 static void *filemon_inotify_readable(oop_source *lp, int fd,
49                                       oop_event e, void *u) {
50   struct inotify_event iev;
51   InputFile *ipf;
52   for (;;) {
53     int r= read(filemon_inotify_fd, &iev, sizeof(iev));
54     if (r==-1) {
55       if (isewouldblock(errno)) break;
56       syscrash("filemon inotify: read from inotify master");
57     } else if (r!=sizeof(iev)) {
58       crash("filemon inotify: read %d bytes when wanted struct of %d",
59             r, (int)sizeof(iev));
60     }
61     Filemon_Perfile *pf;
62     FOR_LIST_NODE(pf, filemon_inotify_watches)
63       if (pf->wd == iev.wd) goto found;
64     /* Linux seems to remember events and can produce them even after
65      * you've removed the watch.  This means that we can't spot bugs
66      * where we lose track of our watches and have to just regard
67      * unexpected random watch events as normal.  It's not a
68      * correctness problem as the watch is just a prod to read a file,
69      * which is harmless if it does not need to be read. */
70     dbg("filemon inotify: read event with unknown wd=%d", iev.wd);
71     continue;
72     
73   found:
74     ipf= pf->ipf;
75     /*dbg("filemon inotify readable read %p wd=%d", ipf, iev.wd);*/
76     tailing_make_readable(ipf);
77   }
78   return OOP_CONTINUE;
79 }
80
81 int filemon_method_init(void) {
82   LIST_INIT(filemon_inotify_watches);
83   filemon_inotify_fd= inotify_init();
84   if (filemon_inotify_fd<0) {
85     syswarn("filemon inotify: inotify_init failed");
86     return 0;
87   }
88   xsetnonblock(filemon_inotify_fd, 1);
89   loop->on_fd(loop, filemon_inotify_fd, OOP_READ, filemon_inotify_readable, 0);
90
91   dbg("filemon inotify: init filemon_inotify_fd=%d", filemon_inotify_fd);
92   return 1;
93 }
94
95 void filemon_method_dump_info(FILE *f) {
96   fprintf(f,"inotify");
97   DUMPV("%d",,filemon_inotify_fd);
98   DUMPV("%d",filemon_inotify_watches.,count);
99   fprintf(f,"\n");
100   Filemon_Perfile *pf;
101   FOR_LIST_NODE(pf, filemon_inotify_watches)
102     fprintf(f," watching %p wd=%d pf=%p\n", pf->ipf, pf->wd, pf);
103 }
104
105 #endif /* HAVE_INOTIFY && !HAVE_FILEMON */
106
107 /*---------- filemon dummy implementation ----------*/
108
109 #if !defined(HAVE_FILEMON)
110
111 struct Filemon_Perfile { int dummy; };
112
113 int filemon_method_init(void) {
114   warn("filemon dummy: no filemon method compiled in");
115   return 0;
116 }
117 static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) { }
118 static void filemon_method_stopfile(InputFile *ipf, Filemon_Perfile *pf) { }
119 void filemon_method_dump_info(FILE *f) { fprintf(f,"dummy\n"); }
120
121 #endif /* !HAVE_FILEMON */
122
123 /*---------- filemon generic interface ----------*/
124
125 void filemon_start(InputFile *ipf) {
126   assert(!ipf->filemon);
127
128   NEW(ipf->filemon);
129   filemon_method_startfile(ipf, ipf->filemon);
130 }
131
132 void filemon_stop(InputFile *ipf) {
133   if (!ipf->filemon) return;
134   filemon_method_stopfile(ipf, ipf->filemon);
135   free(ipf->filemon);
136   ipf->filemon= 0;
137 }