ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/conffile.c
Revision: 1171
Committed: Fri Aug 12 20:00:46 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 25173 byte(s)
Log Message:
- Import ircservices-5.1.24. Don't ever think about modifying anything in this
  folder!
  Since Andrew Church has discontinued his services project in April 2011, the
  ircd-hybrid team has been given permissions to officially continue and
  maintain the already mentioned project.
  The name of this project will be changed for the reason being that the current
  name "IRC Services" is way too generic these days.

  Remember: Don't ever modify anything in here. This folder is kept for reference.

File Contents

# Content
1 /* Configuration file handling.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "conffile.h"
12
13 /*************************************************************************/
14
15 static void do_all_directives(int action, ConfigDirective *directives);
16
17 static int read_config_file(const char *modulename,
18 ConfigDirective *directives);
19
20 static int do_read_config_file(const char *modulename,
21 ConfigDirective *directives, FILE *f,
22 const char *filename, int recursion_level);
23
24 static int parse_config_line(const char *filename, int linenum, char *buf,
25 ConfigDirective *directives);
26
27
28 /* Actions for do_all_directives(): */
29
30 #define ACTION_COPYNEW 0 /* Copy `new' parameters to config variables */
31 #define ACTION_RESTORESAVED 1 /* Restore saved values of config variables */
32
33 /*************************************************************************/
34 /*************************************************************************/
35
36 /* Set configuration options for the given module (if `modulename' is NULL,
37 * set core configuration options). Returns nonzero on success, 0 on error
38 * (an error message is logged, and printed to the terminal if applicable,
39 * in this case). Returns successfully without doing anything if
40 * `directives' is NULL.
41 *
42 * `action' is a bitmask of CONFIGURE_* values (services.h), specifying
43 * what this function should do, as follows:
44 * - CONFIGURE_READ: read new values from the configuration file
45 * - CONFIGURE_SET: copy new values to configuration variables
46 * If both CONFIGURE_READ and CONFIGURE_SET are specified, new values are
47 * copied to the configuration variables only if all values are read in
48 * successfully (i.e. if a configure(...,CONFIGURE_READ) call would have
49 * returned success). CONFIGURE_SET alone will never fail.
50 */
51
52 int configure(const char *modulename, ConfigDirective *directives,
53 int action)
54 {
55 /* If no directives were given, return success */
56 if (!directives)
57 return 1;
58
59 if (action & CONFIGURE_READ) {
60 if (!read_config_file(modulename, directives))
61 return 0;
62 }
63
64 if (action & CONFIGURE_SET)
65 do_all_directives(ACTION_COPYNEW, directives);
66
67 return 1;
68 }
69
70 /*************************************************************************/
71
72 /* Deconfigure given directive array (free any allocated storage and
73 * restore original values). A no-op if `directives' is NULL.
74 */
75
76 void deconfigure(ConfigDirective *directives)
77 {
78 if (directives)
79 do_all_directives(ACTION_RESTORESAVED, directives);
80 }
81
82 /*************************************************************************/
83
84 /* Print a warning or error message to the log (and the console, if open). */
85
86 void config_error(const char *filename, int linenum, const char *message, ...)
87 {
88 char buf[4096];
89 va_list args;
90
91 va_start(args, message);
92 vsnprintf(buf, sizeof(buf), message, args);
93 va_end(args);
94 if (linenum)
95 log("%s:%d: %s", filename, linenum, buf);
96 else
97 log("%s: %s", filename, buf);
98 if (!nofork && isatty(2)) {
99 if (linenum)
100 fprintf(stderr, "%s:%d: %s\n", filename, linenum, buf);
101 else
102 fprintf(stderr, "%s: %s\n", filename, buf);
103 }
104 }
105
106 /*************************************************************************/
107 /*************************************************************************/
108
109 /* Perform an action for all directives in an array; the action is given
110 * by ACTION_*, defined above.
111 */
112
113 static void do_all_directives(int action, ConfigDirective *directives)
114 {
115 int n, i;
116
117 for (n = 0; directives[n].name; n++) {
118 ConfigDirective *d = &directives[n];
119 for (i = 0; i < CONFIG_MAXPARAMS && d->params[i].type != CD_NONE; i++){
120 CDValue val;
121
122 /* Select the appropriate value to copy */
123 if (action == ACTION_COPYNEW)
124 val = d->params[i].new;
125 else
126 val = d->params[i].prev;
127
128 /* In any case, we'll be rewriting the config variable, so free
129 * the previous value if it was one we allocated */
130 if (d->params[i].flags & CF_ALLOCED) {
131 free(*(void **)d->params[i].ptr);
132 d->params[i].flags &= ~CF_ALLOCED;
133 }
134
135 /* Don't do anything if we're copying new values and this
136 * directive/parameter wasn't seen, or if we're restoring saved
137 * values and this parameter hasn't had its value saved (except
138 * for function parameters) */
139 if (action == ACTION_COPYNEW
140 && (!d->was_seen || !(d->params[i].flags & CF_WASSET)))
141 continue;
142 if (action == ACTION_RESTORESAVED
143 && d->params[i].type != CD_FUNC
144 && !(d->params[i].flags & CF_SAVED))
145 continue;
146
147 /* Copy new value to configuration variable */
148 switch (d->params[i].type) {
149 case CD_SET:
150 if (action == ACTION_COPYNEW)
151 *(int *)d->params[i].ptr = (int)val.intval;
152 break;
153 case CD_TIME:
154 *(time_t *)d->params[i].ptr = val.timeval;
155 break;
156 case CD_STRING:
157 *(char **)d->params[i].ptr = val.ptrval;
158 break;
159 case CD_INT:
160 case CD_POSINT:
161 case CD_PORT:
162 case CD_TIMEMSEC:
163 *(int32 *)d->params[i].ptr = val.intval;
164 break;
165 case CD_FUNC: {
166 int (*func)(const char *, int, char *)
167 = (int (*)(const char *,int,char *))(d->params[i].ptr);
168 if (action == ACTION_COPYNEW)
169 func(NULL, CDFUNC_SET, NULL);
170 else
171 func(NULL, CDFUNC_DECONFIG, NULL);
172 break;
173 } /* case CD_FUNC */
174 case CD_DEPRECATED:
175 /* Nothing to do */
176 break;
177 default:
178 log("conffile: do_all_directives BUG: don't know how to "
179 " copy type %d (%s/%d)", d->params[i].type, d->name, i);
180 break;
181 } /* switch */
182
183 /* Fix up flags */
184 if (action == ACTION_COPYNEW) {
185 if (d->params[i].flags & CF_ALLOCED_NEW) {
186 d->params[i].flags |= CF_ALLOCED;
187 /* The value is still allocated, but it's now stored in
188 * the configuration variable, so we don't want to free
189 * it when clearing `new' */
190 d->params[i].flags &= ~CF_ALLOCED_NEW;
191 }
192 } else {
193 d->params[i].flags &= ~CF_SAVED;
194 }
195 } /* for each parameter */
196 } /* for each directive */
197 }
198
199 /*************************************************************************/
200
201 /* Read in configuration options, and return nonzero for success, zero for
202 * failure. Performs the actions needed by configure(...,CONFIGURE_READ).
203 */
204
205 static int read_config_file(const char *modulename,
206 ConfigDirective *directives)
207 {
208 const char *filename;
209 FILE *f;
210 int retval, i, n;
211
212 /* Open default file */
213 filename = modulename==NULL ? IRCSERVICES_CONF : MODULES_CONF;
214 f = fopen(filename, "r");
215 if (!f) {
216 log_perror("Unable to open %s", filename);
217 if (!nofork && isatty(2))
218 fprintf(stderr, "Unable to open %s: %s\n", filename,
219 strerror(errno));
220 return 0;
221 }
222
223 /* Clear `was_set' flag and `new' value for all directives */
224 for (n = 0; directives[n].name != NULL; n++) {
225 directives[n].was_seen = 0;
226 for (i = 0; i < CONFIG_MAXPARAMS; i++) {
227 if (directives[n].params[i].flags & CF_ALLOCED_NEW)
228 free(directives[n].params[i].new.ptrval);
229 directives[n].params[i].flags &= ~(CF_WASSET | CF_ALLOCED_NEW);
230 memset(&directives[n].params[i].new, 0,
231 sizeof(directives[n].params[i].new));
232 if (directives[n].params[i].type == CD_FUNC) {
233 int (*func)(const char *, int, char *)
234 = (int (*)(const char *, int, char *))
235 (directives[n].params[i].ptr);
236 func(NULL, CDFUNC_INIT, NULL);
237 }
238 }
239 }
240
241 /* Actually do the work */
242 retval = do_read_config_file(modulename, directives, f, filename, 0);
243
244 fclose(f);
245
246 /* Make sure all required directives were seen */
247 for (n = 0; directives[n].name != NULL; n++) {
248 if (!directives[n].was_seen
249 && (directives[n].params[0].flags & CF_DIRREQ)
250 ) {
251 config_error(filename, 0, "Required directive `%s' missing",
252 directives[n].name);
253 retval = 0;
254 }
255 }
256
257 return retval;
258 }
259
260 /*************************************************************************/
261
262 /* Read in a single configuration file, recursively processing IncludeFile
263 * directives.
264 */
265
266 static int do_read_config_file(const char *modulename,
267 ConfigDirective *directives, FILE *f,
268 const char *filename, int recursion_level)
269 {
270 char *current_module = NULL; /* Current module in modules.conf */
271 int retval = 1; /* Return value */
272 int linenum = 0;
273 char buf[4096], tmpbuf[4096], *s;
274
275 while (fgets(buf, sizeof(buf), f)) {
276 /* Check for pathologically long files */
277 if (linenum+1 < linenum) {
278 config_error(filename, linenum, "File too long");
279 retval = 0;
280 break;
281 }
282 linenum++;
283 /* Check for pathologically long lines */
284 if (strlen(buf) == sizeof(buf)-1 && buf[sizeof(buf)-1] != '\n') {
285 /* Report the maximum size as sizeof(buf)-3 to allow \r\n as
286 * well as \n to fit */
287 config_error(filename, linenum, "Line too long (%d bytes maximum)",
288 sizeof(buf)-3);
289 /* Skip everything else until an EOL (or EOF) is seen */
290 while (fgets(buf, sizeof(buf), f) && buf[strlen(buf)-1] != '\n')
291 /*nothing*/;
292 retval = 0;
293 }
294 /* Strip out comments (but don't touch # inside of quotes) */
295 s = buf;
296 while (*s) {
297 if (*s == '"') {
298 if (!(s = strchr(s+1, '"')))
299 break;
300 } else if (*s == '#') {
301 *s = 0;
302 break;
303 }
304 s++;
305 }
306 /* Check for IncludeFile directives (use tmpbuf to avoid damaging
307 * the original copy of the line) */
308 strbcpy(tmpbuf, buf);
309 s = strtok(tmpbuf, " \t\r\n");
310 if (s && stricmp(s, "IncludeFile") == 0) {
311 FILE *f2;
312 /* Check recursion level for infinite loops */
313 if (recursion_level > 100) {
314 config_error(filename, linenum,
315 "IncludeFile recursion depth limit exceeded");
316 retval = 0;
317 continue;
318 }
319 /* Find the filename */
320 s = strtok_remaining();
321 if (s && *s == '"') {
322 char *t = strchr(s+1, '"');
323 if (!t) {
324 config_error(filename, linenum,
325 "Missing closing double quote");
326 retval = 0;
327 continue;
328 }
329 *t = 0;
330 s++;
331 } else {
332 s = strtok(s, " \t\r\n");
333 }
334 if (!s || !*s) {
335 config_error(filename, linenum,
336 "Missing filename for IncludeFile");
337 retval = 0;
338 continue;
339 }
340 /* Valid filename string; try to open it */
341 f2 = fopen(s, "r");
342 if (!f2) {
343 config_error(filename, linenum,
344 "Unable to open %s: %s", s, strerror(errno));
345 retval = 0;
346 continue;
347 }
348 if (!do_read_config_file(modulename, directives, f2, s,
349 recursion_level+1))
350 retval = 0;
351 fclose(f2);
352 continue;
353 }
354 /* Handle Module/EndModule lines specially, and don't parse lines
355 * belonging to other modules */
356 if (modulename) {
357 if (current_module) {
358 /* Inside a Module/EndModule pair: discard lines belonging
359 * to other modules, and handle EndModule directives. If
360 * we reach EndModule for the module we're supposed to be
361 * processing, exit the loop to avoid unneeded processing. */
362 strbcpy(tmpbuf, buf);
363 s = strtok(tmpbuf, " \t\r\n");
364 if (s && stricmp(s, "EndModule") == 0) {
365 int strcmp_result = strcmp(current_module, modulename);
366 free(current_module);
367 if (strcmp_result == 0)
368 break; /* stop processing file, we're finished */
369 else
370 current_module = NULL;
371 continue;
372 } else if (strcmp(current_module, modulename) != 0) {
373 continue;
374 }
375 } else { /* !current_module */
376 /* Outside a Module/EndModule pair: handle Module
377 * directives, and report errors for anything else */
378 s = strtok(buf, " \t\r\n");
379 if (!s)
380 continue;
381 if (stricmp(s, "Module") != 0) {
382 config_error(filename, linenum,
383 "Expected `Module' directive");
384 retval = 0;
385 } else {
386 current_module = strtok(NULL, " \t\r\n");
387 if (!current_module) {
388 config_error(filename, linenum, "Module name missing");
389 retval = 0;
390 }
391 current_module = strdup(current_module);
392 if (!current_module) {
393 config_error(filename, linenum, "Out of memory");
394 retval = 0;
395 break;
396 }
397 }
398 continue;
399 } /* if (current_module) */
400 } /* if (modulename) */
401 /* Valid line--parse it */
402 if (!parse_config_line(filename, linenum, buf, directives))
403 retval = 0;
404 }
405
406 return retval;
407 }
408
409 /*************************************************************************/
410
411 /* Parse a configuration line. Return 1 on success; otherwise, print (and
412 * log, if applicable) appropriate error message and return 0. Destroys
413 * the buffer by side effect.
414 */
415
416 static int parse_config_line(const char *filename, int linenum, char *buf,
417 ConfigDirective *directives)
418 {
419 char *s, *t, *directive;
420 int i, n, optind;
421 long longval;
422 unsigned long ulongval;
423 int retval = 1;
424 int ac = 0;
425 char *av[CONFIG_MAXPARAMS];
426
427 directive = strtok(buf, " \t\r\n");
428 s = strtok(NULL, "");
429 if (s) {
430 while (isspace(*s))
431 s++;
432 while (*s) {
433 if (ac >= CONFIG_MAXPARAMS) {
434 config_error(filename, linenum,
435 "Warning: too many parameters (%d max)",
436 CONFIG_MAXPARAMS);
437 break;
438 }
439 t = s;
440 if (*s == '"') {
441 t++;
442 s++;
443 while (*s && *s != '"') {
444 if (*s == '\\' && s[1] != 0)
445 strmove(s, s+1);
446 s++;
447 }
448 if (!*s)
449 config_error(filename, linenum,
450 "Warning: unterminated double-quoted string");
451 else
452 *s++ = 0;
453 } else {
454 s += strcspn(s, " \t\r\n");
455 if (*s)
456 *s++ = 0;
457 }
458 av[ac++] = t;
459 while (isspace(*s))
460 s++;
461 }
462 }
463
464 if (!directive)
465 return 1;
466
467 for (n = 0; directives[n].name; n++) {
468 ConfigDirective *d = &directives[n];
469 if (stricmp(directive, d->name) != 0)
470 continue;
471 d->was_seen = 1;
472 optind = 0;
473 for (i = 0; i < CONFIG_MAXPARAMS && d->params[i].type != CD_NONE; i++){
474 if (d->params[i].type == CD_SET) {
475 if (!(d->params[i].flags & CF_SAVED)) {
476 d->params[i].prev.intval = *(int *)d->params[i].ptr;
477 d->params[i].flags |= CF_SAVED;
478 }
479 d->params[i].new.intval = 1;
480 d->params[i].flags |= CF_WASSET;
481 continue;
482 }
483 if (d->params[i].type == CD_DEPRECATED) {
484 config_error(filename, linenum,
485 "Deprecated directive `%s' used", d->name);
486 d->params[i].flags |= CF_WASSET;
487 continue;
488 }
489 if (optind >= ac) {
490 if (!(d->params[i].flags & CF_OPTIONAL)) {
491 config_error(filename, linenum,
492 "Not enough parameters for `%s'", d->name);
493 retval = 0;
494 }
495 break;
496 }
497 switch (d->params[i].type) {
498 case CD_INT:
499 if (!(d->params[i].flags & CF_SAVED)) {
500 d->params[i].prev.intval = *(int32 *)d->params[i].ptr;
501 d->params[i].flags |= CF_SAVED;
502 }
503 longval = strtol(av[optind++], &s, 0);
504 if (*s) {
505 config_error(filename, linenum,
506 "%s: Expected an integer for parameter %d",
507 d->name, optind);
508 retval = 0;
509 break;
510 }
511 #if SIZEOF_LONG > 4
512 if (longval < -0x80000000L || longval > 0x7FFFFFFFL) {
513 config_error(filename, linenum,
514 "%s: Value out of range for parameter %d",
515 d->name, optind);
516 retval = 0;
517 break;
518 }
519 #endif
520 d->params[i].new.intval = (int32)longval;
521 break;
522 case CD_POSINT:
523 if (!(d->params[i].flags & CF_SAVED)) {
524 d->params[i].prev.intval = *(int32 *)d->params[i].ptr;
525 d->params[i].flags |= CF_SAVED;
526 }
527 ulongval = strtoul(av[optind++], &s, 0);
528 if (*s || ulongval <= 0) {
529 config_error(filename, linenum,
530 "%s: Expected a positive integer for"
531 " parameter %d", d->name, optind);
532 retval = 0;
533 break;
534 }
535 #if SIZEOF_LONG > 4
536 if (ulongval > 0xFFFFFFFFL) {
537 config_error(filename, linenum,
538 "%s: Value out of range for parameter %d",
539 d->name, optind);
540 retval = 0;
541 break;
542 }
543 #endif
544 d->params[i].new.intval = (int32)ulongval;
545 break;
546 case CD_PORT:
547 if (!(d->params[i].flags & CF_SAVED)) {
548 d->params[i].prev.intval = *(int32 *)d->params[i].ptr;
549 d->params[i].flags |= CF_SAVED;
550 }
551 longval = strtol(av[optind++], &s, 0);
552 if (*s) {
553 config_error(filename, linenum,
554 "%s: Expected a port number for parameter %d",
555 d->name, optind);
556 retval = 0;
557 break;
558 }
559 if (longval < 1 || longval > 65535) {
560 config_error(filename, linenum,
561 "Port numbers must be in the range 1..65535");
562 retval = 0;
563 break;
564 }
565 d->params[i].new.intval = (int32)longval;
566 break;
567 case CD_STRING:
568 if (!(d->params[i].flags & CF_SAVED)) {
569 d->params[i].prev.ptrval = *(char **)d->params[i].ptr;
570 d->params[i].flags |= CF_SAVED;
571 }
572 d->params[i].new.ptrval = strdup(av[optind++]);
573 if (!d->params[i].new.ptrval) {
574 config_error(filename, linenum, "%s: Out of memory",
575 d->name);
576 return 0;
577 }
578 d->params[i].flags |= CF_ALLOCED_NEW;
579 break;
580 case CD_TIME:
581 if (!(d->params[i].flags & CF_SAVED)) {
582 d->params[i].prev.timeval = *(time_t *)d->params[i].ptr;
583 d->params[i].flags |= CF_SAVED;
584 }
585 d->params[i].new.timeval = dotime(av[optind++]);
586 if (d->params[i].new.timeval < 0) {
587 config_error(filename, linenum,
588 "%s: Expected a time value for parameter %d",
589 d->name, optind);
590 retval = 0;
591 break;
592 }
593 break;
594 case CD_TIMEMSEC:
595 if (!(d->params[i].flags & CF_SAVED)) {
596 d->params[i].prev.intval = *(int32 *)d->params[i].ptr;
597 d->params[i].flags |= CF_SAVED;
598 }
599 longval = strtol(av[optind++], &s, 10);
600 if (longval < 0) {
601 config_error(filename, linenum,
602 "%s: Expected a positive value for"
603 " parameter %d", d->name, optind);
604 retval = 0;
605 break;
606 } else if (longval > 1000000) {
607 config_error(filename, linenum,
608 "%s: Value too large (maximum 1000000)",
609 d->name);
610 }
611 longval *= 1000;
612 if (*s == '.') {
613 int decimal = 0;
614 int count = 0;
615 s++;
616 while (count < 3 && isdigit(*s)) {
617 decimal = decimal*10 + (*s++ - '0');
618 count++;
619 }
620 while (count++ < 3)
621 decimal *= 10;
622 longval += decimal;
623 while (isdigit(*s))
624 s++;
625 }
626 if (*s) {
627 config_error(filename, linenum,
628 "%s: Expected a decimal number for"
629 " parameter %d", d->name, optind);
630 retval = 0;
631 break;
632 }
633 d->params[i].new.intval = (int32)longval;
634 break;
635 case CD_FUNC: {
636 int (*func)(const char *, int, char *)
637 = (int (*)(const char *, int, char *))(d->params[i].ptr);
638 if (!func(filename, linenum, av[optind++]))
639 retval = 0;
640 break;
641 }
642 default:
643 config_error(filename, linenum, "%s: Unknown type %d for"
644 " param %d", d->name, d->params[i].type, i+1);
645 return 0; /* don't bother continuing--something's bizarre */
646 } /* switch (d->params[i].type) */
647 d->params[i].flags |= CF_WASSET;
648 } /* for all parameters */
649 break; /* because we found a match */
650 } /* for all directives in array */
651
652 if (!directives[n].name) {
653 config_error(filename, linenum, "Unknown directive `%s'", directive);
654 return 1; /* don't cause abort */
655 }
656
657 return retval;
658 } /* parse_config_line() */
659
660 /*************************************************************************/
661
662 /*
663 * Local variables:
664 * c-file-style: "stroustrup"
665 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
666 * indent-tabs-mode: nil
667 * End:
668 *
669 * vim: expandtab shiftwidth=4:
670 */