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