1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * confprocess.c: Generic configuration processing.
4 *
5 * Copyright (c) 2005-2008 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25 #include <limits.h>
26
27 enum conftype
28 {
29 CONF_HANDLER,
30 CONF_UINT,
31 CONF_DURATION,
32 CONF_DUPSTR,
33 CONF_BOOL,
34 CONF_SUBBLOCK
35 };
36
37 struct ConfTable
38 {
39 char *name;
40 enum conftype type;
41 unsigned int flags;
42 union
43 {
44 int (*handler) (mowgli_config_file_entry_t *);
45
46 struct
47 {
48 unsigned int *var;
49 unsigned int min;
50 unsigned int max;
51 unsigned int def;
52 } uint_val;
53 struct
54 {
55 unsigned int *var;
56 const char *defunit;
57 unsigned int def;
58 } duration_val;
59 struct
60 {
61 char **var;
62 char *def;
63 } dupstr_val;
64 struct
65 {
66 bool *var;
67 bool def;
68 } bool_val;
69
70 mowgli_list_t *subblock;
71 } un;
72 mowgli_node_t node;
73 };
74
75 mowgli_heap_t *conftable_heap;
76
77 mowgli_list_t confblocks;
78 bool conf_need_rehash;
79
80 /* *INDENT-ON* */
81
conf_report_warning(mowgli_config_file_entry_t * ce,const char * fmt,...)82 void conf_report_warning(mowgli_config_file_entry_t *ce, const char *fmt, ...)
83 {
84 va_list va;
85 char buf[BUFSIZE];
86 char name[80];
87
88 return_if_fail(ce != NULL);
89 return_if_fail(fmt != NULL);
90
91 va_start(va, fmt);
92 vsnprintf(buf, BUFSIZE, fmt, va);
93 va_end(va);
94
95 if (ce->prevlevel == NULL)
96 mowgli_strlcpy(name, ce->varname, sizeof name);
97 else if (ce->prevlevel->prevlevel == NULL)
98 snprintf(name, sizeof name, "%s::%s",
99 ce->prevlevel->varname, ce->varname);
100 else if (ce->prevlevel->prevlevel->prevlevel == NULL)
101 snprintf(name, sizeof name, "%s::%s::%s",
102 ce->prevlevel->prevlevel->varname,
103 ce->prevlevel->varname, ce->varname);
104 else
105 snprintf(name, sizeof name, "...::%s::%s::%s",
106 ce->prevlevel->prevlevel->varname,
107 ce->prevlevel->varname, ce->varname);
108 slog(LG_ERROR, "%s:%d: [%s] warning: %s", ce->fileptr->filename, ce->varlinenum, name, buf);
109 }
110
process_uint_configentry(mowgli_config_file_entry_t * ce,unsigned int * var,unsigned int min,unsigned int max)111 bool process_uint_configentry(mowgli_config_file_entry_t *ce, unsigned int *var,
112 unsigned int min, unsigned int max)
113 {
114 unsigned long v;
115 char *end;
116
117 if (ce->vardata == NULL)
118 {
119 conf_report_warning(ce, "no parameter for configuration option");
120 return false;
121 }
122 errno = 0;
123 v = strtoul(ce->vardata, &end, 10);
124 if (errno != 0 || *end != '\0' || end == ce->vardata)
125 {
126 conf_report_warning(ce, "invalid number \"%s\"",
127 ce->vardata);
128 return false;
129 }
130 if (v > max || v < min)
131 {
132 conf_report_warning(ce, "value %lu is out of range [%u,%u]",
133 v,
134 min,
135 max);
136 return false;
137 }
138 *var = v;
139 return true;
140 }
141
142 static struct
143 {
144 const char *name;
145 unsigned int value;
146 } duration_units[] =
147 {
148 { "s", 1 }, /* must be first */
149 { "m", 60 },
150 { "h", 60 * 60 },
151 { "d", 24 * 60 * 60 },
152 { "w", 7 * 24 * 60 * 60 },
153 { NULL, 0 }
154 };
155
process_duration_configentry(mowgli_config_file_entry_t * ce,unsigned int * var,const char * defunit)156 bool process_duration_configentry(mowgli_config_file_entry_t *ce, unsigned int *var,
157 const char *defunit)
158 {
159 unsigned long v;
160 unsigned int i;
161 unsigned int max;
162 const char *unit;
163 char *end;
164
165 return_val_if_fail(ce != NULL, false);
166 return_val_if_fail(var != NULL, false);
167 return_val_if_fail(defunit != NULL, false);
168
169 if (ce->vardata == NULL)
170 {
171 conf_report_warning(ce, "no parameter for configuration option");
172 return false;
173 }
174 errno = 0;
175 v = strtoul(ce->vardata, &end, 10);
176 if (errno != 0 || end == ce->vardata)
177 {
178 conf_report_warning(ce, "invalid number \"%s\"",
179 ce->vardata);
180 return false;
181 }
182 while (*end == ' ' || *end == '\t')
183 end++;
184 unit = *end != '\0' ? end : defunit;
185 if (unit == NULL || *unit == '\0')
186 i = 0;
187 else
188 {
189 for (i = 0; duration_units[i].name != NULL; i++)
190 if (!strcasecmp(duration_units[i].name, unit))
191 break;
192 if (duration_units[i].name == NULL)
193 {
194 conf_report_warning(ce, "invalid unit \"%s\"", unit);
195 return false;
196 }
197 }
198 max = UINT_MAX / duration_units[i].value;
199 if (v > max)
200 {
201 conf_report_warning(ce, "value %lu%s is out of range [%u%s,%u%s]",
202 v,
203 duration_units[i].name,
204 0,
205 duration_units[i].name,
206 max,
207 duration_units[i].name);
208 return false;
209 }
210 *var = v * duration_units[i].value;
211 return true;
212 }
213
set_default(struct ConfTable * ct)214 static void set_default(struct ConfTable *ct)
215 {
216 return_if_fail(ct != NULL);
217
218 if (ct->flags & CONF_NO_REHASH && runflags & RF_REHASHING)
219 return;
220
221 switch (ct->type)
222 {
223 case CONF_UINT:
224 *ct->un.uint_val.var = ct->un.uint_val.def;
225 break;
226 case CONF_DURATION:
227 *ct->un.duration_val.var = ct->un.duration_val.def;
228 break;
229 case CONF_DUPSTR:
230 if (*ct->un.dupstr_val.var)
231 free(*ct->un.dupstr_val.var);
232 *ct->un.dupstr_val.var = ct->un.dupstr_val.def ? sstrdup(ct->un.dupstr_val.def) : NULL;
233 break;
234 case CONF_BOOL:
235 *ct->un.bool_val.var = ct->un.bool_val.def;
236 break;
237 case CONF_HANDLER:
238 case CONF_SUBBLOCK:
239 break;
240 }
241 }
242
process_configentry(struct ConfTable * ct,mowgli_config_file_entry_t * ce)243 static void process_configentry(struct ConfTable *ct, mowgli_config_file_entry_t *ce)
244 {
245 return_if_fail(ct != NULL);
246 return_if_fail(ce != NULL);
247
248 if (ct->flags & CONF_NO_REHASH && runflags & RF_REHASHING)
249 return;
250
251 switch (ct->type)
252 {
253 case CONF_HANDLER:
254 ct->un.handler(ce);
255 break;
256 case CONF_UINT:
257 if (!process_uint_configentry(ce, ct->un.uint_val.var,
258 ct->un.uint_val.min,
259 ct->un.uint_val.max))
260 return;
261 break;
262 case CONF_DURATION:
263 if (!process_duration_configentry(ce,
264 ct->un.duration_val.var,
265 ct->un.duration_val.defunit))
266 return;
267 break;
268 case CONF_DUPSTR:
269 if (ce->vardata == NULL)
270 conf_report_warning(ce, "no parameter for configuration option");
271 else
272 {
273 if (*ct->un.dupstr_val.var)
274 free(*ct->un.dupstr_val.var);
275 *ct->un.dupstr_val.var = sstrdup(ce->vardata);
276 }
277 break;
278 case CONF_BOOL:
279 if (ce->vardata == NULL ||
280 !strcasecmp(ce->vardata, "yes") ||
281 !strcasecmp(ce->vardata, "on") ||
282 !strcasecmp(ce->vardata, "true") ||
283 !strcmp(ce->vardata, "1"))
284 *ct->un.bool_val.var = true;
285 else if (!strcasecmp(ce->vardata, "no") ||
286 !strcasecmp(ce->vardata, "off") ||
287 !strcasecmp(ce->vardata, "false") ||
288 !strcmp(ce->vardata, "0"))
289 *ct->un.bool_val.var = false;
290 else
291 conf_report_warning(ce, "invalid boolean \"%s\"", ce->vardata);
292 break;
293 case CONF_SUBBLOCK:
294 subblock_handler(ce, ct->un.subblock);
295 break;
296 }
297 }
298
conf_process(mowgli_config_file_t * cfp)299 void conf_process(mowgli_config_file_t *cfp)
300 {
301 mowgli_config_file_t *cfptr;
302 mowgli_config_file_entry_t *ce;
303 mowgli_node_t *tn;
304 struct ConfTable *ct = NULL;
305 struct ConfTable *loadmodule = NULL;
306
307 return_if_fail(cfp != NULL);
308
309 MOWGLI_ITER_FOREACH(tn, confblocks.head)
310 {
311 ct = tn->data;
312
313 set_default(ct);
314
315 if (!strcasecmp(ct->name, "LOADMODULE"))
316 loadmodule = ct;
317 }
318
319
320 /* LOADMODULEs may change confblocks, so we must
321 * load modules before doing anything else */
322 MOWGLI_ITER_FOREACH(cfptr, cfp)
323 {
324 MOWGLI_ITER_FOREACH(ce, cfptr->entries)
325 {
326 if (!strcasecmp(ce->varname, "LOADMODULE"))
327 {
328 process_configentry(loadmodule, ce);
329 }
330 }
331 }
332
333 MOWGLI_ITER_FOREACH(cfptr, cfp)
334 {
335 MOWGLI_ITER_FOREACH(ce, cfptr->entries)
336 {
337 MOWGLI_ITER_FOREACH(tn, confblocks.head)
338 {
339 ct = tn->data;
340
341 if ((!strcasecmp(ct->name, ce->varname)) && (strcasecmp(ce->varname, "LOADMODULE")))
342 {
343 process_configentry(ct, ce);
344 break;
345 }
346 }
347
348 if (ct == NULL)
349 conf_report_warning(ce, "invalid configuration option");
350 }
351 }
352
353 conf_need_rehash = false;
354 }
355
subblock_handler(mowgli_config_file_entry_t * ce,mowgli_list_t * entries)356 int subblock_handler(mowgli_config_file_entry_t *ce, mowgli_list_t *entries)
357 {
358 mowgli_node_t *tn;
359 struct ConfTable *ct = NULL;
360
361 return_val_if_fail(ce != NULL, 0);
362 return_val_if_fail(entries != NULL, 0);
363
364 MOWGLI_ITER_FOREACH(tn, entries->head)
365 {
366 ct = tn->data;
367
368 set_default(ct);
369 }
370
371 MOWGLI_ITER_FOREACH(ce, ce->entries)
372 {
373 MOWGLI_ITER_FOREACH(tn, entries->head)
374 {
375 ct = tn->data;
376
377 if (!strcasecmp(ct->name, ce->varname))
378 {
379 process_configentry(ct, ce);
380 break;
381 }
382 }
383
384 if (ct == NULL)
385 conf_report_warning(ce, "invalid configuration option");
386 }
387 return 0;
388 }
389
find_top_conf(const char * name)390 struct ConfTable *find_top_conf(const char *name)
391 {
392 mowgli_node_t *n;
393 struct ConfTable *ct;
394
395 return_val_if_fail(name != NULL, NULL);
396
397 MOWGLI_ITER_FOREACH(n, confblocks.head)
398 {
399 ct = n->data;
400
401 if (!strcasecmp(ct->name, name))
402 return ct;
403 }
404
405 return NULL;
406 }
407
find_conf_item(const char * name,mowgli_list_t * conflist)408 struct ConfTable *find_conf_item(const char *name, mowgli_list_t *conflist)
409 {
410 mowgli_node_t *n;
411 struct ConfTable *ct;
412
413 return_val_if_fail(name != NULL, NULL);
414 return_val_if_fail(conflist != NULL, NULL);
415
416 MOWGLI_ITER_FOREACH(n, conflist->head)
417 {
418 ct = n->data;
419
420 if (!strcasecmp(ct->name, name))
421 return ct;
422 }
423
424 return NULL;
425 }
426
add_top_conf(const char * name,int (* handler)(mowgli_config_file_entry_t * ce))427 void add_top_conf(const char *name, int (*handler) (mowgli_config_file_entry_t *ce))
428 {
429 struct ConfTable *ct;
430
431 return_if_fail(name != NULL);
432 return_if_fail(handler != NULL);
433
434 if (find_top_conf(name))
435 {
436 slog(LG_DEBUG, "add_top_conf(): duplicate config block '%s'.", name);
437 return;
438 }
439
440 ct = mowgli_heap_alloc(conftable_heap);
441
442 ct->name = sstrdup(name);
443 ct->type = CONF_HANDLER;
444 ct->flags = 0;
445 ct->un.handler = handler;
446
447 mowgli_node_add(ct, &ct->node, &confblocks);
448 conf_need_rehash = true;
449 }
450
add_subblock_top_conf(const char * name,mowgli_list_t * list)451 void add_subblock_top_conf(const char *name, mowgli_list_t *list)
452 {
453 struct ConfTable *ct;
454
455 return_if_fail(name != NULL);
456 return_if_fail(list != NULL);
457
458 if (find_top_conf(name))
459 {
460 slog(LG_DEBUG, "add_top_conf(): duplicate config block '%s'.", name);
461 return;
462 }
463
464 ct = mowgli_heap_alloc(conftable_heap);
465
466 ct->name = sstrdup(name);
467 ct->type = CONF_SUBBLOCK;
468 ct->flags = 0;
469 ct->un.subblock = list;
470
471 mowgli_node_add(ct, &ct->node, &confblocks);
472 conf_need_rehash = true;
473 }
474
add_conf_item(const char * name,mowgli_list_t * conflist,int (* handler)(mowgli_config_file_entry_t * ce))475 void add_conf_item(const char *name, mowgli_list_t *conflist, int (*handler) (mowgli_config_file_entry_t *ce))
476 {
477 struct ConfTable *ct;
478
479 return_if_fail(name != NULL);
480 return_if_fail(conflist != NULL);
481 return_if_fail(handler != NULL);
482
483 if (find_conf_item(name, conflist))
484 {
485 slog(LG_DEBUG, "add_conf_item(): duplicate item %s", name);
486 return;
487 }
488
489 ct = mowgli_heap_alloc(conftable_heap);
490
491 ct->name = sstrdup(name);
492 ct->type = CONF_HANDLER;
493 ct->flags = 0;
494 ct->un.handler = handler;
495
496 mowgli_node_add(ct, &ct->node, conflist);
497 conf_need_rehash = true;
498 }
499
add_uint_conf_item(const char * name,mowgli_list_t * conflist,unsigned int flags,unsigned int * var,unsigned int min,unsigned int max,unsigned int def)500 void add_uint_conf_item(const char *name, mowgli_list_t *conflist, unsigned int flags, unsigned int *var, unsigned int min, unsigned int max, unsigned int def)
501 {
502 struct ConfTable *ct;
503
504 if (find_conf_item(name, conflist))
505 {
506 slog(LG_DEBUG, "add_uint_conf_item(): duplicate item %s", name);
507 return;
508 }
509
510 ct = mowgli_heap_alloc(conftable_heap);
511
512 ct->name = sstrdup(name);
513 ct->type = CONF_UINT;
514 ct->flags = flags;
515 ct->un.uint_val.var = var;
516 ct->un.uint_val.min = min;
517 ct->un.uint_val.max = max;
518 ct->un.uint_val.def = def;
519
520 mowgli_node_add(ct, &ct->node, conflist);
521 conf_need_rehash = true;
522 }
523
add_duration_conf_item(const char * name,mowgli_list_t * conflist,unsigned int flags,unsigned int * var,const char * defunit,unsigned int def)524 void add_duration_conf_item(const char *name, mowgli_list_t *conflist, unsigned int flags, unsigned int *var, const char *defunit, unsigned int def)
525 {
526 struct ConfTable *ct;
527
528 if (find_conf_item(name, conflist))
529 {
530 slog(LG_DEBUG, "add_duration_conf_item(): duplicate item %s", name);
531 return;
532 }
533
534 ct = mowgli_heap_alloc(conftable_heap);
535
536 ct->name = sstrdup(name);
537 ct->type = CONF_DURATION;
538 ct->flags = flags;
539 ct->un.duration_val.var = var;
540 ct->un.duration_val.defunit = defunit;
541 ct->un.duration_val.def = def;
542
543 mowgli_node_add(ct, &ct->node, conflist);
544 conf_need_rehash = true;
545 }
546
add_dupstr_conf_item(const char * name,mowgli_list_t * conflist,unsigned int flags,char ** var,const char * def)547 void add_dupstr_conf_item(const char *name, mowgli_list_t *conflist, unsigned int flags, char **var, const char *def)
548 {
549 struct ConfTable *ct;
550
551 if (find_conf_item(name, conflist))
552 {
553 slog(LG_DEBUG, "add_dupstr_conf_item(): duplicate item %s", name);
554 return;
555 }
556
557 ct = mowgli_heap_alloc(conftable_heap);
558
559 ct->name = sstrdup(name);
560 ct->type = CONF_DUPSTR;
561 ct->flags = flags;
562 ct->un.dupstr_val.var = var;
563 ct->un.dupstr_val.def = sstrdup(def);
564
565 mowgli_node_add(ct, &ct->node, conflist);
566 conf_need_rehash = true;
567 }
568
add_bool_conf_item(const char * name,mowgli_list_t * conflist,unsigned int flags,bool * var,bool def)569 void add_bool_conf_item(const char *name, mowgli_list_t *conflist, unsigned int flags, bool *var, bool def)
570 {
571 struct ConfTable *ct;
572
573 if (find_conf_item(name, conflist))
574 {
575 slog(LG_DEBUG, "add_bool_conf_item(): duplicate item %s", name);
576 return;
577 }
578
579 ct = mowgli_heap_alloc(conftable_heap);
580
581 ct->name = sstrdup(name);
582 ct->type = CONF_BOOL;
583 ct->flags = flags;
584 ct->un.bool_val.var = var;
585 ct->un.bool_val.def = def;
586
587 mowgli_node_add(ct, &ct->node, conflist);
588 conf_need_rehash = true;
589 }
590
del_top_conf(const char * name)591 void del_top_conf(const char *name)
592 {
593 struct ConfTable *ct;
594
595 return_if_fail(name != NULL);
596
597 if (!(ct = find_top_conf(name)))
598 {
599 slog(LG_DEBUG, "del_top_conf(): cannot delete nonexistant block %s", name);
600 return;
601 }
602
603 mowgli_node_delete(&ct->node, &confblocks);
604
605 free(ct->name);
606
607 mowgli_heap_free(conftable_heap, ct);
608 }
609
del_conf_item(const char * name,mowgli_list_t * conflist)610 void del_conf_item(const char *name, mowgli_list_t *conflist)
611 {
612 struct ConfTable *ct;
613
614 return_if_fail(name != NULL);
615 return_if_fail(conflist != NULL);
616
617 if (!(ct = find_conf_item(name, conflist)))
618 {
619 slog(LG_DEBUG, "del_conf_item(): cannot delete nonexistant item %s", name);
620 return;
621 }
622
623 mowgli_node_delete(&ct->node, conflist);
624
625 if (ct->type == CONF_DUPSTR && ct->un.dupstr_val.def)
626 {
627 free(ct->un.dupstr_val.def);
628 }
629
630 free(ct->name);
631
632 mowgli_heap_free(conftable_heap, ct);
633 }
634
conftable_get_conf_handler(struct ConfTable * ct)635 conf_handler_t conftable_get_conf_handler(struct ConfTable *ct)
636 {
637 return_val_if_fail(ct != NULL, NULL);
638
639 return ct->type == CONF_HANDLER ? ct->un.handler : NULL;
640 }
641
642 /* stolen from Sentinel */
token_to_value(struct Token token_table[],const char * token)643 int token_to_value(struct Token token_table[], const char *token)
644 {
645 int i;
646
647 return_val_if_fail(token_table != NULL, TOKEN_ERROR);
648 return_val_if_fail(token != NULL, TOKEN_ERROR);
649
650 for (i = 0; token_table[i].text != NULL; i++)
651 {
652 if (strcasecmp(token_table[i].text, token) == 0)
653 {
654 return token_table[i].value;
655 }
656 }
657
658 /* Otherwise... */
659 return TOKEN_UNMATCHED;
660 }
661
init_confprocess(void)662 void init_confprocess(void)
663 {
664 conftable_heap = sharedheap_get(sizeof(struct ConfTable));
665
666 if (!conftable_heap)
667 {
668 slog(LG_ERROR, "init_confprocess(): block allocator failure.");
669 exit(EXIT_FAILURE);
670 }
671 }
672
673 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
674 * vim:ts=8
675 * vim:sw=8
676 * vim:noexpandtab
677 */
678