1 /* direvent - directory content watcher daemon
2 Copyright (C) 2012-2016 Sergey Poznyakoff
3
4 Direvent is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Direvent is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with direvent. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "direvent.h"
18 #include <dirent.h>
19 #include <sys/stat.h>
20
21 void
watchpoint_ref(struct watchpoint * wpt)22 watchpoint_ref(struct watchpoint *wpt)
23 {
24 ++wpt->refcnt;
25 }
26
27 void
watchpoint_unref(struct watchpoint * wpt)28 watchpoint_unref(struct watchpoint *wpt)
29 {
30 if (--wpt->refcnt)
31 return;
32 free(wpt->dirname);
33 handler_list_unref(wpt->handler_list);
34 free(wpt);
35 }
36
37
38 struct wpref {
39 int used;
40 struct watchpoint *wpt;
41 };
42
43 static unsigned
wpref_hash(void * data,unsigned long hashsize)44 wpref_hash(void *data, unsigned long hashsize)
45 {
46 struct wpref *sym = data;
47 return grecs_hash_string(sym->wpt->dirname, hashsize);
48 }
49
50 static int
wpref_cmp(const void * a,const void * b)51 wpref_cmp(const void *a, const void *b)
52 {
53 struct wpref const *syma = a;
54 struct wpref const *symb = b;
55
56 return strcmp(syma->wpt->dirname, symb->wpt->dirname);
57 }
58
59 static int
wpref_copy(void * a,void * b)60 wpref_copy(void *a, void *b)
61 {
62 struct wpref *syma = a;
63 struct wpref *symb = b;
64
65 syma->used = 1;
66 syma->wpt = symb->wpt;
67 return 0;
68 }
69
70 static void
wpref_free(void * p)71 wpref_free(void *p)
72 {
73 struct wpref *wpref = p;
74 watchpoint_unref(wpref->wpt);
75 free(wpref);
76 }
77
78 struct grecs_symtab *nametab;
79
80 struct watchpoint *
watchpoint_install(const char * path,int * pnew)81 watchpoint_install(const char *path, int *pnew)
82 {
83 struct watchpoint wpkey;
84 struct wpref key;
85 struct wpref *ent;
86 int install = 1;
87
88 if (!nametab) {
89 nametab = grecs_symtab_create(sizeof(struct wpref),
90 wpref_hash, wpref_cmp, wpref_copy,
91 NULL, wpref_free);
92 if (!nametab) {
93 diag(LOG_CRIT, _("not enough memory"));
94 exit(1);
95 }
96 }
97
98 wpkey.dirname = (char*) path;
99 key.wpt = &wpkey;
100 ent = grecs_symtab_lookup_or_install(nametab, &key, &install);
101 if (install) {
102 struct watchpoint *wpt = ecalloc(1, sizeof(*wpt));
103 wpt->dirname = estrdup(path);
104 wpt->wd = -1;
105 wpt->handler_list = handler_list_create();
106 wpt->refcnt = 0;
107 ent->wpt = wpt;
108 }
109 if (!ent)
110 abort(); /* FIXME */
111 watchpoint_ref(ent->wpt);
112 if (pnew)
113 *pnew = install;
114 return ent->wpt;
115 }
116
117 struct watchpoint *
watchpoint_install_ptr(struct watchpoint * wpt)118 watchpoint_install_ptr(struct watchpoint *wpt)
119 {
120 struct wpref key;
121 int install = 1;
122 key.wpt = wpt;
123
124 if (!grecs_symtab_lookup_or_install(nametab, &key, &install)) {
125 diag(LOG_CRIT, _("not enough memory"));
126 exit(1);
127 }
128 watchpoint_ref(wpt);
129 return wpt;
130 }
131
132 static void
wpref_destroy(void * data)133 wpref_destroy(void *data)
134 {
135 struct watchpoint *wpt = data;
136 watchpoint_destroy(wpt);
137 }
138
139 static grecs_list_ptr_t watchpoint_gc_list;
140
141 void
watchpoint_gc(void)142 watchpoint_gc(void)
143 {
144 if (watchpoint_gc_list) {
145 grecs_list_free(watchpoint_gc_list);
146 watchpoint_gc_list = NULL;
147 }
148 }
149
150 struct watchpoint *
watchpoint_lookup(const char * dirname)151 watchpoint_lookup(const char *dirname)
152 {
153 struct watchpoint wpkey;
154 struct wpref key;
155 struct wpref *ent;
156
157 if (!nametab)
158 return NULL;
159
160 wpkey.dirname = (char*) dirname;
161 key.wpt = &wpkey;
162 ent = grecs_symtab_lookup_or_install(nametab, &key, NULL);
163 return ent ? ent->wpt : NULL;
164 }
165
166 static void
watchpoint_remove(const char * dirname)167 watchpoint_remove(const char *dirname)
168 {
169 struct watchpoint wpkey;
170 struct wpref key;
171
172 if (!nametab)
173 return;
174
175 wpkey.dirname = (char*) dirname;
176 key.wpt = &wpkey;
177 grecs_symtab_remove(nametab, &key);
178 }
179
180 void
watchpoint_destroy(struct watchpoint * wpt)181 watchpoint_destroy(struct watchpoint *wpt)
182 {
183 debug(1, (_("removing watcher %s"), wpt->dirname));
184 sysev_rm_watch(wpt);
185 watchpoint_remove(wpt->dirname);
186 }
187
188 void
watchpoint_suspend(struct watchpoint * wpt)189 watchpoint_suspend(struct watchpoint *wpt)
190 {
191 if (!wpt->parent) /* A top-level watchpoint */
192 watchpoint_install_sentinel(wpt);//FIXME: error checking
193 watchpoint_destroy(wpt);
194 if (grecs_symtab_count(nametab) == 0) {
195 diag(LOG_CRIT, _("no watchers left; exiting now"));
196 stop = 1;
197 }
198 }
199
200 struct sentinel {
201 struct handler *hp;
202 struct watchpoint *watchpoint;
203 };
204
205 static int
sentinel_handler_run(struct watchpoint * wp,event_mask * event,const char * dirname,const char * file,void * data)206 sentinel_handler_run(struct watchpoint *wp, event_mask *event,
207 const char *dirname, const char *file, void *data)
208 {
209 struct sentinel *sentinel = data;
210 struct watchpoint *wpt = sentinel->watchpoint;
211
212 watchpoint_init(wpt);
213 watchpoint_install_ptr(wpt);
214 deliver_ev_create(wpt, dirname, file);
215
216 if (handler_list_remove(wp->handler_list, sentinel->hp) == 0) {
217 if (!watchpoint_gc_list) {
218 watchpoint_gc_list = grecs_list_create();
219 watchpoint_gc_list->free_entry = wpref_destroy;
220 }
221 grecs_list_append(watchpoint_gc_list, wp);
222 }
223 return 0;
224 }
225
226 static void
sentinel_handler_free(void * ptr)227 sentinel_handler_free(void *ptr)
228 {
229 struct sentinel *sentinel = ptr;
230 watchpoint_unref(sentinel->watchpoint);
231 free(sentinel);
232 }
233
234 int
watchpoint_install_sentinel(struct watchpoint * wpt)235 watchpoint_install_sentinel(struct watchpoint *wpt)
236 {
237 struct watchpoint *sent;
238 char *dirname;
239 char *filename;
240 struct handler *hp;
241 event_mask ev_mask;
242 struct sentinel *sentinel;
243
244 filename = split_pathname(wpt, &dirname);
245 sent = watchpoint_install(dirname, NULL);
246
247 getevt("create", &ev_mask);
248 hp = handler_alloc(ev_mask);
249 hp->run = sentinel_handler_run;
250 hp->free = sentinel_handler_free;
251
252 sentinel = emalloc(sizeof(*sentinel));
253 sentinel->watchpoint = wpt;
254 sentinel->hp = hp;
255 watchpoint_ref(wpt);
256
257 hp->data = sentinel;
258
259 filpatlist_add_exact(&hp->fnames, filename);
260 handler_list_append(sent->handler_list, hp);
261 unsplit_pathname(wpt);
262 diag(LOG_NOTICE, _("installing CREATE sentinel for %s"), wpt->dirname);
263 return watchpoint_init(sent);
264 }
265
266 int
watchpoint_init(struct watchpoint * wpt)267 watchpoint_init(struct watchpoint *wpt)
268 {
269 struct stat st;
270 event_mask mask = { 0, 0 };
271 struct handler *hp;
272 handler_iterator_t itr;
273 int wd;
274
275 debug(1, (_("creating watcher %s"), wpt->dirname));
276
277 if (stat(wpt->dirname, &st)) {
278 if (errno == ENOENT) {
279 return watchpoint_install_sentinel(wpt);
280 } else {
281 diag(LOG_ERR, _("cannot set watcher on %s: %s"),
282 wpt->dirname, strerror(errno));
283 return 1;
284 }
285 }
286
287 wpt->isdir = S_ISDIR(st.st_mode);
288
289 for_each_handler(wpt, itr, hp) {
290 mask.sys_mask |= hp->ev_mask.sys_mask;
291 mask.gen_mask |= hp->ev_mask.gen_mask;
292 }
293
294 wd = sysev_add_watch(wpt, mask);
295 if (wd == -1) {
296 diag(LOG_ERR, _("cannot set watcher on %s: %s"),
297 wpt->dirname, strerror(errno));
298 return 1;
299 }
300
301 wpt->wd = wd;
302
303 return 0;
304 }
305
306 static int watch_subdirs(struct watchpoint *parent, int notify);
307
308 int
subwatcher_create(struct watchpoint * parent,const char * dirname,int notify)309 subwatcher_create(struct watchpoint *parent, const char *dirname,
310 int notify)
311 {
312 struct watchpoint *wpt;
313 int inst;
314
315 wpt = watchpoint_install(dirname, &inst);
316 if (!inst)
317 return -1;
318
319 wpt->handler_list = handler_list_copy(parent->handler_list);
320 wpt->parent = parent;
321
322 if (parent->depth == -1)
323 wpt->depth = parent->depth;
324 else if (parent->depth)
325 wpt->depth = parent->depth - 1;
326 else
327 wpt->depth = 0;
328
329 if (watchpoint_init(wpt)) {
330 //FIXME watchpoint_free(wpt);
331 return -1;
332 }
333
334 return 1 + watch_subdirs(wpt, notify);
335 }
336
337 /* Deliver GENEV_CREATE event */
338 void
deliver_ev_create(struct watchpoint * wp,const char * dirname,const char * name)339 deliver_ev_create(struct watchpoint *wp, const char *dirname, const char *name)
340 {
341 event_mask m = { GENEV_CREATE, 0 };
342 struct handler *hp;
343 handler_iterator_t itr;
344
345 for_each_handler(wp, itr, hp) {
346 if (handler_matches_event(hp, gen, GENEV_CREATE, name))
347 hp->run(wp, &m, dirname, name, hp->data);
348 }
349 }
350
351 /* Check if a new watcher must be created and create it if so.
352
353 A watcher must be created if its parent's recursion depth has a non-null
354 value. If it has a negative value, which means "recursively watch new
355 subdirectories without limit on their nesting level", it will be inherited
356 by the new watcher. Otherwise, the new watcher will inherit the parent's
357 depth decreased by one, thus eventually cutting off creation of new
358 watchers.
359
360 Return 0 on success, -1 on error.
361 */
362 int
check_new_watcher(const char * dir,const char * name)363 check_new_watcher(const char *dir, const char *name)
364 {
365 int rc;
366 char *fname;
367 struct stat st;
368 struct watchpoint *parent;
369
370 parent = watchpoint_lookup(dir);
371 if (!parent || !parent->depth)
372 return 0;
373
374 fname = mkfilename(dir, name);
375 if (!fname) {
376 diag(LOG_ERR,
377 _("cannot create watcher %s/%s: not enough memory"),
378 dir, name);
379 return -1;
380 }
381
382 if (stat(fname, &st)) {
383 diag(LOG_ERR,
384 _("cannot create watcher %s/%s, stat failed: %s"),
385 dir, name, strerror(errno));
386 rc = -1;
387 } else if (S_ISDIR(st.st_mode)) {
388 deliver_ev_create(parent, parent->dirname, name);
389 rc = subwatcher_create(parent, fname, 1);
390 } else
391 rc = 0;
392 free(fname);
393 return rc;
394 }
395
396 int
watchpoint_pattern_match(struct watchpoint * wpt,const char * file_name)397 watchpoint_pattern_match(struct watchpoint *wpt, const char *file_name)
398 {
399 struct handler *hp;
400 handler_iterator_t itr;
401
402 for_each_handler(wpt, itr, hp) {
403 if (filpatlist_match(hp->fnames, file_name) == 0)
404 return 0;
405 }
406 return 1;
407 }
408
409 /* Recursively scan subdirectories of parent and add them to the
410 watcher list, as requested by the parent's recursion depth value. */
411 static int
watch_subdirs(struct watchpoint * parent,int notify)412 watch_subdirs(struct watchpoint *parent, int notify)
413 {
414 DIR *dir;
415 struct dirent *ent;
416 int filemask;
417 int total = 0;
418
419 if (!parent->isdir)
420 return 0;
421
422 filemask = sysev_filemask(parent);
423 if (parent->depth)
424 filemask |= S_IFDIR;
425 if (!filemask)
426 return 0;
427
428 dir = opendir(parent->dirname);
429 if (!dir) {
430 diag(LOG_ERR, _("cannot open directory %s: %s"),
431 parent->dirname, strerror(errno));
432 return 0;
433 }
434
435 while (ent = readdir(dir)) {
436 struct stat st;
437 char *dirname;
438
439 if (ent->d_name[0] == '.' &&
440 (ent->d_name[1] == 0 ||
441 (ent->d_name[1] == '.' && ent->d_name[2] == 0)))
442 continue;
443
444 dirname = mkfilename(parent->dirname, ent->d_name);
445 if (!dirname) {
446 diag(LOG_ERR,
447 _("cannot stat %s/%s: not enough memory"),
448 parent->dirname, ent->d_name);
449 continue;
450 }
451 if (stat(dirname, &st)) {
452 diag(LOG_ERR, _("cannot stat %s: %s"),
453 dirname, strerror(errno));
454 } else if (watchpoint_pattern_match(parent, ent->d_name)
455 == 0) {
456 if (notify)
457 deliver_ev_create(parent, parent->dirname,
458 ent->d_name);
459 if (st.st_mode & filemask) {
460 int rc = subwatcher_create(parent, dirname,
461 notify);
462 if (rc > 0)
463 total += rc;
464 }
465 }
466 free(dirname);
467 }
468 closedir(dir);
469 return total;
470 }
471
472
473 static int
setwatcher(void * ent,void * data)474 setwatcher(void *ent, void *data)
475 {
476 struct wpref *wpref = (struct wpref *) ent;
477 struct watchpoint *wpt = wpref->wpt;
478
479 if (wpt->wd == -1 && watchpoint_init(wpt) == 0)
480 watch_subdirs(wpt, 0);
481 return 0;
482 }
483
484 static int
checkwatcher(void * ent,void * data)485 checkwatcher(void *ent, void *data)
486 {
487 struct wpref *wpref = (struct wpref *) ent;
488 struct watchpoint *wpt = wpref->wpt;
489 return wpt->wd >= 0;
490 }
491
492 void
setup_watchers(void)493 setup_watchers(void)
494 {
495 sysev_init();
496 if (grecs_symtab_count(nametab) == 0) {
497 diag(LOG_CRIT, _("no event handlers configured"));
498 exit(1);
499 }
500 grecs_symtab_foreach(nametab, setwatcher, NULL);
501 if (!grecs_symtab_foreach(nametab, checkwatcher, NULL)) {
502 diag(LOG_CRIT, _("no event handlers installed"));
503 exit(2);
504 }
505 }
506
507 static int
stopwatcher(void * ent,void * data)508 stopwatcher(void *ent, void *data)
509 {
510 struct wpref *wpref = (struct wpref *) ent;
511 struct watchpoint *wpt = wpref->wpt;
512 if (wpt->wd != -1) {
513 debug(1, (_("removing watcher %s"), wpt->dirname));
514 sysev_rm_watch(wpt);
515 }
516 return 0;
517 }
518
519 void
shutdown_watchers(void)520 shutdown_watchers(void)
521 {
522 grecs_symtab_foreach(nametab, stopwatcher, NULL);
523 grecs_symtab_clear(nametab);
524 }
525
526
527 char *
split_pathname(struct watchpoint * dp,char ** dirname)528 split_pathname(struct watchpoint *dp, char **dirname)
529 {
530 char *p = strrchr(dp->dirname, '/');
531 if (p) {
532 dp->split_p = p;
533 *p++ = 0;
534 *dirname = dp->dirname;
535 } else {
536 p = dp->dirname;
537 *dirname = ".";
538 }
539 return p;
540 }
541
542 void
unsplit_pathname(struct watchpoint * dp)543 unsplit_pathname(struct watchpoint *dp)
544 {
545 if (dp->split_p) {
546 *dp->split_p = '/';
547 dp->split_p = NULL;
548 }
549 }
550