File: filemon.c

package info (click to toggle)
innduct 2.2
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 664 kB
  • sloc: sh: 4,270; ansic: 3,114; perl: 130; makefile: 33
file content (137 lines) | stat: -rw-r--r-- 4,126 bytes parent folder | download
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;
}