2 * onak-conf.c - Routines related to runtime config.
4 * Copyright 2002,2012 Jonathan McDowell <noodles@earth.li>
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <https://www.gnu.org/licenses/>.
30 #include "onak-conf.h"
32 extern struct onak_dbctx *DBINIT(struct onak_db_config *dbcfg, bool readonly);
35 * config - Runtime configuration for onak.
37 * This is the default config; normally overridden with values from the
40 struct onak_config config = {
56 .clean_policies = ONAK_CLEAN_CHECK_SIGHASH,
62 struct onak_db_config *find_db_backend_config(struct ll *backends, char *name)
67 while (cur != NULL && strcmp(name,
68 ((struct onak_db_config *) cur->object)->name)) {
72 return (cur != NULL) ? (struct onak_db_config *) cur->object : NULL;
75 bool parsebool(char *str, bool fallback)
77 if (!strcasecmp(str, "false") || !strcasecmp(str, "no") ||
78 !strcasecmp(str, "0")) {
80 } else if (!strcasecmp(str, "true") || !strcasecmp(str, "yes") ||
81 !strcasecmp(str, "1")) {
84 logthing(LOGTHING_CRITICAL,
85 "Couldn't parse %s as a boolean config variable, "
86 "returning fallback of '%s'.",
88 fallback ? "true" : "false");
94 * Parse an old pksd style config line, as used in onak 0.4.6 and earlier.
96 static bool parseoldconfigline(char *line)
98 if (line[0] == '#' || line[0] == 0) {
100 * Comment line, ignore.
102 } else if (!strncmp("db_dir ", line, 7)) {
103 config.backend->location = strdup(&line[7]);
104 } else if (!strncmp("debug ", line, 6)) {
106 * Not supported yet; ignore for compatibility with
109 } else if (!strncmp("default_language ", line, 17)) {
111 * Not supported yet; ignore for compatibility with
114 } else if (!strncmp("mail_delivery_client ", line, 21)) {
115 config.mta = strdup(&line[21]);
116 } else if (!strncmp("maintainer_email ", line, 17)) {
117 config.adminemail = strdup(&line[17]);
118 } else if (!strncmp("mail_intro_file ", line, 16)) {
120 * Not supported yet; ignore for compatibility with
123 } else if (!strncmp("help_dir ", line, 9)) {
125 * Not supported yet; ignore for compatibility with
128 } else if (!strncmp("max_last ", line, 9)) {
130 * Not supported yet; ignore for compatibility with
133 } else if (!strncmp("max_reply_keys ", line, 15)) {
134 config.maxkeys = atoi(&line[15]);
135 } else if (!strncmp("pg_dbhost ", line, 10)) {
136 config.backend->hostname = strdup(&line[10]);
137 } else if (!strncmp("pg_dbname ", line, 10)) {
138 config.backend->location = strdup(&line[10]);
139 } else if (!strncmp("pg_dbuser ", line, 10)) {
140 config.backend->username = strdup(&line[10]);
141 } else if (!strncmp("pg_dbpass ", line, 10)) {
142 config.backend->password = strdup(&line[10]);
143 } else if (!strncmp("syncsite ", line, 9)) {
145 lladd(config.syncsites, strdup(&line[9]));
146 } else if (!strncmp("logfile ", line, 8)) {
147 config.logfile = strdup(&line[8]);
148 } else if (!strncmp("loglevel ", line, 9)) {
149 setlogthreshold(atoi(&line[9]));
150 } else if (!strncmp("this_site ", line, 10)) {
151 config.thissite = strdup(&line[10]);
152 } else if (!strncmp("socket_name ", line, 12) ||
153 !strncmp("www_port ", line, 9)) {
155 * Not applicable; ignored for compatibility with pksd.
157 } else if (!strncmp("pks_bin_dir ", line, 12)) {
158 config.bin_dir = strdup(&line[12]);
159 } else if (!strncmp("mail_dir ", line, 9)) {
160 config.mail_dir = strdup(&line[9]);
161 } else if (!strncmp("db_backend ", line, 11)) {
162 config.backend->type = strdup(&line[11]);
163 config.backend->name = strdup(&line[11]);
164 config.db_backend = strdup(&line[11]);
165 } else if (!strncmp("backends_dir ", line, 13)) {
166 config.backends_dir = strdup(&line[13]);
167 } else if (!strncmp("use_keyd ", line, 9)) {
168 config.use_keyd = parsebool(&line[9],
170 } else if (!strncmp("sock_dir ", line, 9)) {
171 config.sock_dir = strdup(&line[9]);
172 } else if (!strncmp("check_sighash ", line, 9)) {
173 if (parsebool(&line[9], config.clean_policies &
174 ONAK_CLEAN_CHECK_SIGHASH)) {
175 config.clean_policies |=
176 ONAK_CLEAN_CHECK_SIGHASH;
178 config.clean_policies &=
179 ~ONAK_CLEAN_CHECK_SIGHASH;
189 * Parse a new style .ini config line, supporting [sections] and name=value
192 static bool parseconfigline(char *line)
194 /* Yes, this means we're not re-entrant. */
195 static char section[32] = "";
196 struct onak_db_config *backend;
200 if (line[0] == '#' || line[0] == ';' ||
203 * Comment line, ignore.
205 } else if (line[0] == '[') {
208 if (line[len - 1] != ']') {
209 logthing(LOGTHING_CRITICAL,
210 "Malformed section header '%s' in "
211 "config file.", line);
214 if (len > sizeof(section)) {
215 logthing(LOGTHING_CRITICAL,
216 "Section header '%s' is too long in "
217 "config file.", line);
221 strncpy(section, &line[1], len);
222 } else if ((value = strchr(line, '=')) != NULL) {
226 /* We can have multiple backend: sections */
227 if (!strncmp(section, "backend:", 8)) {
228 backend = find_db_backend_config(
229 config.backends, §ion[8]);
230 if (backend == NULL) {
232 sizeof(struct onak_db_config));
233 backend->name = strdup(§ion[8]);
234 config.backends = lladd(config.backends,
238 if (!strcmp(name, "type")) {
239 backend->type = strdup(value);
240 } else if (!strcmp(name, "location")) {
241 backend->location = strdup(value);
242 } else if (!strcmp(name, "hostname")) {
243 backend->location = strdup(value);
244 } else if (!strcmp(name, "username")) {
245 backend->location = strdup(value);
246 } else if (!strcmp(name, "password")) {
247 backend->location = strdup(value);
250 #define MATCH(s, n) !strcmp(section, s) && !strcmp(name, n)
252 } else if (MATCH("main", "backend")) {
253 config.db_backend = strdup(value);
254 } else if (MATCH("main", "backends_dir")) {
255 config.backends_dir = strdup(value);
256 } else if (MATCH("main", "logfile")) {
257 config.logfile = strdup(value);
258 } else if (MATCH("main", "loglevel")) {
259 setlogthreshold(atoi(value));
260 } else if (MATCH("main", "use_keyd")) {
261 config.use_keyd = parsebool(value,
263 } else if (MATCH("main", "sock_dir")) {
264 config.sock_dir = strdup(value);
265 } else if (MATCH("main", "max_reply_keys")) {
266 config.maxkeys = atoi(value);
268 } else if (MATCH("mail", "maintainer_email")) {
269 config.adminemail = strdup(value);
270 } else if (MATCH("mail", "mail_dir")) {
271 config.mail_dir = strdup(value);
272 } else if (MATCH("mail", "mta")) {
273 config.mta = strdup(value);
274 } else if (MATCH("mail", "bin_dir")) {
275 config.bin_dir = strdup(value);
276 } else if (MATCH("mail", "this_site")) {
277 config.thissite = strdup(value);
278 } else if (MATCH("mail", "syncsite")) {
279 config.syncsites = lladd(config.syncsites,
281 /* [verification] section */
282 } else if (MATCH("verification", "check_sighash")) {
283 if (parsebool(value, config.clean_policies &
284 ONAK_CLEAN_CHECK_SIGHASH)) {
285 config.clean_policies |=
286 ONAK_CLEAN_CHECK_SIGHASH;
288 config.clean_policies &=
289 ~ONAK_CLEAN_CHECK_SIGHASH;
291 } else if (MATCH("verification", "check_packet_size")) {
292 if (parsebool(value, config.clean_policies &
293 ONAK_CLEAN_LARGE_PACKETS)) {
294 config.clean_policies |=
295 ONAK_CLEAN_LARGE_PACKETS;
297 config.clean_policies &=
298 ~ONAK_CLEAN_LARGE_PACKETS;
310 void readconfig(const char *configfile) {
316 struct onak_db_config *backend;
317 bool oldstyle = false;
323 * Try to find a config file to use. If one is explicitly provided,
324 * use that. Otherwise look in $XDG_CONFIG_HOME, $HOME and finally
325 * the build in configuration directory. We try an old style onak.conf
326 * first and then the new style onak.ini if that wasn't found - this
327 * is to prevent breaking existing installs on upgrade.
329 if (configfile == NULL) {
331 if ((dir = getenv("XDG_CONFIG_HOME")) != NULL) {
332 /* dir + / + onak.conf + NUL */
333 len = strlen(dir) + 1 + 9 + 1;
335 snprintf(conf, len, "%s/onak.conf", dir);
336 conffile = fopen(conf, "r");
337 if (conffile == NULL) {
338 /* Conveniently .ini is shorter than .conf */
339 snprintf(conf, len, "%s/onak.ini", dir);
340 conffile = fopen(conf, "r");
345 } else if ((dir = getenv("HOME")) != NULL) {
346 /* dir + /.config/onak.conf + NUL */
347 len = strlen(dir) + 18 + 1;
349 snprintf(conf, len, "%s/.config/onak.conf", dir);
350 conffile = fopen(conf, "r");
351 if (conffile == NULL) {
352 /* Conveniently .ini is shorter than .conf */
353 snprintf(conf, len, "%s/onak.ini", dir);
354 conffile = fopen(conf, "r");
360 if (conffile == NULL) {
361 conffile = fopen(CONFIGDIR "/onak.conf", "r");
362 if (conffile == NULL) {
363 conffile = fopen(CONFIGDIR "/onak.ini", "r");
370 * Explicitly provided config file; if the filename ends .conf
371 * assume it's old style.
373 len = strlen(configfile);
374 if (!strcmp(&configfile[len - 5], ".conf")) {
377 conffile = fopen(configfile, "r");
380 if (conffile != NULL) {
381 if (!fgets(curline, 1023, conffile)) {
382 logthing(LOGTHING_CRITICAL,
383 "Problem reading configuration file.");
389 /* Add a single DB configuration */
390 backend = calloc(1, sizeof(*backend));
391 config.backend = backend;
392 config.backends = lladd(NULL, backend);
395 while (!feof(conffile)) {
396 /* Strip any trailing white space */
397 for (i = strlen(curline) - 1;
398 i >= 0 && isspace(curline[i]);
403 /* Strip any leading white space */
405 while (curline[i] != 0 && isspace(curline[i])) {
410 res = parseoldconfigline(&curline[i]);
412 res = parseconfigline(&curline[i]);
415 logthing(LOGTHING_ERROR,
416 "Unknown config line: %s", curline);
419 if (!fgets(curline, 1023, conffile) &&
421 logthing(LOGTHING_CRITICAL,
422 "Problem reading configuration file.");
428 if (config.db_backend == NULL) {
429 logthing(LOGTHING_CRITICAL,
430 "No database backend configured.");
431 } else if (!oldstyle) {
432 config.backend = find_db_backend_config(
433 config.backends, config.db_backend);
434 if (config.backend == NULL) {
435 logthing(LOGTHING_NOTICE,
436 "Couldn't find configuration for %s "
437 "backend.", config.db_backend);
441 logthing(LOGTHING_NOTICE,
442 "Couldn't open config file; using defaults.");
446 void writeconfig(const char *configfile)
452 conffile = fopen(configfile, "w");
457 #define WRITE_IF_NOT_NULL(c, s) if (c != NULL) { \
458 fprintf(conffile, s "=%s\n", c); \
460 #define WRITE_BOOL(c, s) fprintf(conffile, s "=%s\n", s ? "true" : "false");
462 fprintf(conffile, "[main]\n");
463 WRITE_IF_NOT_NULL(config.backend->name, "backend");
464 WRITE_IF_NOT_NULL(config.backends_dir, "backends_dir");
465 WRITE_IF_NOT_NULL(config.logfile, "logfile");
466 fprintf(conffile, "loglevel=%d\n", getlogthreshold());
467 WRITE_BOOL(config.use_keyd, "use_keyd");
468 WRITE_IF_NOT_NULL(config.sock_dir, "sock_dir");
469 fprintf(conffile, "max_reply_keys=%d\n", config.maxkeys);
470 fprintf(conffile, "\n");
472 fprintf(conffile, "[verification]\n");
473 WRITE_BOOL(config.clean_policies & ONAK_CLEAN_CHECK_SIGHASH,
475 fprintf(conffile, "\n");
477 fprintf(conffile, "[mail]\n");
478 WRITE_IF_NOT_NULL(config.adminemail, "maintainer_email");
479 WRITE_IF_NOT_NULL(config.mail_dir, "mail_dir");
480 WRITE_IF_NOT_NULL(config.mta, "mta");
481 WRITE_IF_NOT_NULL(config.bin_dir, "bin_dir");
482 WRITE_IF_NOT_NULL(config.thissite, "this_site");
484 cur = config.syncsites;
485 while (cur != NULL) {
486 fprintf(conffile, "syncsite=%s\n", (char *) cur->object);
490 cur = config.backends;
491 while (cur != NULL) {
492 struct onak_db_config *backend =
493 (struct onak_db_config *) cur->object;
494 fprintf(conffile, "\n[backend:%s]\n", backend->name);
495 WRITE_IF_NOT_NULL(backend->type, "type");
496 WRITE_IF_NOT_NULL(backend->location, "location");
497 WRITE_IF_NOT_NULL(backend->hostname, "hostname");
498 WRITE_IF_NOT_NULL(backend->username, "username");
499 WRITE_IF_NOT_NULL(backend->password, "password");
508 void cleanupdbconfig(void *object)
510 struct onak_db_config *dbconfig = (struct onak_db_config *) object;
512 if (dbconfig->name != NULL) {
513 free(dbconfig->name);
514 dbconfig->name = NULL;
516 if (dbconfig->type != NULL) {
517 free(dbconfig->type);
518 dbconfig->type = NULL;
520 if (dbconfig->location != NULL) {
521 free(dbconfig->location);
522 dbconfig->location = NULL;
524 if (dbconfig->hostname != NULL) {
525 free(dbconfig->hostname);
526 dbconfig->hostname = NULL;
528 if (dbconfig->username != NULL) {
529 free(dbconfig->username);
530 dbconfig->username = NULL;
532 if (dbconfig->password != NULL) {
533 free(dbconfig->password);
534 dbconfig->password = NULL;
540 void cleanupconfig(void) {
541 /* Free any defined DB backend configuration first */
542 llfree(config.backends, cleanupdbconfig);
543 config.backends = NULL;
545 if (config.thissite != NULL) {
546 free(config.thissite);
547 config.thissite = NULL;
549 if (config.adminemail != NULL) {
550 free(config.adminemail);
551 config.adminemail = NULL;
553 if (config.mta != NULL) {
557 if (config.syncsites != NULL) {
558 llfree(config.syncsites, free);
559 config.syncsites = NULL;
561 if (config.logfile != NULL) {
562 free(config.logfile);
563 config.logfile = NULL;
565 if (config.db_backend != NULL) {
566 free(config.db_backend);
567 config.db_backend = NULL;
569 if (config.backends_dir != NULL) {
570 free(config.backends_dir);
571 config.backends_dir = NULL;
573 if (config.sock_dir != NULL) {
574 free(config.sock_dir);
575 config.sock_dir = NULL;
577 if (config.bin_dir != NULL) {
578 free(config.bin_dir);
579 config.bin_dir = NULL;
581 if (config.mail_dir != NULL) {
582 free(config.mail_dir);
583 config.mail_dir = NULL;