1 /* $Id: innconf.c 7751 2008-04-06 14:35:40Z iulius $
3 ** Manage the global innconf struct.
5 ** The functions in this file collapse the parse tree for inn.conf into the
6 ** innconf struct that's used throughout INN. The code to collapse a
7 ** configuration parse tree into a struct is fairly generic and should
8 ** probably be moved into a separate library.
10 ** When adding new inn.conf parameters, make sure to add them in all of the
13 ** * The table in this file.
14 ** * include/inn/innconf.h
15 ** * doc/pod/inn.conf.pod (and regenerate doc/man/inn.conf.5)
16 ** * Add the default value to samples/inn.conf.in
18 ** Please maintain the current organization of parameters. There are two
19 ** different orders, one of which is a logical order used by the
20 ** documentation, the include file, and the sample file, and the other of
21 ** which is used in this file. The order in this file is documentation of
22 ** where each parameter is used, for later work at breaking up this mess
23 ** of parameters into separate configuration groups for each INN subsystem.
30 #include "inn/confparse.h"
31 #include "inn/innconf.h"
32 #include "inn/messages.h"
33 #include "inn/vector.h"
37 /* Instantiation of the global innconf variable. */
38 struct innconf *innconf = NULL;
40 /* Data types used to express the mappings from the configuration parse into
41 the innconf struct. */
60 /* The following macros are helpers to make it easier to define the table that
61 specifies how to convert the configuration file into a struct. */
63 #define K(name) (#name), offsetof(struct innconf, name)
65 #define BOOL(def) TYPE_BOOLEAN, { (def), 0, NULL }
66 #define NUMBER(def) TYPE_NUMBER, { 0, (def), NULL }
67 #define STRING(def) TYPE_STRING, { 0, 0, (def) }
69 /* Accessor macros to get a pointer to a value inside a struct. */
70 #define CONF_BOOL(conf, offset) (bool *) (void *)((char *) (conf) + (offset))
71 #define CONF_LONG(conf, offset) (long *) (void *)((char *) (conf) + (offset))
72 #define CONF_STRING(conf, offset) (char **)(void *)((char *) (conf) + (offset))
76 checkincludedtext and localmaxartisize are used by both nnrpd and inews,
77 but inews should probably just let nnrpd do that checking.
79 organization is used by both nnrpd and inews. Perhaps inews should just
82 mergetogroups is currently used by nnrpd for permission checking on
83 posting. I think the check should always be performed based on the
84 newsgroup to which the user is actually posting, and nnrpd should let
87 useoverchan is only used in innd and overchan. It should probably be
88 something the storage system knows. Ideally, the storage system would
89 handle overchan itself, but that would require a lot of infrastructure;
90 in the interim, it could be something that programs could ask the
91 overview subsystem about.
93 doinnwatch and docnfsstat are used by rc.news currently, but really
94 should be grouped with the appropriate subsystem.
96 newsrequeue also uses nntplinklog, but the parameter should go away
99 timer is currently used in various places, but it may be best to replace
100 it with individual settings for innd and innfeed, and any other code that
103 maxforks is used by rnews and nnrpd. I'm not sure this is that useful of
107 const struct config config_table[] = {
108 { K(domain), STRING (NULL) },
109 { K(enableoverview), BOOL (true) },
110 { K(fromhost), STRING (NULL) },
111 { K(groupbaseexpiry), BOOL (true) },
112 { K(mailcmd), STRING (NULL) },
113 { K(maxforks), NUMBER (10) },
114 { K(mta), STRING (NULL) },
115 { K(nicekids), NUMBER (4) },
116 { K(ovmethod), STRING (NULL) },
117 { K(pathhost), STRING (NULL) },
118 { K(rlimitnofile), NUMBER (-1) },
119 { K(server), STRING (NULL) },
120 { K(sourceaddress), STRING (NULL) },
121 { K(sourceaddress6), STRING (NULL) },
122 { K(timer), NUMBER (0) },
124 { K(patharchive), STRING (NULL) },
125 { K(patharticles), STRING (NULL) },
126 { K(pathbin), STRING (NULL) },
127 { K(pathcontrol), STRING (NULL) },
128 { K(pathdb), STRING (NULL) },
129 { K(pathetc), STRING (NULL) },
130 { K(pathfilter), STRING (NULL) },
131 { K(pathhttp), STRING (NULL) },
132 { K(pathincoming), STRING (NULL) },
133 { K(pathlog), STRING (NULL) },
134 { K(pathnews), STRING (NULL) },
135 { K(pathoutgoing), STRING (NULL) },
136 { K(pathoverview), STRING (NULL) },
137 { K(pathrun), STRING (NULL) },
138 { K(pathspool), STRING (NULL) },
139 { K(pathtmp), STRING (NULL) },
141 /* The following settings are specific to innd. */
142 { K(artcutoff), NUMBER (10) },
143 { K(badiocount), NUMBER (5) },
144 { K(bindaddress), STRING (NULL) },
145 { K(bindaddress6), STRING (NULL) },
146 { K(blockbackoff), NUMBER (120) },
147 { K(chaninacttime), NUMBER (600) },
148 { K(chanretrytime), NUMBER (300) },
149 { K(datamovethreshold), NUMBER (8192) },
150 { K(dontrejectfiltered), BOOL (false) },
151 { K(hiscachesize), NUMBER (0) },
152 { K(icdsynccount), NUMBER (10) },
153 { K(ignorenewsgroups), BOOL (false) },
154 { K(linecountfuzz), NUMBER (0) },
155 { K(logartsize), BOOL (true) },
156 { K(logcancelcomm), BOOL (false) },
157 { K(logipaddr), BOOL (true) },
158 { K(logsitename), BOOL (true) },
159 { K(maxartsize), NUMBER (1000000) },
160 { K(maxconnections), NUMBER (50) },
161 { K(mergetogroups), BOOL (false) },
162 { K(nntpactsync), NUMBER (200) },
163 { K(nntplinklog), BOOL (false) },
164 { K(noreader), BOOL (false) },
165 { K(pathalias), STRING (NULL) },
166 { K(pathcluster), STRING (NULL) },
167 { K(pauseretrytime), NUMBER (300) },
168 { K(peertimeout), NUMBER (3600) },
169 { K(port), NUMBER (119) },
170 { K(readerswhenstopped), BOOL (false) },
171 { K(refusecybercancels), BOOL (false) },
172 { K(remembertrash), BOOL (true) },
173 { K(stathist), STRING (NULL) },
174 { K(status), NUMBER (0) },
175 { K(verifycancels), BOOL (false) },
176 { K(wanttrash), BOOL (false) },
177 { K(wipcheck), NUMBER (5) },
178 { K(wipexpire), NUMBER (10) },
179 { K(xrefslave), BOOL (false) },
181 /* The following settings are specific to nnrpd. */
182 { K(addnntppostingdate), BOOL (true) },
183 { K(addnntppostinghost), BOOL (true) },
184 { K(allownewnews), BOOL (true) },
185 { K(backoffauth), BOOL (false) },
186 { K(backoffdb), STRING (NULL) },
187 { K(backoffk), NUMBER (1) },
188 { K(backoffpostfast), NUMBER (0) },
189 { K(backoffpostslow), NUMBER (1) },
190 { K(backofftrigger), NUMBER (10000) },
191 { K(checkincludedtext), BOOL (false) },
192 { K(clienttimeout), NUMBER (600) },
193 { K(complaints), STRING (NULL) },
194 { K(initialtimeout), NUMBER (10) },
195 { K(keyartlimit), NUMBER (100000) },
196 { K(keylimit), NUMBER (512) },
197 { K(keymaxwords), NUMBER (250) },
198 { K(keywords), BOOL (false) },
199 { K(localmaxartsize), NUMBER (1000000) },
200 { K(maxcmdreadsize), NUMBER (BUFSIZ) },
201 { K(msgidcachesize), NUMBER (10000) },
202 { K(moderatormailer), STRING (NULL) },
203 { K(nfsreader), BOOL (false) },
204 { K(nfsreaderdelay), NUMBER (60) },
205 { K(nicenewnews), NUMBER (0) },
206 { K(nicennrpd), NUMBER (0) },
207 { K(nnrpdflags), STRING ("") },
208 { K(nnrpdauthsender), BOOL (false) },
209 { K(nnrpdloadlimit), NUMBER (16) },
210 { K(nnrpdoverstats), BOOL (false) },
211 { K(organization), STRING (NULL) },
212 { K(readertrack), BOOL (false) },
213 { K(spoolfirst), BOOL (false) },
214 { K(strippostcc), BOOL (false) },
216 /* The following settings are used by nnrpd and rnews. */
217 { K(nnrpdposthost), STRING (NULL) },
218 { K(nnrpdpostport), NUMBER (119) },
220 /* The following settings are specific to the storage subsystem. */
221 { K(articlemmap), BOOL (false) },
222 { K(cnfscheckfudgesize), NUMBER (0) },
223 { K(immediatecancel), BOOL (false) },
224 { K(keepmmappedthreshold), NUMBER (1024) },
225 { K(nfswriter), BOOL (false) },
226 { K(nnrpdcheckart), BOOL (true) },
227 { K(overcachesize), NUMBER (15) },
228 { K(ovgrouppat), STRING (NULL) },
229 { K(storeonxref), BOOL (true) },
230 { K(tradindexedmmap), BOOL (true) },
231 { K(useoverchan), BOOL (false) },
232 { K(wireformat), BOOL (false) },
234 /* The following settings are specific to the history subsystem. */
235 { K(hismethod), STRING (NULL) },
237 /* The following settings are specific to rc.news. */
238 { K(docnfsstat), BOOL (false) },
239 { K(innflags), STRING (NULL) },
240 { K(pgpverify), BOOL (false) },
242 /* The following settings are specific to innwatch. */
243 { K(doinnwatch), BOOL (true) },
244 { K(innwatchbatchspace), NUMBER (800) },
245 { K(innwatchlibspace), NUMBER (25000) },
246 { K(innwatchloload), NUMBER (1000) },
247 { K(innwatchhiload), NUMBER (2000) },
248 { K(innwatchpauseload), NUMBER (1500) },
249 { K(innwatchsleeptime), NUMBER (600) },
250 { K(innwatchspoolnodes), NUMBER (200) },
251 { K(innwatchspoolspace), NUMBER (8000) },
253 /* The following settings are specific to scanlogs. */
254 { K(logcycles), NUMBER (3) },
259 ** Set some defaults that cannot be included in the table because they depend
260 ** on other elements or require function calls to set. Called after the
261 ** configuration file is read, so any that have to override what's read have
262 ** to free whatever values are set by the file.
265 innconf_set_defaults(void)
269 /* Some environment variables override settings in inn.conf. */
270 value = getenv("FROMHOST");
272 if (innconf->fromhost != NULL)
273 free(innconf->fromhost);
274 innconf->fromhost = xstrdup(value);
276 value = getenv("NNTPSERVER");
278 if (innconf->server != NULL)
279 free(innconf->server);
280 innconf->server = xstrdup(value);
282 value = getenv("ORGANIZATION");
284 if (innconf->organization != NULL)
285 free(innconf->organization);
286 innconf->organization = xstrdup(value);
288 value = getenv("INND_BIND_ADDRESS");
290 if (innconf->bindaddress != NULL)
291 free(innconf->bindaddress);
292 innconf->bindaddress = xstrdup(value);
294 value = getenv("INND_BIND_ADDRESS6");
296 if (innconf->bindaddress6 != NULL)
297 free(innconf->bindaddress6);
298 innconf->bindaddress6 = xstrdup(value);
301 /* Some parameters have defaults that depend on other parameters. */
302 if (innconf->fromhost == NULL)
303 innconf->fromhost = xstrdup(GetFQDN(innconf->domain));
304 if (innconf->pathhost == NULL)
305 innconf->pathhost = xstrdup(GetFQDN(innconf->domain));
306 if (innconf->pathtmp == NULL)
307 innconf->pathtmp = xstrdup(_PATH_TMP);
309 /* All of the paths are relative to other paths if not set except for
310 pathnews, which is required to be set by innconf_validate. */
311 if (innconf->pathbin == NULL)
312 innconf->pathbin = concatpath(innconf->pathnews, "bin");
313 if (innconf->pathfilter == NULL)
314 innconf->pathfilter = concatpath(innconf->pathbin, "filter");
315 if (innconf->pathdb == NULL)
316 innconf->pathdb = concatpath(innconf->pathnews, "db");
317 if (innconf->pathetc == NULL)
318 innconf->pathetc = concatpath(innconf->pathnews, "etc");
319 if (innconf->pathrun == NULL)
320 innconf->pathrun = concatpath(innconf->pathnews, "run");
321 if (innconf->pathlog == NULL)
322 innconf->pathlog = concatpath(innconf->pathnews, "log");
323 if (innconf->pathhttp == NULL)
324 innconf->pathhttp = xstrdup(innconf->pathlog);
325 if (innconf->pathspool == NULL)
326 innconf->pathspool = concatpath(innconf->pathnews, "spool");
327 if (innconf->patharticles == NULL)
328 innconf->patharticles = concatpath(innconf->pathspool, "articles");
329 if (innconf->pathoverview == NULL)
330 innconf->pathoverview = concatpath(innconf->pathspool, "overview");
331 if (innconf->pathoutgoing == NULL)
332 innconf->pathoutgoing = concatpath(innconf->pathspool, "outgoing");
333 if (innconf->pathincoming == NULL)
334 innconf->pathincoming = concatpath(innconf->pathspool, "incoming");
335 if (innconf->patharchive == NULL)
336 innconf->patharchive = concatpath(innconf->pathspool, "archive");
338 /* One other parameter depends on pathbin. */
339 if (innconf->mailcmd == NULL)
340 innconf->mailcmd = concatpath(innconf->pathbin, "innmail");
345 ** Given a config_group struct representing the inn.conf file, parse that
346 ** into an innconf struct and return the newly allocated struct. This
347 ** routine should be pulled out into a library for smashing configuration
348 ** file parse results into structs.
350 static struct innconf *
351 innconf_parse(struct config_group *group)
356 const char *char_ptr;
358 struct innconf *config;
360 config = xmalloc(sizeof(struct innconf));
361 for (i = 0; i < ARRAY_SIZE(config_table); i++)
362 switch (config_table[i].type) {
364 bool_ptr = CONF_BOOL(config, config_table[i].location);
365 if (!config_param_boolean(group, config_table[i].name, bool_ptr))
366 *bool_ptr = config_table[i].defaults.boolean;
369 long_ptr = CONF_LONG(config, config_table[i].location);
370 if (!config_param_integer(group, config_table[i].name, long_ptr))
371 *long_ptr = config_table[i].defaults.integer;
374 if (!config_param_string(group, config_table[i].name, &char_ptr))
375 char_ptr = config_table[i].defaults.string;
376 string = CONF_STRING(config, config_table[i].location);
377 *string = (char_ptr == NULL) ? NULL : xstrdup(char_ptr);
380 die("internal error: invalid type in row %u of config table", i);
388 ** Check the configuration file for consistency and ensure that mandatory
389 ** settings are present. Returns true if the file is okay and false
393 innconf_validate(struct config_group *group)
398 if (GetFQDN(innconf->domain) == NULL) {
399 warn("hostname does not resolve or domain not set in inn.conf");
402 if (innconf->mta == NULL) {
403 warn("must set mta in inn.conf");
406 if (innconf->pathnews == NULL) {
407 warn("must set pathnews in inn.conf");
410 if (innconf->hismethod == NULL) {
411 warn("must set hismethod in inn.conf");
414 if (innconf->enableoverview && innconf->ovmethod == NULL) {
415 warn("ovmethod must be set in inn.conf if enableoverview is true");
418 threshold = innconf->datamovethreshold;
419 if (threshold <= 0 || threshold > 1024 * 1024) {
420 config_error_param(group, "datamovethreshold",
421 "maximum value for datamovethreshold is 1MB");
422 innconf->datamovethreshold = 1024 * 1024;
429 ** Read in inn.conf. Takes a single argument, which is either NULL to read
430 ** the default configuration file or a path to an alternate configuration
431 ** file to read. Returns true if the file was read successfully and false
435 innconf_read(const char *path)
437 struct config_group *group;
441 innconf_free(innconf);
443 path = getenv("INNCONF");
444 group = config_parse_file(path == NULL ? _PATH_CONFIG : path);
448 innconf = innconf_parse(group);
449 if (!innconf_validate(group))
452 innconf_set_defaults();
454 /* It's not clear that this belongs here, but it was done by the old
455 configuration parser, so this is a convenient place to do it. */
456 tmpdir = getenv("TMPDIR");
457 if (tmpdir == NULL || strcmp(tmpdir, innconf->pathtmp) != 0)
458 if (setenv("TMPDIR", innconf->pathtmp, true) != 0) {
459 warn("cannot set TMPDIR in the environment");
468 ** Check an inn.conf file. This involves reading it in and then additionally
469 ** making sure that there are no keys defined in the inn.conf file that
470 ** aren't recognized. This doesn't have to be very fast (and isn't).
471 ** Returns true if everything checks out successfully, and false otherwise.
473 ** A lot of code is duplicated with innconf_read here and should be
477 innconf_check(const char *path)
479 struct config_group *group;
480 struct vector *params;
486 innconf_free(innconf);
488 path = getenv("INNCONF");
489 group = config_parse_file(path == NULL ? _PATH_CONFIG : path);
493 innconf = innconf_parse(group);
494 if (!innconf_validate(group))
497 /* Now, do the work that innconf_read doesn't do. Get a list of
498 parameters defined in innconf and then walk our list of valid
499 parameters and see if there are any set that we don't recognize. */
500 params = config_params(group);
501 for (set = 0; set < params->count; set++) {
503 for (known = 0; known < ARRAY_SIZE(config_table); known++)
504 if (strcmp(params->strings[set], config_table[known].name) == 0)
507 config_error_param(group, params->strings[set],
508 "unknown parameter %s", params->strings[set]);
513 /* Check and warn about a few other parameters. */
514 if (innconf->peertimeout < 3 * 60)
515 config_error_param(group, "peertimeout",
516 "warning: NNTP draft (15) states inactivity"
517 " timeouts MUST be at least three minutes");
518 if (innconf->clienttimeout < 3 * 60)
519 config_error_param(group, "clienttimeout",
520 "warning: NNTP draft (15) states inactivity"
521 " timeouts MUST be at least three minutes");
523 /* All done. Free the parse tree and return. */
530 ** Free innconf, requiring some complexity since all strings stored in the
531 ** innconf struct are allocated memory. This routine is mostly generic to
532 ** any struct smashed down from a configuration file parse.
535 innconf_free(struct innconf *config)
540 for (i = 0; i < ARRAY_SIZE(config_table); i++)
541 if (config_table[i].type == TYPE_STRING) {
542 p = *CONF_STRING(config, config_table[i].location);
551 ** Print a single boolean value with appropriate quoting.
554 print_boolean(FILE *file, const char *key, bool value,
555 enum innconf_quoting quoting)
560 case INNCONF_QUOTE_NONE:
561 fprintf(file, "%s\n", value ? "true" : "false");
563 case INNCONF_QUOTE_SHELL:
564 upper = xstrdup(key);
565 for (p = upper; *p != '\0'; p++)
567 fprintf(file, "%s=%s; export %s;\n", upper, value ? "true" : "false",
571 case INNCONF_QUOTE_PERL:
572 fprintf(file, "$%s = '%s';\n", key, value ? "true" : "false");
574 case INNCONF_QUOTE_TCL:
575 fprintf(file, "set inn_%s \"%s\"\n", key, value ? "true" : "false");
582 ** Print a single integer value with appropriate quoting.
585 print_number(FILE *file, const char *key, long value,
586 enum innconf_quoting quoting)
591 case INNCONF_QUOTE_NONE:
592 fprintf(file, "%ld\n", value);
594 case INNCONF_QUOTE_SHELL:
595 upper = xstrdup(key);
596 for (p = upper; *p != '\0'; p++)
598 fprintf(file, "%s=%ld; export %s;\n", upper, value, upper);
601 case INNCONF_QUOTE_PERL:
602 fprintf(file, "$%s = %ld;\n", key, value);
604 case INNCONF_QUOTE_TCL:
605 fprintf(file, "set inn_%s %ld\n", key, value);
612 ** Print a single string value with appropriate quoting.
615 print_string(FILE *file, const char *key, const char *value,
616 enum innconf_quoting quoting)
620 static const char tcl_unsafe[] = "$[]{}\"\\";
623 case INNCONF_QUOTE_NONE:
624 fprintf(file, "%s\n", value != NULL ? value : "");
626 case INNCONF_QUOTE_SHELL:
627 upper = xstrdup(key);
628 for (p = upper; *p != '\0'; p++)
630 fprintf(file, "%s='", upper);
631 for (letter = value; letter != NULL && *letter != '\0'; letter++) {
633 fputs("'\\''", file);
634 else if (*letter == '\\')
637 fputc(*letter, file);
639 fprintf(file, "'; export %s;\n", upper);
642 case INNCONF_QUOTE_PERL:
643 fprintf(file, "$%s = '", key);
644 for (letter = value; letter != NULL && *letter != '\0'; letter++) {
645 if (*letter == '\'' || *letter == '\\')
647 fputc(*letter, file);
651 case INNCONF_QUOTE_TCL:
652 fprintf(file, "set inn_%s \"", key);
653 for (letter = value; letter != NULL && *letter != '\0'; letter++) {
654 if (strchr(tcl_unsafe, *letter) != NULL)
656 fputc(*letter, file);
665 ** Print a single paramter to the given file. Takes an index into the table
666 ** specifying the attribute to print and the quoting.
669 print_parameter(FILE *file, size_t i, enum innconf_quoting quoting)
673 const char *string_val;
675 switch (config_table[i].type) {
677 bool_val = *CONF_BOOL(innconf, config_table[i].location);
678 print_boolean(file, config_table[i].name, bool_val, quoting);
681 long_val = *CONF_LONG(innconf, config_table[i].location);
682 print_number(file, config_table[i].name, long_val, quoting);
685 string_val = *CONF_STRING(innconf, config_table[i].location);
686 print_string(file, config_table[i].name, string_val, quoting);
689 die("internal error: invalid type in row %d of config table", i);
696 ** Given a single parameter, find it in the table and print out its value.
699 innconf_print_value(FILE *file, const char *key, enum innconf_quoting quoting)
703 for (i = 0; i < ARRAY_SIZE(config_table); i++)
704 if (strcmp(key, config_table[i].name) == 0) {
705 print_parameter(file, i, quoting);
713 ** Dump the entire inn.conf configuration with appropriate quoting.
716 innconf_dump(FILE *file, enum innconf_quoting quoting)
720 for (i = 0; i < ARRAY_SIZE(config_table); i++)
721 print_parameter(file, i, quoting);
726 ** Compare two innconf structs to see if they represent identical
727 ** configurations. This routine is mostly used for testing. Prints warnings
728 ** about where the two configurations differ and return false if they differ,
729 ** true if they match. This too should be moved into a config smashing
733 innconf_compare(struct innconf *conf1, struct innconf *conf2)
738 const char *string1, *string2;
741 for (i = 0; i < ARRAY_SIZE(config_table); i++)
742 switch (config_table[i].type) {
744 bool1 = *CONF_BOOL(conf1, config_table[i].location);
745 bool2 = *CONF_BOOL(conf2, config_table[i].location);
746 if (bool1 != bool2) {
747 warn("boolean variable %s differs: %d != %d",
748 config_table[i].name, bool1, bool2);
753 long1 = *CONF_LONG(conf1, config_table[i].location);
754 long2 = *CONF_LONG(conf2, config_table[i].location);
755 if (long1 != long2) {
756 warn("integer variable %s differs: %ld != %ld",
757 config_table[i].name, long1, long2);
762 string1 = *CONF_STRING(conf1, config_table[i].location);
763 string2 = *CONF_STRING(conf2, config_table[i].location);
764 if (string1 == NULL && string2 != NULL) {
765 warn("string variable %s differs: NULL != %s",
766 config_table[i].name, string2);
768 } else if (string1 != NULL && string2 == NULL) {
769 warn("string variable %s differs: %s != NULL",
770 config_table[i].name, string1);
772 } else if (string1 != NULL && string2 != NULL) {
773 if (strcmp(string1, string2) != 0) {
774 warn("string variable %s differs: %s != %s",
775 config_table[i].name, string1, string2);
781 die("internal error: invalid type in row %d of config table", i);