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, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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 .check_sighash = true,
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 config.check_sighash = parsebool(&line[9],
174 config.check_sighash);
183 * Parse a new style .ini config line, supporting [sections] and name=value
186 static bool parseconfigline(char *line)
188 /* Yes, this means we're not re-entrant. */
189 static char section[32] = "";
190 struct onak_db_config *backend;
194 if (line[0] == '#' || line[0] == ';' ||
197 * Comment line, ignore.
199 } else if (line[0] == '[') {
202 if (line[len - 1] != ']') {
203 logthing(LOGTHING_CRITICAL,
204 "Malformed section header '%s' in "
205 "config file.", line);
208 if (len > sizeof(section)) {
209 logthing(LOGTHING_CRITICAL,
210 "Section header '%s' is too long in "
211 "config file.", line);
215 strncpy(section, &line[1], len);
216 } else if ((value = strchr(line, '=')) != NULL) {
220 /* We can have multiple backend: sections */
221 if (!strncmp(section, "backend:", 8)) {
222 backend = find_db_backend_config(
223 config.backends, §ion[8]);
224 if (backend == NULL) {
226 sizeof(struct onak_db_config));
227 backend->name = strdup(§ion[8]);
228 config.backends = lladd(config.backends,
232 if (!strcmp(name, "type")) {
233 backend->type = strdup(value);
234 } else if (!strcmp(name, "location")) {
235 backend->location = strdup(value);
236 } else if (!strcmp(name, "hostname")) {
237 backend->location = strdup(value);
238 } else if (!strcmp(name, "username")) {
239 backend->location = strdup(value);
240 } else if (!strcmp(name, "password")) {
241 backend->location = strdup(value);
244 #define MATCH(s, n) !strcmp(section, s) && !strcmp(name, n)
246 } else if (MATCH("main", "backend")) {
247 config.db_backend = strdup(value);
248 } else if (MATCH("main", "backends_dir")) {
249 config.backends_dir = strdup(value);
250 } else if (MATCH("main", "logfile")) {
251 config.logfile = strdup(value);
252 } else if (MATCH("main", "loglevel")) {
253 setlogthreshold(atoi(value));
254 } else if (MATCH("main", "use_keyd")) {
255 config.use_keyd = parsebool(value,
257 } else if (MATCH("main", "sock_dir")) {
258 config.sock_dir = strdup(value);
259 } else if (MATCH("main", "max_reply_keys")) {
260 config.maxkeys = atoi(value);
262 } else if (MATCH("mail", "maintainer_email")) {
263 config.adminemail = strdup(value);
264 } else if (MATCH("mail", "mail_dir")) {
265 config.mail_dir = strdup(value);
266 } else if (MATCH("mail", "mta")) {
267 config.mta = strdup(value);
268 } else if (MATCH("mail", "bin_dir")) {
269 config.bin_dir = strdup(value);
270 } else if (MATCH("mail", "this_site")) {
271 config.thissite = strdup(value);
272 } else if (MATCH("mail", "syncsite")) {
273 config.syncsites = lladd(config.syncsites,
275 /* [verification] section */
276 } else if (MATCH("verification", "check_sighash")) {
277 config.check_sighash = parsebool(value,
278 config.check_sighash);
289 void readconfig(const char *configfile) {
295 struct onak_db_config *backend;
296 bool oldstyle = false;
302 * Try to find a config file to use. If one is explicitly provided,
303 * use that. Otherwise look in $XDG_CONFIG_HOME, $HOME and finally
304 * the build in configuration directory. We try an old style onak.conf
305 * first and then the new style onak.ini if that wasn't found - this
306 * is to prevent breaking existing installs on upgrade.
308 if (configfile == NULL) {
310 if ((dir = getenv("XDG_CONFIG_HOME")) != NULL) {
311 /* dir + / + onak.conf + NUL */
312 len = strlen(dir) + 1 + 9 + 1;
314 snprintf(conf, len, "%s/onak.conf", dir);
315 conffile = fopen(conf, "r");
316 if (conffile == NULL) {
317 /* Conveniently .ini is shorter than .conf */
318 snprintf(conf, len, "%s/onak.ini", dir);
319 conffile = fopen(conf, "r");
324 } else if ((dir = getenv("HOME")) != NULL) {
325 /* dir + /.config/onak.conf + NUL */
326 len = strlen(dir) + 18 + 1;
328 snprintf(conf, len, "%s/.config/onak.conf", dir);
329 conffile = fopen(conf, "r");
330 if (conffile == NULL) {
331 /* Conveniently .ini is shorter than .conf */
332 snprintf(conf, len, "%s/onak.ini", dir);
333 conffile = fopen(conf, "r");
339 if (conffile == NULL) {
340 conffile = fopen(CONFIGDIR "/onak.conf", "r");
341 if (conffile == NULL) {
342 conffile = fopen(CONFIGDIR "/onak.ini", "r");
349 * Explicitly provided config file; if the filename ends .conf
350 * assume it's old style.
352 len = strlen(configfile);
353 if (!strcmp(&configfile[len - 5], ".conf")) {
356 conffile = fopen(configfile, "r");
359 if (conffile != NULL) {
360 if (!fgets(curline, 1023, conffile)) {
361 logthing(LOGTHING_CRITICAL,
362 "Problem reading configuration file.");
368 /* Add a single DB configuration */
369 backend = calloc(1, sizeof(*backend));
370 config.backend = backend;
371 config.backends = lladd(NULL, backend);
374 while (!feof(conffile)) {
375 /* Strip any trailing white space */
376 for (i = strlen(curline) - 1;
377 i >= 0 && isspace(curline[i]);
382 /* Strip any leading white space */
384 while (curline[i] != 0 && isspace(curline[i])) {
389 res = parseoldconfigline(&curline[i]);
391 res = parseconfigline(&curline[i]);
394 logthing(LOGTHING_ERROR,
395 "Unknown config line: %s", curline);
398 if (!fgets(curline, 1023, conffile) &&
400 logthing(LOGTHING_CRITICAL,
401 "Problem reading configuration file.");
407 if (config.db_backend == NULL) {
408 logthing(LOGTHING_CRITICAL,
409 "No database backend configured.");
410 } else if (!oldstyle) {
411 config.backend = find_db_backend_config(
412 config.backends, config.db_backend);
413 if (config.backend == NULL) {
414 logthing(LOGTHING_NOTICE,
415 "Couldn't find configuration for %s "
416 "backend.", config.db_backend);
420 logthing(LOGTHING_NOTICE,
421 "Couldn't open config file; using defaults.");
425 void writeconfig(const char *configfile)
431 conffile = fopen(configfile, "w");
436 #define WRITE_IF_NOT_NULL(c, s) if (c != NULL) { \
437 fprintf(conffile, s "=%s\n", c); \
439 #define WRITE_BOOL(c, s) fprintf(conffile, s "=%s\n", s ? "true" : "false");
441 fprintf(conffile, "[main]\n");
442 WRITE_IF_NOT_NULL(config.backend->name, "backend");
443 WRITE_IF_NOT_NULL(config.backends_dir, "backends_dir");
444 WRITE_IF_NOT_NULL(config.logfile, "logfile");
445 fprintf(conffile, "loglevel=%d\n", getlogthreshold());
446 WRITE_BOOL(config.use_keyd, "use_keyd");
447 WRITE_IF_NOT_NULL(config.sock_dir, "sock_dir");
448 fprintf(conffile, "max_reply_keys=%d\n", config.maxkeys);
449 fprintf(conffile, "\n");
451 fprintf(conffile, "[verification]\n");
452 WRITE_BOOL(config.check_sighash, "check_sighash");
453 fprintf(conffile, "\n");
455 fprintf(conffile, "[mail]\n");
456 WRITE_IF_NOT_NULL(config.adminemail, "maintainer_email");
457 WRITE_IF_NOT_NULL(config.mail_dir, "mail_dir");
458 WRITE_IF_NOT_NULL(config.mta, "mta");
459 WRITE_IF_NOT_NULL(config.bin_dir, "bin_dir");
460 WRITE_IF_NOT_NULL(config.thissite, "this_site");
462 cur = config.syncsites;
463 while (cur != NULL) {
464 fprintf(conffile, "syncsite=%s\n", (char *) cur->object);
468 cur = config.backends;
469 while (cur != NULL) {
470 struct onak_db_config *backend =
471 (struct onak_db_config *) cur->object;
472 fprintf(conffile, "\n[backend:%s]\n", backend->name);
473 WRITE_IF_NOT_NULL(backend->type, "type");
474 WRITE_IF_NOT_NULL(backend->location, "location");
475 WRITE_IF_NOT_NULL(backend->hostname, "hostname");
476 WRITE_IF_NOT_NULL(backend->username, "username");
477 WRITE_IF_NOT_NULL(backend->password, "password");
486 void cleanupdbconfig(void *object)
488 struct onak_db_config *dbconfig = (struct onak_db_config *) object;
490 if (dbconfig->name != NULL) {
491 free(dbconfig->name);
492 dbconfig->name = NULL;
494 if (dbconfig->type != NULL) {
495 free(dbconfig->type);
496 dbconfig->type = NULL;
498 if (dbconfig->location != NULL) {
499 free(dbconfig->location);
500 dbconfig->location = NULL;
502 if (dbconfig->hostname != NULL) {
503 free(dbconfig->hostname);
504 dbconfig->hostname = NULL;
506 if (dbconfig->username != NULL) {
507 free(dbconfig->username);
508 dbconfig->username = NULL;
510 if (dbconfig->password != NULL) {
511 free(dbconfig->password);
512 dbconfig->password = NULL;
518 void cleanupconfig(void) {
519 /* Free any defined DB backend configuration first */
520 llfree(config.backends, cleanupdbconfig);
521 config.backends = NULL;
523 if (config.thissite != NULL) {
524 free(config.thissite);
525 config.thissite = NULL;
527 if (config.adminemail != NULL) {
528 free(config.adminemail);
529 config.adminemail = NULL;
531 if (config.mta != NULL) {
535 if (config.syncsites != NULL) {
536 llfree(config.syncsites, free);
537 config.syncsites = NULL;
539 if (config.logfile != NULL) {
540 free(config.logfile);
541 config.logfile = NULL;
543 if (config.db_backend != NULL) {
544 free(config.db_backend);
545 config.db_backend = NULL;
547 if (config.backends_dir != NULL) {
548 free(config.backends_dir);
549 config.backends_dir = NULL;
551 if (config.sock_dir != NULL) {
552 free(config.sock_dir);
553 config.sock_dir = NULL;
555 if (config.bin_dir != NULL) {
556 free(config.bin_dir);
557 config.bin_dir = NULL;
559 if (config.mail_dir != NULL) {
560 free(config.mail_dir);
561 config.mail_dir = NULL;