1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
/*
* innduct
* tailing reliable realtime streaming feeder for inn
* filemon.c - file monitoring (inotify, kqueue, poll, etc.)
*
* Copyright Ian Jackson <ijackson@chiark.greenend.org.uk>
* and contributors; see LICENCE.txt.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "innduct.h"
/*---------- filemon implemented with inotify ----------*/
#if defined(HAVE_SYS_INOTIFY_H) && !defined(HAVE_FILEMON)
#define HAVE_FILEMON
#include <sys/inotify.h>
DEFLIST(Filemon_Perfile);
struct Filemon_Perfile {
ISNODE(Filemon_Perfile);
InputFile *ipf;
int wd;
};
static int filemon_inotify_fd;
static Filemon_PerfileList filemon_inotify_watches;
static void filemon_method_startfile(InputFile *ipf, Filemon_Perfile *pf) {
pf->ipf= ipf;
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);
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) {
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("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));
}
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;
}
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 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);
return 1;
}
void filemon_method_dump_info(FILE *f) {
fprintf(f,"inotify");
DUMPV("%d",,filemon_inotify_fd);
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 */
/*---------- filemon dummy implementation ----------*/
#if !defined(HAVE_FILEMON)
struct Filemon_Perfile { int dummy; };
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) { }
void filemon_method_dump_info(FILE *f) { fprintf(f,"dummy\n"); }
#endif /* !HAVE_FILEMON */
/*---------- filemon generic interface ----------*/
void filemon_start(InputFile *ipf) {
assert(!ipf->filemon);
NEW(ipf->filemon);
filemon_method_startfile(ipf, ipf->filemon);
}
void filemon_stop(InputFile *ipf) {
if (!ipf->filemon) return;
filemon_method_stopfile(ipf, ipf->filemon);
free(ipf->filemon);
ipf->filemon= 0;
}
|