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 <grecs.h>
19 #include <pwd.h>
20 #include <grp.h>
21
22 static struct transtab kwpri[] = {
23 { "emerg", LOG_EMERG },
24 { "alert", LOG_ALERT },
25 { "crit", LOG_CRIT },
26 { "err", LOG_ERR },
27 { "warning", LOG_WARNING },
28 { "notice", LOG_NOTICE },
29 { "info", LOG_INFO },
30 { "debug", LOG_DEBUG },
31 { NULL }
32 };
33
34 static struct transtab kwfac[] = {
35 { "user", LOG_USER },
36 { "daemon", LOG_DAEMON },
37 { "auth", LOG_AUTH },
38 { "authpriv",LOG_AUTHPRIV },
39 { "mail", LOG_MAIL },
40 { "cron", LOG_CRON },
41 { "local0", LOG_LOCAL0 },
42 { "local1", LOG_LOCAL1 },
43 { "local2", LOG_LOCAL2 },
44 { "local3", LOG_LOCAL3 },
45 { "local4", LOG_LOCAL4 },
46 { "local5", LOG_LOCAL5 },
47 { "local6", LOG_LOCAL6 },
48 { "local7", LOG_LOCAL7 },
49 { NULL }
50 };
51
52 int
get_facility(const char * arg)53 get_facility(const char *arg)
54 {
55 int f;
56 char *p;
57
58 errno = 0;
59 f = strtoul (arg, &p, 0);
60 if (*p == 0 && errno == 0)
61 return f;
62 if (trans_strtotok(kwfac, arg, &f)) {
63 diag(LOG_CRIT, _("unknown syslog facility: %s"), arg);
64 exit(1);
65 }
66 return f;
67 }
68
69 int
get_priority(const char * arg)70 get_priority(const char *arg)
71 {
72 int f;
73 char *p;
74
75 errno = 0;
76 f = strtoul (arg, &p, 0);
77 if (*p == 0 && errno == 0)
78 return f;
79 if (trans_strtotok(kwpri, arg, &f)) {
80 diag(LOG_CRIT, _("unknown syslog priority: %s"), arg);
81 exit(1);
82 }
83 return f;
84 }
85
86 #define ASSERT_SCALAR(cmd, locus) \
87 if ((cmd) != grecs_callback_set_value) { \
88 grecs_error(locus, 0, _("unexpected block statement")); \
89 return 1; \
90 }
91
92 int
assert_grecs_value_type(grecs_locus_t * locus,const grecs_value_t * value,int type)93 assert_grecs_value_type(grecs_locus_t *locus,
94 const grecs_value_t *value, int type)
95 {
96 if (GRECS_VALUE_EMPTY_P(value)) {
97 grecs_error(locus, 0, _("expected %s"),
98 grecs_data_type_string(type));
99 return 1;
100 }
101 if (value->type != type) {
102 grecs_error(locus, 0, _("expected %s, but found %s"),
103 grecs_data_type_string(type),
104 grecs_data_type_string(value->type));
105 return 1;
106 }
107 return 0;
108 }
109
110 static int
cb_syslog_facility(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)111 cb_syslog_facility(enum grecs_callback_command cmd, grecs_node_t *node,
112 void *varptr, void *cb_data)
113 {
114 grecs_locus_t *locus = &node->locus;
115 grecs_value_t *value = node->v.value;
116 int fac;
117
118 ASSERT_SCALAR(cmd, locus);
119 if (assert_grecs_value_type(&value->locus, value, GRECS_TYPE_STRING))
120 return 1;
121
122 if (trans_strtotok(kwfac, value->v.string, &fac))
123 grecs_error(&value->locus, 0,
124 _("unknown syslog facility `%s'"),
125 value->v.string);
126 else
127 *(int*)varptr = fac;
128 return 0;
129 }
130
131 static struct grecs_keyword syslog_kw[] = {
132 { "facility",
133 N_("name"),
134 N_("Set syslog facility. Arg is one of the following: user, daemon, "
135 "auth, authpriv, mail, cron, local0 through local7 "
136 "(case-insensitive), or a facility number."),
137 grecs_type_string, GRECS_DFLT,
138 &facility, 0, cb_syslog_facility },
139 { "tag", N_("string"), N_("Tag syslog messages with this string"),
140 grecs_type_string, GRECS_DFLT,
141 &tag },
142 { "print-priority", N_("arg"),
143 N_("Prefix each message with its priority"),
144 grecs_type_bool, GRECS_DFLT,
145 &syslog_include_prio },
146 { NULL },
147 };
148
149 struct eventconf {
150 struct grecs_list *pathlist;
151 event_mask ev_mask;
152 filpatlist_t fpat;
153 struct prog_handler prog_handler;
154 };
155
156 static struct eventconf eventconf;
157
158 static void
eventconf_init(void)159 eventconf_init(void)
160 {
161 memset(&eventconf, 0, sizeof eventconf);
162 eventconf.prog_handler.timeout = DEFAULT_TIMEOUT;
163 }
164
165 static void
eventconf_free(void)166 eventconf_free(void)
167 {
168 grecs_list_free(eventconf.pathlist);
169 prog_handler_free(&eventconf.prog_handler);
170 filpatlist_destroy(&eventconf.fpat);
171 }
172
173 void
eventconf_flush(grecs_locus_t * loc)174 eventconf_flush(grecs_locus_t *loc)
175 {
176 struct grecs_list_entry *ep;
177 struct handler *hp = prog_handler_alloc(eventconf.ev_mask,
178 eventconf.fpat,
179 &eventconf.prog_handler);
180
181 for (ep = eventconf.pathlist->head; ep; ep = ep->next) {
182 struct pathent *pe = ep->data;
183 struct watchpoint *wpt;
184 int isnew;
185
186 wpt = watchpoint_install(pe->path, &isnew);
187 if (!wpt)
188 abort();
189 if (!isnew && wpt->depth != pe->depth)
190 grecs_error(loc, 0,
191 _("%s: recursion depth does not match previous definition"),
192 pe->path);
193 wpt->depth = pe->depth;
194 handler_list_append(wpt->handler_list, hp);
195 }
196 grecs_list_free(eventconf.pathlist);
197 eventconf_init();
198 }
199
200 static int
cb_watcher(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)201 cb_watcher(enum grecs_callback_command cmd, grecs_node_t *node,
202 void *varptr, void *cb_data)
203 {
204 int err = 0;
205
206 switch (cmd) {
207 case grecs_callback_section_begin:
208 eventconf_init();
209 break;
210 case grecs_callback_section_end:
211 if (!eventconf.pathlist) {
212 grecs_error(&node->locus, 0, _("no paths configured"));
213 ++err;
214 }
215 if (!eventconf.prog_handler.command) {
216 grecs_error(&node->locus, 0,
217 _("no command configured"));
218 ++err;
219 }
220 if (evtnullp(&eventconf.ev_mask))
221 evtsetall(&eventconf.ev_mask);
222 if (err == 0)
223 eventconf_flush(&node->locus);
224 else
225 eventconf_free();
226 break;
227 case grecs_callback_set_value:
228 grecs_error(&node->locus, 0,
229 _("invalid use of block statement"));
230 }
231 return 0;
232 }
233
234 static struct pathent *
pathent_alloc(char * s,long depth)235 pathent_alloc(char *s, long depth)
236 {
237 size_t len = strlen(s);
238 struct pathent *p = emalloc(sizeof(*p) + len);
239 p->len = len;
240 strcpy(p->path, s);
241 p->depth = depth;
242 return p;
243 }
244
245 static int
cb_path(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)246 cb_path(enum grecs_callback_command cmd, grecs_node_t *node,
247 void *varptr, void *cb_data)
248 {
249 grecs_locus_t *locus = &node->locus;
250 grecs_value_t *val = node->v.value;
251 struct grecs_list **lpp = varptr, *lp;
252 struct pathent *pe;
253 char *s;
254 long depth = 0;
255
256 ASSERT_SCALAR(cmd, locus);
257
258 switch (val->type) {
259 case GRECS_TYPE_STRING:
260 s = val->v.string;
261 break;
262
263 case GRECS_TYPE_ARRAY:
264 if (assert_grecs_value_type(&val->v.arg.v[0]->locus,
265 val->v.arg.v[0],
266 GRECS_TYPE_STRING))
267 return 1;
268 if (assert_grecs_value_type(&val->v.arg.v[1]->locus,
269 val->v.arg.v[1],
270 GRECS_TYPE_STRING))
271 return 1;
272 if (strcmp(val->v.arg.v[1]->v.string, "recursive")) {
273 grecs_error(&val->v.arg.v[1]->locus, 0,
274 _("expected \"recursive\" or end of statement"));
275 return 1;
276 }
277 switch (val->v.arg.c) {
278 case 2:
279 depth = -1;
280 break;
281 case 3:
282 if (grecs_string_convert(&depth, grecs_type_long,
283 val->v.arg.v[2]->v.string,
284 &val->v.arg.v[2]->locus))
285 return 1;
286 break;
287 default:
288 grecs_error(&val->v.arg.v[3]->locus, 0,
289 _("surplus argument"));
290 return 1;
291 }
292 s = val->v.arg.v[0]->v.string;
293 break;
294 case GRECS_TYPE_LIST:
295 grecs_error(locus, 0, _("unexpected list"));
296 return 1;
297 }
298 pe = pathent_alloc(s, depth);
299 if (*lpp)
300 lp = *lpp;
301 else {
302 lp = _grecs_simple_list_create(1);
303 *lpp = lp;
304 }
305 grecs_list_append(lp, pe);
306 return 0;
307 }
308
309 static int
cb_eventlist(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)310 cb_eventlist(enum grecs_callback_command cmd, grecs_node_t *node,
311 void *varptr, void *cb_data)
312 {
313 grecs_locus_t *locus = &node->locus;
314 grecs_value_t *val = node->v.value;
315 event_mask *mask = varptr;
316 event_mask m;
317 struct grecs_list_entry *ep;
318 int i;
319
320 ASSERT_SCALAR(cmd, locus);
321
322 switch (val->type) {
323 case GRECS_TYPE_STRING:
324 if (getevt(val->v.string, &m)) {
325 grecs_error(&val->locus, 0,
326 _("unrecognized event code"));
327 return 1;
328 }
329 mask->gen_mask |= m.gen_mask;
330 mask->sys_mask |= m.sys_mask;
331 break;
332
333 case GRECS_TYPE_ARRAY:
334 for (i = 0; i < val->v.arg.c; i++) {
335 if (assert_grecs_value_type(&val->v.arg.v[i]->locus,
336 val->v.arg.v[i],
337 GRECS_TYPE_STRING))
338 return 1;
339 if (getevt(val->v.arg.v[i]->v.string, &m)) {
340 grecs_error(&val->v.arg.v[i]->locus, 0,
341 _("unrecognized event code"));
342 return 1;
343 }
344 mask->gen_mask |= m.gen_mask;
345 mask->sys_mask |= m.sys_mask;
346 }
347 break;
348 case GRECS_TYPE_LIST:
349 for (ep = val->v.list->head; ep; ep = ep->next) {
350 grecs_value_t *vp = ep->data;
351 if (assert_grecs_value_type(&vp->locus, vp,
352 GRECS_TYPE_STRING))
353 return 1;
354 if (getevt(vp->v.string, &m)) {
355 grecs_error(&vp->locus, 0,
356 _("unrecognized event code"));
357 return 1;
358 }
359 mask->gen_mask |= m.gen_mask;
360 mask->sys_mask |= m.sys_mask;
361 }
362 break;
363 }
364 return 0;
365 }
366
367 static int
membergid(gid_t gid,size_t gc,gid_t * gv)368 membergid(gid_t gid, size_t gc, gid_t *gv)
369 {
370 int i;
371 for (i = 0; i < gc; i++)
372 if (gv[i] == gid)
373 return 1;
374 return 0;
375 }
376
377 static void
get_user_groups(char * user,gid_t gid,size_t * pgidc,gid_t ** pgidv)378 get_user_groups(char *user, gid_t gid, size_t *pgidc, gid_t **pgidv)
379 {
380 size_t gidc = 0, n = 0;
381 gid_t *gidv = NULL;
382 struct group *gr;
383
384 n = 32;
385 gidv = emalloc(n * sizeof(gidv[0]));
386 gidv[0] = gid;
387 gidc = 1;
388
389 setgrent();
390 while (gr = getgrent()) {
391 char **p;
392 for (p = gr->gr_mem; *p; p++)
393 if (strcmp(*p, user) == 0) {
394 if (n == gidc) {
395 n += 32;
396 gidv = erealloc(gidv,
397 n * sizeof(gidv[0]));
398 }
399 if (!membergid(gr->gr_gid, gidc, gidv))
400 gidv[gidc++] = gr->gr_gid;
401 }
402 }
403 endgrent();
404 *pgidc = gidc;
405 *pgidv = gidv;
406 }
407
408 static int
cb_user(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)409 cb_user(enum grecs_callback_command cmd, grecs_node_t *node,
410 void *varptr, void *cb_data)
411 {
412 grecs_locus_t *locus = &node->locus;
413 grecs_value_t *val = node->v.value;
414 struct passwd *pw;
415 struct group *gr;
416 grecs_value_t *uv, *gv = NULL;
417 gid_t gid;
418
419 ASSERT_SCALAR(cmd, locus);
420 switch (val->type) {
421 case GRECS_TYPE_STRING:
422 uv = val;
423 break;
424
425 case GRECS_TYPE_ARRAY:
426 if (assert_grecs_value_type(&val->v.arg.v[0]->locus,
427 val->v.arg.v[0],
428 GRECS_TYPE_STRING))
429 return 1;
430 if (assert_grecs_value_type(&val->v.arg.v[1]->locus,
431 val->v.arg.v[1],
432 GRECS_TYPE_STRING))
433 return 1;
434 if (val->v.arg.c > 2) {
435 grecs_locus_t loc;
436 loc.beg = val->v.arg.v[2]->locus.beg;
437 loc.end = val->v.arg.v[val->v.arg.c - 1]->locus.end;
438 grecs_error(&loc, 0, _("surplus arguments"));
439 return 1;
440 }
441 uv = val->v.arg.v[0];
442 gv = val->v.arg.v[1];
443 break;
444
445 case GRECS_TYPE_LIST:
446 grecs_error(locus, 0, _("unexpected list"));
447 return 1;
448 }
449
450 pw = getpwnam(uv->v.string);
451 if (!pw) {
452 grecs_error(&uv->locus, 0, _("no such user"));
453 return 1;
454 }
455
456 if (gv) {
457 gr = getgrnam(gv->v.string);
458 if (!gr) {
459 grecs_error(&gv->locus, 0, _("no such group"));
460 return 1;
461 }
462 gid = gr->gr_gid;
463 } else
464 gid = pw->pw_gid;
465
466 eventconf.prog_handler.uid = pw->pw_uid;
467 get_user_groups(uv->v.string, gid,
468 &eventconf.prog_handler.gidc, &eventconf.prog_handler.gidv);
469
470 return 0;
471 }
472
473 static int
cb_option(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)474 cb_option(enum grecs_callback_command cmd, grecs_node_t *node,
475 void *varptr, void *cb_data)
476 {
477 grecs_locus_t *locus = &node->locus;
478 grecs_value_t *val = node->v.value;
479 struct grecs_list_entry *ep;
480
481 ASSERT_SCALAR(cmd, locus);
482 if (assert_grecs_value_type(&val->locus, val, GRECS_TYPE_LIST))
483 return 1;
484
485 for (ep = val->v.list->head; ep; ep = ep->next) {
486 grecs_value_t *vp = ep->data;
487 if (assert_grecs_value_type(&vp->locus, vp,
488 GRECS_TYPE_STRING))
489 return 1;
490 if (strcmp(vp->v.string, "nowait") == 0)
491 eventconf.prog_handler.flags |= HF_NOWAIT;
492 else if (strcmp(vp->v.string, "wait") == 0)
493 eventconf.prog_handler.flags &= ~HF_NOWAIT;
494 else if (strcmp(vp->v.string, "stdout") == 0)
495 eventconf.prog_handler.flags |= HF_STDOUT;
496 else if (strcmp(vp->v.string, "stderr") == 0)
497 eventconf.prog_handler.flags |= HF_STDERR;
498 else if (strcmp(vp->v.string, "shell") == 0)
499 eventconf.prog_handler.flags |= HF_SHELL;
500 else
501 grecs_error(&vp->locus, 0, _("unrecognized option"));
502 }
503 return 0;
504 }
505
506 static int
cb_environ(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)507 cb_environ(enum grecs_callback_command cmd, grecs_node_t *node,
508 void *varptr, void *cb_data)
509 {
510 grecs_locus_t *locus = &node->locus;
511 grecs_value_t *val = node->v.value;
512 struct grecs_list_entry *ep;
513 int i, j;
514
515 ASSERT_SCALAR(cmd, locus);
516 switch (val->type) {
517 case GRECS_TYPE_STRING:
518 if (assert_grecs_value_type(&val->locus, val,
519 GRECS_TYPE_STRING))
520 return 1;
521 i = prog_handler_envrealloc(&eventconf.prog_handler, 1);
522 eventconf.prog_handler.env[i] = estrdup(val->v.string);
523 eventconf.prog_handler.env[i+1] = NULL;
524 break;
525
526 case GRECS_TYPE_ARRAY:
527 j = prog_handler_envrealloc(&eventconf.prog_handler, val->v.arg.c);
528 for (i = 0; i < val->v.arg.c; i++, j++) {
529 if (assert_grecs_value_type(&val->v.arg.v[i]->locus,
530 val->v.arg.v[i],
531 GRECS_TYPE_STRING))
532 return 1;
533 eventconf.prog_handler.env[j] = estrdup(val->v.arg.v[i]->v.string);
534 }
535 eventconf.prog_handler.env[j] = NULL;
536 break;
537
538 case GRECS_TYPE_LIST:
539 j = prog_handler_envrealloc(&eventconf.prog_handler,
540 val->v.list->count);
541 for (ep = val->v.list->head; ep; ep = ep->next, j++) {
542 grecs_value_t *vp = ep->data;
543 if (assert_grecs_value_type(&vp->locus, vp,
544 GRECS_TYPE_STRING))
545 return 1;
546 eventconf.prog_handler.env[j] = estrdup(vp->v.string);
547 }
548 eventconf.prog_handler.env[j] = NULL;
549 }
550 return 0;
551 }
552
553 static int
file_name_pattern(filpatlist_t * fptr,grecs_value_t * val)554 file_name_pattern(filpatlist_t *fptr, grecs_value_t *val)
555 {
556 if (assert_grecs_value_type(&val->locus, val, GRECS_TYPE_STRING))
557 return 1;
558 return filpatlist_add(fptr, val->v.string, &val->locus);
559 }
560
561 static int
cb_file_pattern(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)562 cb_file_pattern(enum grecs_callback_command cmd, grecs_node_t *node,
563 void *varptr, void *cb_data)
564 {
565 grecs_value_t *val = node->v.value;
566 filpatlist_t *fpat = varptr;
567 struct grecs_list_entry *ep;
568 int i;
569
570 ASSERT_SCALAR(cmd, &node->locus);
571
572 switch (val->type) {
573 case GRECS_TYPE_STRING:
574 file_name_pattern(fpat, val);
575 break;
576
577 case GRECS_TYPE_ARRAY:
578 for (i = 0; i < val->v.arg.c; i++)
579 if (file_name_pattern(fpat, val->v.arg.v[i]))
580 break;
581 break;
582
583 case GRECS_TYPE_LIST:
584 for (ep = val->v.list->head; ep; ep = ep->next)
585 if (file_name_pattern(fpat,
586 (grecs_value_t *) ep->data))
587 break;
588 break;
589 }
590
591 return 0;
592 }
593
594 static struct grecs_keyword watcher_kw[] = {
595 { "path", NULL, N_("Pathname to watch"),
596 grecs_type_string, GRECS_DFLT, &eventconf.pathlist, 0,
597 cb_path },
598 { "event", NULL, N_("Events to watch for"),
599 grecs_type_string, GRECS_LIST, &eventconf.ev_mask, 0,
600 cb_eventlist },
601 { "file", N_("regexp"), N_("Files to watch for"),
602 grecs_type_string, GRECS_LIST, &eventconf.fpat, 0,
603 cb_file_pattern },
604 { "command", NULL, N_("Command to execute on event"),
605 grecs_type_string, GRECS_DFLT, &eventconf.prog_handler.command },
606 { "user", N_("name"), N_("Run command as this user"),
607 grecs_type_string, GRECS_DFLT, NULL, 0,
608 cb_user },
609 { "timeout", N_("seconds"), N_("Timeout for the command"),
610 grecs_type_uint, GRECS_DFLT, &eventconf.prog_handler.timeout },
611 { "option", NULL, N_("List of additional options"),
612 grecs_type_string, GRECS_LIST, NULL, 0,
613 cb_option },
614 { "environ", N_("<arg: string> <arg: string>..."),
615 N_("Modify environment"),
616 grecs_type_string, GRECS_DFLT, NULL, 0,
617 cb_environ },
618 { NULL }
619 };
620
621 static struct grecs_keyword direvent_kw[] = {
622 { "user", NULL, N_("Run as this user"),
623 grecs_type_string, GRECS_DFLT, &user },
624 { "foreground", NULL, N_("Run in foreground"),
625 grecs_type_bool, GRECS_DFLT, &foreground },
626 { "pidfile", N_("file"), N_("Set pid file name"),
627 grecs_type_string, GRECS_DFLT, &pidfile },
628 { "syslog", NULL, N_("Configure syslog logging"),
629 grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, syslog_kw },
630 { "debug", N_("level"), N_("Set debug level"),
631 grecs_type_int, GRECS_DFLT, &debug_level },
632 { "watcher", NULL, N_("Configure event watcher"),
633 grecs_type_section, GRECS_DFLT, NULL, 0,
634 cb_watcher, NULL, watcher_kw },
635 { NULL }
636 };
637
638
639 void
config_help()640 config_help()
641 {
642 static char docstring[] =
643 N_("Configuration file structure for direvent.\n"
644 "For more information, use `info direvent configuration'.");
645 grecs_print_docstring(docstring, 0, stdout);
646 grecs_print_statement_array(direvent_kw, 1, 0, stdout);
647 }
648
649 void
config_init(void)650 config_init(void)
651 {
652 grecs_include_path_setup(INCLUDE_PATH_ARGS, NULL);
653 }
654
655 void
config_parse(char const * conffile)656 config_parse(char const *conffile)
657 {
658 struct grecs_node *tree;
659
660 grecs_parser_options = GRECS_OPTION_QUOTED_STRING_CONCAT;
661 tree = grecs_parse(conffile);
662 if (!tree)
663 exit(1);
664 if (grecs_tree_process(tree, direvent_kw))
665 exit(1);
666
667 }
668