aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/configuration.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/configuration.c')
-rw-r--r--src/lib/util/configuration.c2576
1 files changed, 2576 insertions, 0 deletions
diff --git a/src/lib/util/configuration.c b/src/lib/util/configuration.c
new file mode 100644
index 000000000..17ba253ff
--- /dev/null
+++ b/src/lib/util/configuration.c
@@ -0,0 +1,2576 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2006, 2007, 2008, 2009, 2013, 2020, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file src/util/configuration.c
22 * @brief configuration management
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_configuration_lib.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "util", __VA_ARGS__)
31
32#define LOG_STRERROR_FILE(kind, syscall, filename) \
33 GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
34
35/**
36 * @brief configuration entry
37 */
38struct ConfigEntry
39{
40 /**
41 * This is a linked list.
42 */
43 struct ConfigEntry *next;
44
45 /**
46 * key for this entry
47 */
48 char *key;
49
50 /**
51 * current, committed value
52 */
53 char *val;
54
55 /**
56 * Diagnostics information for the filename.
57 */
58 char *hint_filename;
59
60 /**
61 * Diagnostics information for the line number.
62 */
63 unsigned int hint_lineno;
64};
65
66
67/**
68 * @brief configuration section
69 */
70struct ConfigSection
71{
72 /**
73 * This is a linked list.
74 */
75 struct ConfigSection *next;
76
77 /**
78 * entries in the section
79 */
80 struct ConfigEntry *entries;
81
82 /**
83 * name of the section
84 */
85 char *name;
86
87 /**
88 * Is the configuration section marked as inaccessible?
89 *
90 * This can happen if the section name is used in an \@inline-secret\@
91 * directive, but the referenced file can't be found or accessed.
92 */
93 bool inaccessible;
94
95 /**
96 * Diagnostics hint for the secret file.
97 */
98 char *hint_secret_filename;
99
100 /**
101 * Extra information regarding permissions of the secret file.
102 */
103 char *hint_secret_stat;
104
105 /**
106 * For secret sections: Where was this inlined from?
107 */
108 char *hint_inlined_from_filename;
109
110 /**
111 * For secret sections: Where was this inlined from?
112 */
113 unsigned int hint_inlined_from_line;
114};
115
116struct ConfigFile
117{
118 /**
119 * Source filename.
120 */
121 char *source_filename;
122
123 /**
124 * Level in the tree of loaded config files.
125 */
126 unsigned int level;
127
128 struct ConfigFile *prev;
129
130 struct ConfigFile *next;
131
132 /**
133 * Was this configuration file parsed via
134 * \@inline-secret\@?
135 */
136 char *hint_restrict_section;
137
138 /**
139 * Was this configuration file inaccessible?
140 */
141 bool hint_inaccessible;
142};
143
144
145/**
146 * @brief configuration data
147 */
148struct GNUNET_CONFIGURATION_Handle
149{
150 /**
151 * Configuration sections.
152 */
153 struct ConfigSection *sections;
154
155 /**
156 * Linked list of loaded files.
157 */
158 struct ConfigFile *loaded_files_head;
159
160 /**
161 * Linked list of loaded files.
162 */
163 struct ConfigFile *loaded_files_tail;
164
165 /**
166 * Current nesting level of file loading.
167 */
168 unsigned int current_nest_level;
169
170 /**
171 * Enable diagnostics.
172 */
173 bool diagnostics;
174
175 /**
176 * Modification indication since last save
177 * #GNUNET_NO if clean, #GNUNET_YES if dirty,
178 * #GNUNET_SYSERR on error (i.e. last save failed)
179 */
180 enum GNUNET_GenericReturnValue dirty;
181
182 /**
183 * Was the configuration ever loaded via GNUNET_CONFIGURATION_load?
184 */
185 bool load_called;
186
187 /**
188 * Name of the entry point configuration file.
189 */
190 char *main_filename;
191
192 /**
193 * When parsing into this configuration, and this value
194 * is non-NULL, only parse sections of the same name,
195 * and ban import statements.
196 */
197 const char *restrict_section;
198};
199
200
201/**
202 * Used for diffing a configuration object against
203 * the default one
204 */
205struct DiffHandle
206{
207 const struct GNUNET_CONFIGURATION_Handle *cfg_default;
208
209 struct GNUNET_CONFIGURATION_Handle *cfgDiff;
210};
211
212
213void
214GNUNET_CONFIGURATION_enable_diagnostics (struct
215 GNUNET_CONFIGURATION_Handle *cfg)
216{
217 cfg->diagnostics = true;
218}
219
220
221struct GNUNET_CONFIGURATION_Handle *
222GNUNET_CONFIGURATION_create ()
223{
224 struct GNUNET_CONFIGURATION_Handle *cfg;
225 char *p;
226
227 cfg = GNUNET_new (struct GNUNET_CONFIGURATION_Handle);
228 /* make certain values from the project data available
229 as PATHS */
230 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
231 if (NULL != p)
232 {
233 GNUNET_CONFIGURATION_set_value_string (cfg,
234 "PATHS",
235 "DATADIR",
236 p);
237 GNUNET_free (p);
238 }
239 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
240 if (NULL != p)
241 {
242 GNUNET_CONFIGURATION_set_value_string (cfg,
243 "PATHS",
244 "LIBDIR",
245 p);
246 GNUNET_free (p);
247 }
248 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
249 if (NULL != p)
250 {
251 GNUNET_CONFIGURATION_set_value_string (cfg,
252 "PATHS",
253 "BINDIR",
254 p);
255 GNUNET_free (p);
256 }
257 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_PREFIX);
258 if (NULL != p)
259 {
260 GNUNET_CONFIGURATION_set_value_string (cfg,
261 "PATHS",
262 "PREFIX",
263 p);
264 GNUNET_free (p);
265 }
266 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
267 if (NULL != p)
268 {
269 GNUNET_CONFIGURATION_set_value_string (cfg,
270 "PATHS",
271 "LOCALEDIR",
272 p);
273 GNUNET_free (p);
274 }
275 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_ICONDIR);
276 if (NULL != p)
277 {
278 GNUNET_CONFIGURATION_set_value_string (cfg,
279 "PATHS",
280 "ICONDIR",
281 p);
282 GNUNET_free (p);
283 }
284 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DOCDIR);
285 if (NULL != p)
286 {
287 GNUNET_CONFIGURATION_set_value_string (cfg,
288 "PATHS",
289 "DOCDIR",
290 p);
291 GNUNET_free (p);
292 }
293 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
294 if (NULL != p)
295 {
296 GNUNET_CONFIGURATION_set_value_string (cfg,
297 "PATHS",
298 "LIBEXECDIR",
299 p);
300 GNUNET_free (p);
301 }
302 return cfg;
303}
304
305
306void
307GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg)
308{
309 struct ConfigSection *sec;
310 struct ConfigFile *cf;
311
312 while (NULL != (sec = cfg->sections))
313 GNUNET_CONFIGURATION_remove_section (cfg, sec->name);
314 while (NULL != (cf = cfg->loaded_files_head))
315 {
316 GNUNET_free (cf->hint_restrict_section);
317 GNUNET_free (cf->source_filename);
318 GNUNET_CONTAINER_DLL_remove (cfg->loaded_files_head,
319 cfg->loaded_files_tail,
320 cf);
321 GNUNET_free (cf);
322 }
323 GNUNET_free (cfg->main_filename);
324 GNUNET_free (cfg);
325}
326
327
328enum GNUNET_GenericReturnValue
329GNUNET_CONFIGURATION_parse_and_run (const char *filename,
330 GNUNET_CONFIGURATION_Callback cb,
331 void *cb_cls)
332{
333 struct GNUNET_CONFIGURATION_Handle *cfg;
334 enum GNUNET_GenericReturnValue ret;
335
336 cfg = GNUNET_CONFIGURATION_create ();
337 if (GNUNET_OK !=
338 GNUNET_CONFIGURATION_load (cfg,
339 filename))
340 {
341 GNUNET_break (0);
342 GNUNET_CONFIGURATION_destroy (cfg);
343 return GNUNET_SYSERR;
344 }
345 ret = cb (cb_cls, cfg);
346 GNUNET_CONFIGURATION_destroy (cfg);
347 return ret;
348}
349
350
351/**
352 * Closure to collect_files_cb.
353 */
354struct CollectFilesContext
355{
356 /**
357 * Collected files from globbing.
358 */
359 char **files;
360
361 /**
362 * Size of the files array.
363 */
364 unsigned int files_length;
365};
366
367
368/**
369 * Function called with a filename.
370 *
371 * @param cls closure
372 * @param filename complete filename (absolute path)
373 * @return #GNUNET_OK to continue to iterate,
374 * #GNUNET_NO to stop iteration with no error,
375 * #GNUNET_SYSERR to abort iteration with error!
376 */
377static enum GNUNET_GenericReturnValue
378collect_files_cb (void *cls,
379 const char *filename)
380{
381 struct CollectFilesContext *igc = cls;
382
383 GNUNET_array_append (igc->files,
384 igc->files_length,
385 GNUNET_strdup (filename));
386 return GNUNET_OK;
387}
388
389
390/**
391 * Find a section entry from a configuration.
392 *
393 * @param cfg configuration to search in
394 * @param section name of the section to look for
395 * @return matching entry, NULL if not found
396 */
397static struct ConfigSection *
398find_section (const struct GNUNET_CONFIGURATION_Handle *cfg,
399 const char *section)
400{
401 struct ConfigSection *pos;
402
403 pos = cfg->sections;
404 while ((pos != NULL) && (0 != strcasecmp (section, pos->name)))
405 pos = pos->next;
406 return pos;
407}
408
409
410static int
411pstrcmp (const void *a, const void *b)
412{
413 return strcmp (*((const char **) a), *((const char **) b));
414}
415
416
417/**
418 * Handle an inline directive.
419 *
420 * @returns #GNUNET_SYSERR on error, #GNUNET_OK otherwise
421 */
422enum GNUNET_GenericReturnValue
423handle_inline (struct GNUNET_CONFIGURATION_Handle *cfg,
424 const char *path_or_glob,
425 bool path_is_glob,
426 const char *restrict_section,
427 const char *source_filename,
428 unsigned int source_lineno)
429{
430 char *inline_path = NULL;
431 struct GNUNET_CONFIGURATION_Handle *other_cfg = NULL;
432 struct CollectFilesContext igc = {
433 .files = NULL,
434 .files_length = 0,
435 };
436 enum GNUNET_GenericReturnValue fun_ret;
437 unsigned int old_nest_level = cfg->current_nest_level++;
438
439 /* We support the section restriction only for non-globs */
440 GNUNET_assert (! (path_is_glob && (NULL != restrict_section)));
441
442 if (NULL == source_filename)
443 {
444 LOG (GNUNET_ERROR_TYPE_DEBUG,
445 "Refusing to parse inline configurations, "
446 "not allowed without source filename!\n");
447 fun_ret = GNUNET_SYSERR;
448 goto cleanup;
449 }
450
451 if ('/' == *path_or_glob)
452 inline_path = GNUNET_strdup (path_or_glob);
453 else
454 {
455 /* We compute the canonical, absolute path first,
456 so that relative imports resolve properly with symlinked
457 config files. */
458 char *source_realpath;
459 char *endsep;
460
461 source_realpath = realpath (source_filename,
462 NULL);
463 if (NULL == source_realpath)
464 {
465 /* Couldn't even resolve path of base dir. */
466 GNUNET_break (0);
467 /* failed to parse included config */
468 fun_ret = GNUNET_SYSERR;
469 goto cleanup;
470 }
471 endsep = strrchr (source_realpath, '/');
472 GNUNET_assert (NULL != endsep);
473 *endsep = '\0';
474 GNUNET_asprintf (&inline_path,
475 "%s/%s",
476 source_realpath,
477 path_or_glob);
478 free (source_realpath);
479 }
480
481 if (path_is_glob)
482 {
483 int nret;
484
485 LOG (GNUNET_ERROR_TYPE_DEBUG,
486 "processing config glob '%s'\n",
487 inline_path);
488
489 nret = GNUNET_DISK_glob (inline_path, collect_files_cb, &igc);
490 if (-1 == nret)
491 {
492 fun_ret = GNUNET_SYSERR;
493 goto cleanup;
494 }
495 GNUNET_assert (nret == igc.files_length);
496 qsort (igc.files, igc.files_length, sizeof (char *), pstrcmp);
497 for (int i = 0; i < nret; i++)
498 {
499 if (GNUNET_OK !=
500 GNUNET_CONFIGURATION_parse (cfg,
501 igc.files[i]))
502 {
503 fun_ret = GNUNET_SYSERR;
504 goto cleanup;
505 }
506 }
507 fun_ret = GNUNET_OK;
508 }
509 else if (NULL != restrict_section)
510 {
511 enum GNUNET_GenericReturnValue inner_ret;
512 struct ConfigSection *cs;
513 struct ConfigFile *cf = GNUNET_new (struct ConfigFile);
514
515 inner_ret = GNUNET_DISK_file_test_read (inline_path);
516
517 cs = find_section (cfg, restrict_section);
518
519 if (NULL == cs)
520 {
521 cs = GNUNET_new (struct ConfigSection);
522 cs->name = GNUNET_strdup (restrict_section);
523 cs->next = cfg->sections;
524 cfg->sections = cs;
525 cs->entries = NULL;
526 }
527 if (cfg->diagnostics)
528 {
529 char *sfn = GNUNET_STRINGS_filename_expand (inline_path);
530 struct stat istat;
531
532 cs->hint_secret_filename = sfn;
533 if (0 == stat (sfn, &istat))
534 {
535 struct passwd *pw = getpwuid (istat.st_uid);
536 struct group *gr = getgrgid (istat.st_gid);
537 char *pwname = (NULL == pw) ? "<unknown>" : pw->pw_name;
538 char *grname = (NULL == gr) ? "<unknown>" : gr->gr_name;
539
540 GNUNET_asprintf (&cs->hint_secret_stat,
541 "%s:%s %o",
542 pwname,
543 grname,
544 istat.st_mode);
545 }
546 else
547 {
548 cs->hint_secret_stat = GNUNET_strdup ("<can't stat file>");
549 }
550 if (source_filename)
551 {
552 /* Possible that this secret section has been inlined before */
553 GNUNET_free (cs->hint_inlined_from_filename);
554 cs->hint_inlined_from_filename = GNUNET_strdup (source_filename);
555 cs->hint_inlined_from_line = source_lineno;
556 }
557 }
558
559 /* Put file in the load list for diagnostics, even if we can't access it. */
560 {
561 cf->level = cfg->current_nest_level;
562 cf->source_filename = GNUNET_strdup (inline_path);
563 cf->hint_restrict_section = GNUNET_strdup (restrict_section);
564 GNUNET_CONTAINER_DLL_insert_tail (cfg->loaded_files_head,
565 cfg->loaded_files_tail,
566 cf);
567 }
568
569 if (GNUNET_OK != inner_ret)
570 {
571 cs->inaccessible = true;
572 cf->hint_inaccessible = true;
573 /* File can't be accessed, but that's okay. */
574 fun_ret = GNUNET_OK;
575 goto cleanup;
576 }
577
578 other_cfg = GNUNET_CONFIGURATION_create ();
579 other_cfg->restrict_section = restrict_section;
580 inner_ret = GNUNET_CONFIGURATION_parse (other_cfg,
581 inline_path);
582 if (GNUNET_OK != inner_ret)
583 {
584 cf->hint_inaccessible = true;
585 fun_ret = inner_ret;
586 goto cleanup;
587 }
588
589 cs = find_section (other_cfg, restrict_section);
590 if (NULL == cs)
591 {
592 LOG (GNUNET_ERROR_TYPE_INFO,
593 "Configuration file '%s' loaded with @inline-secret@ "
594 "does not contain section '%s'.\n",
595 inline_path,
596 restrict_section);
597 /* Inlined onfiguration is accessible but doesn't contain any values.
598 We treat this as if the inlined section was empty, and do not
599 consider it an error. */
600 fun_ret = GNUNET_OK;
601 goto cleanup;
602 }
603 for (struct ConfigEntry *ce = cs->entries;
604 NULL != ce;
605 ce = ce->next)
606 GNUNET_CONFIGURATION_set_value_string (cfg,
607 restrict_section,
608 ce->key,
609 ce->val);
610 fun_ret = GNUNET_OK;
611 }
612 else if (GNUNET_OK !=
613 GNUNET_CONFIGURATION_parse (cfg,
614 inline_path))
615 {
616 fun_ret = GNUNET_SYSERR;
617 goto cleanup;
618 }
619 else
620 {
621 fun_ret = GNUNET_OK;
622 }
623cleanup:
624 cfg->current_nest_level = old_nest_level;
625 if (NULL != other_cfg)
626 GNUNET_CONFIGURATION_destroy (other_cfg);
627 GNUNET_free (inline_path);
628 if (igc.files_length > 0)
629 {
630 for (size_t i = 0; i < igc.files_length; i++)
631 GNUNET_free (igc.files[i]);
632 GNUNET_array_grow (igc.files, igc.files_length, 0);
633 }
634 return fun_ret;
635}
636
637
638/**
639 * Find an entry from a configuration.
640 *
641 * @param cfg handle to the configuration
642 * @param section section the option is in
643 * @param key the option
644 * @return matching entry, NULL if not found
645 */
646static struct ConfigEntry *
647find_entry (const struct GNUNET_CONFIGURATION_Handle *cfg,
648 const char *section,
649 const char *key)
650{
651 struct ConfigSection *sec;
652 struct ConfigEntry *pos;
653
654 if (NULL == (sec = find_section (cfg, section)))
655 return NULL;
656 if (sec->inaccessible)
657 {
658 LOG (GNUNET_ERROR_TYPE_WARNING,
659 "Section '%s' is marked as inaccessible, because the configuration "
660 " file that contains the section can't be read. Attempts to use "
661 "option '%s' will fail.\n",
662 section,
663 key);
664 return NULL;
665 }
666 pos = sec->entries;
667 while ((pos != NULL) && (0 != strcasecmp (key, pos->key)))
668 pos = pos->next;
669 return pos;
670}
671
672
673/**
674 * Set a configuration hint.
675 *
676 * @param cfg configuration handle
677 * @param section section
678 * @param option config option
679 * @param hint_filename
680 * @param hint_line
681 */
682static void
683set_entry_hint (struct GNUNET_CONFIGURATION_Handle *cfg,
684 const char *section,
685 const char *option,
686 const char *hint_filename,
687 unsigned int hint_line)
688{
689 struct ConfigEntry *e = find_entry (cfg, section, option);
690 if (! cfg->diagnostics)
691 return;
692 if (! e)
693 return;
694 e->hint_filename = GNUNET_strdup (hint_filename);
695 e->hint_lineno = hint_line;
696}
697
698
699enum GNUNET_GenericReturnValue
700GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg,
701 const char *mem,
702 size_t size,
703 const char *source_filename)
704{
705 size_t line_size;
706 unsigned int nr;
707 size_t r_bytes;
708 size_t to_read;
709 enum GNUNET_GenericReturnValue ret;
710 char *section;
711 char *eq;
712 char *tag;
713 char *value;
714 char *line_orig = NULL;
715
716 ret = GNUNET_OK;
717 section = NULL;
718 nr = 0;
719 r_bytes = 0;
720 while (r_bytes < size)
721 {
722 char *pos;
723 char *line;
724 bool emptyline;
725
726 GNUNET_free (line_orig);
727 /* fgets-like behaviour on buffer */
728 to_read = size - r_bytes;
729 pos = memchr (&mem[r_bytes], '\n', to_read);
730 if (NULL == pos)
731 {
732 line_orig = GNUNET_strndup (&mem[r_bytes],
733 line_size = to_read);
734 r_bytes += line_size;
735 }
736 else
737 {
738 line_orig = GNUNET_strndup (&mem[r_bytes],
739 line_size = (pos - &mem[r_bytes]));
740 r_bytes += line_size + 1;
741 }
742 line = line_orig;
743 /* increment line number */
744 nr++;
745 /* tabs and '\r' are whitespace */
746 emptyline = GNUNET_YES;
747 for (size_t i = 0; i < line_size; i++)
748 {
749 if (line[i] == '\t')
750 line[i] = ' ';
751 if (line[i] == '\r')
752 line[i] = ' ';
753 if (' ' != line[i])
754 emptyline = GNUNET_NO;
755 }
756 /* ignore empty lines */
757 if (GNUNET_YES == emptyline)
758 continue;
759
760 /* remove tailing whitespace */
761 for (size_t i = line_size - 1;
762 (i >= 1) && (isspace ((unsigned char) line[i]));
763 i--)
764 line[i] = '\0';
765
766 /* remove leading whitespace */
767 for (; line[0] != '\0' && (isspace ((unsigned char) line[0])); line++)
768 ;
769
770 /* ignore comments */
771 if ( ('#' == line[0]) ||
772 ('%' == line[0]) )
773 continue;
774
775 /* Handle special directives. */
776 if ('@' == line[0])
777 {
778 char *end = strchr (line + 1, '@');
779 char *directive;
780 enum GNUNET_GenericReturnValue directive_ret;
781
782 if (NULL != cfg->restrict_section)
783 {
784 LOG (GNUNET_ERROR_TYPE_WARNING,
785 "Illegal directive in line %u (parsing restricted section %s)\n",
786 nr,
787 cfg->restrict_section);
788 ret = GNUNET_SYSERR;
789 break;
790 }
791
792 if (NULL == end)
793 {
794 LOG (GNUNET_ERROR_TYPE_WARNING,
795 "Bad directive in line %u\n",
796 nr);
797 ret = GNUNET_SYSERR;
798 break;
799 }
800 *end = '\0';
801 directive = line + 1;
802
803 if (0 == strcasecmp (directive,
804 "INLINE"))
805 {
806 const char *path = end + 1;
807
808 /* Skip space before path */
809 for (; isspace (*path); path++)
810 ;
811
812 directive_ret = handle_inline (cfg,
813 path,
814 false,
815 NULL,
816 source_filename,
817 nr);
818 }
819 else if (0 == strcasecmp (directive,
820 "INLINE-MATCHING"))
821 {
822 const char *path = end + 1;
823
824 /* Skip space before path */
825 for (; isspace (*path); path++)
826 ;
827
828 directive_ret = handle_inline (cfg,
829 path,
830 true,
831 NULL,
832 source_filename,
833 nr);
834 }
835 else if (0 == strcasecmp (directive,
836 "INLINE-SECRET"))
837 {
838 char *secname = end + 1;
839 char *secname_end;
840 const char *path;
841
842 /* Skip space before secname */
843 for (; isspace (*secname); secname++)
844 ;
845
846 secname_end = strchr (secname, ' ');
847
848 if (NULL == secname_end)
849 {
850 LOG (GNUNET_ERROR_TYPE_WARNING,
851 "Bad inline-secret directive in line %u\n",
852 nr);
853 ret = GNUNET_SYSERR;
854 break;
855 }
856 *secname_end = '\0';
857 path = secname_end + 1;
858
859 /* Skip space before path */
860 for (; isspace (*path); path++)
861 ;
862
863 directive_ret = handle_inline (cfg,
864 path,
865 false,
866 secname,
867 source_filename,
868 nr);
869 }
870 else
871 {
872 LOG (GNUNET_ERROR_TYPE_WARNING,
873 "Unknown or malformed directive '%s' in line %u\n",
874 directive,
875 nr);
876 ret = GNUNET_SYSERR;
877 break;
878 }
879 if (GNUNET_OK != directive_ret)
880 {
881 ret = directive_ret;
882 break;
883 }
884 continue;
885 }
886 if ( ('[' == line[0]) &&
887 (']' == line[line_size - 1]) )
888 {
889 /* [value] */
890 line[line_size - 1] = '\0';
891 value = &line[1];
892 GNUNET_free (section);
893 section = GNUNET_strdup (value);
894 continue;
895 }
896 if (NULL != (eq = strchr (line, '=')))
897 {
898 size_t i;
899
900 if (NULL == section)
901 {
902 LOG (GNUNET_ERROR_TYPE_WARNING,
903 "Syntax error while deserializing in line %u (option without section)\n",
904 nr);
905 ret = GNUNET_SYSERR;
906 break;
907 }
908
909 /* tag = value */
910 tag = GNUNET_strndup (line, eq - line);
911 /* remove tailing whitespace */
912 for (i = strlen (tag) - 1;
913 (i >= 1) && (isspace ((unsigned char) tag[i]));
914 i--)
915 tag[i] = '\0';
916
917 /* Strip whitespace */
918 value = eq + 1;
919 while (isspace ((unsigned char) value[0]))
920 value++;
921 for (i = strlen (value) - 1;
922 (i >= 1) && (isspace ((unsigned char) value[i]));
923 i--)
924 value[i] = '\0';
925
926 /* remove quotes */
927 i = 0;
928 if ( ('"' == value[0]) &&
929 ('"' == value[strlen (value) - 1]) )
930 {
931 value[strlen (value) - 1] = '\0';
932 value++;
933 }
934 GNUNET_CONFIGURATION_set_value_string (cfg,
935 section,
936 tag,
937 &value[i]);
938 if (cfg->diagnostics)
939 {
940 set_entry_hint (cfg,
941 section,
942 tag,
943 source_filename
944 ? source_filename
945 : "<input>",
946 nr);
947 }
948 GNUNET_free (tag);
949 continue;
950 }
951 /* parse error */
952 LOG (GNUNET_ERROR_TYPE_WARNING,
953 "Syntax error while deserializing in line %u\n",
954 nr);
955 ret = GNUNET_SYSERR;
956 break;
957 }
958 GNUNET_free (line_orig);
959 GNUNET_free (section);
960 GNUNET_assert ( (GNUNET_OK != ret) ||
961 (r_bytes == size) );
962 return ret;
963}
964
965
966enum GNUNET_GenericReturnValue
967GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg,
968 const char *filename)
969{
970 uint64_t fs64;
971 size_t fs;
972 char *fn;
973 char *mem;
974 int dirty;
975 enum GNUNET_GenericReturnValue ret;
976 ssize_t sret;
977
978 fn = GNUNET_STRINGS_filename_expand (filename);
979 LOG (GNUNET_ERROR_TYPE_DEBUG, "Asked to parse config file `%s'\n", fn);
980 if (NULL == fn)
981 return GNUNET_SYSERR;
982
983
984 /* Check for cycles */
985 {
986 unsigned int lvl = cfg->current_nest_level;
987 struct ConfigFile *cf = cfg->loaded_files_tail;
988 struct ConfigFile *parent = NULL;
989
990
991 for (; NULL != cf; parent = cf, cf = cf->prev)
992 {
993 /* Check parents based on level, skipping children of siblings. */
994 if (cf->level >= lvl)
995 continue;
996 lvl = cf->level;
997 if ( (NULL == cf->source_filename) || (NULL == filename))
998 continue;
999 if (0 == strcmp (cf->source_filename, filename))
1000 {
1001 if (NULL == parent)
1002 {
1003 LOG (GNUNET_ERROR_TYPE_ERROR,
1004 "Forbidden direct cyclic configuration import (%s -> %s)\n",
1005 cf->source_filename,
1006 filename);
1007 }
1008 else
1009 LOG (GNUNET_ERROR_TYPE_ERROR,
1010 "Forbidden indirect cyclic configuration import (%s -> ... -> %s -> %s)\n",
1011 cf->source_filename,
1012 parent->source_filename,
1013 filename);
1014 GNUNET_free (fn);
1015 return GNUNET_SYSERR;
1016 }
1017 }
1018
1019 }
1020
1021 /* Keep track of loaded files.*/
1022 {
1023 struct ConfigFile *cf = GNUNET_new (struct ConfigFile);
1024
1025 cf->level = cfg->current_nest_level;
1026 cf->source_filename = GNUNET_strdup (filename ? filename : "<input>");
1027 GNUNET_CONTAINER_DLL_insert_tail (cfg->loaded_files_head,
1028 cfg->loaded_files_tail,
1029 cf);
1030 }
1031
1032 dirty = cfg->dirty; /* back up value! */
1033 if (GNUNET_SYSERR ==
1034 GNUNET_DISK_file_size (fn,
1035 &fs64,
1036 GNUNET_YES,
1037 GNUNET_YES))
1038 {
1039 LOG (GNUNET_ERROR_TYPE_WARNING,
1040 "Error while determining the file size of `%s'\n",
1041 fn);
1042 GNUNET_free (fn);
1043 return GNUNET_SYSERR;
1044 }
1045 if (fs64 > SIZE_MAX)
1046 {
1047 GNUNET_break (0); /* File size is more than the heap size */
1048 GNUNET_free (fn);
1049 return GNUNET_SYSERR;
1050 }
1051 fs = fs64;
1052 mem = GNUNET_malloc (fs);
1053 sret = GNUNET_DISK_fn_read (fn, mem, fs);
1054 if ((sret < 0) || (fs != (size_t) sret))
1055 {
1056 LOG (GNUNET_ERROR_TYPE_WARNING,
1057 "Error while reading file `%s'\n",
1058 fn);
1059 GNUNET_free (fn);
1060 GNUNET_free (mem);
1061 return GNUNET_SYSERR;
1062 }
1063 LOG (GNUNET_ERROR_TYPE_DEBUG,
1064 "Deserializing contents of file `%s'\n",
1065 fn);
1066 ret = GNUNET_CONFIGURATION_deserialize (cfg,
1067 mem,
1068 fs,
1069 fn);
1070 if (GNUNET_SYSERR == ret)
1071 {
1072 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1073 _ ("Failed to parse configuration file `%s'\n"),
1074 fn);
1075 }
1076 GNUNET_free (fn);
1077 GNUNET_free (mem);
1078 /* restore dirty flag - anything we set in the meantime
1079 * came from disk */
1080 cfg->dirty = dirty;
1081 return ret;
1082}
1083
1084
1085enum GNUNET_GenericReturnValue
1086GNUNET_CONFIGURATION_is_dirty (const struct GNUNET_CONFIGURATION_Handle *cfg)
1087{
1088 return cfg->dirty;
1089}
1090
1091
1092/**
1093 * Should we skip this configuration entry when serializing?
1094 *
1095 * @param sec section name
1096 * @param key key
1097 * @return true if we should skip it
1098 */
1099static bool
1100do_skip (const char *sec,
1101 const char *key)
1102{
1103 if (0 != strcasecmp ("PATHS",
1104 sec))
1105 return false;
1106 return ( (0 == strcasecmp ("DATADIR",
1107 key)) ||
1108 (0 == strcasecmp ("LIBDIR",
1109 key)) ||
1110 (0 == strcasecmp ("BINDIR",
1111 key)) ||
1112 (0 == strcasecmp ("PREFIX",
1113 key)) ||
1114 (0 == strcasecmp ("LOCALEDIR",
1115 key)) ||
1116 (0 == strcasecmp ("ICONDIR",
1117 key)) ||
1118 (0 == strcasecmp ("DOCDIR",
1119 key)) ||
1120 (0 == strcasecmp ("DEFAULTCONFIG",
1121 key)) ||
1122 (0 == strcasecmp ("LIBEXECDIR",
1123 key)) );
1124}
1125
1126
1127char *
1128GNUNET_CONFIGURATION_serialize (const struct GNUNET_CONFIGURATION_Handle *cfg,
1129 size_t *size)
1130{
1131 char *mem;
1132 char *cbuf;
1133 char *val;
1134 char *pos;
1135 size_t m_size;
1136 size_t c_size;
1137
1138 /* Pass1 : calculate the buffer size required */
1139 m_size = 0;
1140 for (struct ConfigSection *sec = cfg->sections;
1141 NULL != sec;
1142 sec = sec->next)
1143 {
1144 if (sec->inaccessible)
1145 continue;
1146 /* For each section we need to add 3 characters: {'[',']','\n'} */
1147 m_size += strlen (sec->name) + 3;
1148 for (struct ConfigEntry *ent = sec->entries;
1149 NULL != ent;
1150 ent = ent->next)
1151 {
1152 if (do_skip (sec->name,
1153 ent->key))
1154 continue;
1155 if (NULL != ent->val)
1156 {
1157 /* if val has any '\n' then they occupy +1 character as '\n'->'\\','n' */
1158 pos = ent->val;
1159 while (NULL != (pos = strstr (pos, "\n")))
1160 {
1161 m_size++;
1162 pos++;
1163 }
1164 /* For each key = value pair we need to add 4 characters (2
1165 spaces and 1 equal-to character and 1 new line) */
1166 m_size += strlen (ent->key) + strlen (ent->val) + 4;
1167 }
1168 }
1169 /* A new line after section end */
1170 m_size++;
1171 }
1172
1173 /* Pass2: Allocate memory and write the configuration to it */
1174 mem = GNUNET_malloc (m_size);
1175 c_size = 0;
1176 *size = c_size;
1177 for (struct ConfigSection *sec = cfg->sections;
1178 NULL != sec;
1179 sec = sec->next)
1180 {
1181 int len;
1182
1183 len = GNUNET_asprintf (&cbuf,
1184 "[%s]\n",
1185 sec->name);
1186 GNUNET_assert (0 < len);
1187 GNUNET_memcpy (mem + c_size,
1188 cbuf,
1189 len);
1190 c_size += len;
1191 GNUNET_free (cbuf);
1192 for (struct ConfigEntry *ent = sec->entries;
1193 NULL != ent;
1194 ent = ent->next)
1195 {
1196 if (do_skip (sec->name,
1197 ent->key))
1198 continue;
1199 if (NULL != ent->val)
1200 {
1201 val = GNUNET_malloc (strlen (ent->val) * 2 + 1);
1202 strcpy (val, ent->val);
1203 while (NULL != (pos = strstr (val, "\n")))
1204 {
1205 memmove (&pos[2], &pos[1], strlen (&pos[1]));
1206 pos[0] = '\\';
1207 pos[1] = 'n';
1208 }
1209 len = GNUNET_asprintf (&cbuf, "%s = %s\n", ent->key, val);
1210 GNUNET_free (val);
1211 GNUNET_memcpy (mem + c_size, cbuf, len);
1212 c_size += len;
1213 GNUNET_free (cbuf);
1214 }
1215 }
1216 GNUNET_memcpy (mem + c_size, "\n", 1);
1217 c_size++;
1218 }
1219 GNUNET_assert (c_size == m_size);
1220 *size = c_size;
1221 return mem;
1222}
1223
1224
1225char *
1226GNUNET_CONFIGURATION_serialize_diagnostics (const struct
1227 GNUNET_CONFIGURATION_Handle *cfg)
1228{
1229 struct GNUNET_Buffer buf = { 0 };
1230
1231 GNUNET_buffer_write_fstr (&buf,
1232 "#\n# Configuration file diagnostics\n#\n");
1233 GNUNET_buffer_write_fstr (&buf,
1234 "# Entry point: %s\n",
1235 cfg->main_filename ? cfg->main_filename :
1236 "<none>");
1237 GNUNET_buffer_write_fstr (&buf,
1238 "#\n# Files Loaded:\n");
1239
1240 for (struct ConfigFile *cfil = cfg->loaded_files_head;
1241 NULL != cfil;
1242 cfil = cfil->next)
1243 {
1244 GNUNET_buffer_write_fstr (&buf,
1245 "# ");
1246 for (unsigned int i = 0; i < cfil->level; i++)
1247 GNUNET_buffer_write_fstr (&buf,
1248 "+");
1249 if (0 != cfil->level)
1250 GNUNET_buffer_write_fstr (&buf,
1251 " ");
1252
1253 GNUNET_buffer_write_fstr (&buf,
1254 "%s",
1255 cfil->source_filename);
1256
1257 if (NULL != cfil->hint_restrict_section)
1258 GNUNET_buffer_write_fstr (&buf,
1259 " (%s secret section %s)",
1260 cfil->hint_inaccessible
1261 ? "inaccessible"
1262 : "loaded",
1263 cfil->hint_restrict_section);
1264
1265 GNUNET_buffer_write_str (&buf,
1266 "\n");
1267 }
1268
1269 GNUNET_buffer_write_fstr (&buf,
1270 "#\n\n");
1271
1272 for (struct ConfigSection *sec = cfg->sections;
1273 NULL != sec;
1274 sec = sec->next)
1275 {
1276 if (sec->hint_secret_filename)
1277 GNUNET_buffer_write_fstr (&buf,
1278 "# secret section from %s\n# secret file stat %s\n",
1279 sec->hint_secret_filename,
1280 sec->hint_secret_stat);
1281 if (sec->hint_inlined_from_filename)
1282 {
1283 GNUNET_buffer_write_fstr (&buf,
1284 "# inlined from %s:%u\n",
1285 sec->hint_inlined_from_filename,
1286 sec->hint_inlined_from_line);
1287 }
1288 GNUNET_buffer_write_fstr (&buf,
1289 "[%s]\n\n",
1290 sec->name);
1291 if (sec->inaccessible)
1292 {
1293 GNUNET_buffer_write_fstr (&buf,
1294 "# <section contents inaccessible>\n\n\n");
1295 continue;
1296 }
1297 for (struct ConfigEntry *ent = sec->entries;
1298 NULL != ent;
1299 ent = ent->next)
1300 {
1301 if (do_skip (sec->name,
1302 ent->key))
1303 continue;
1304 if (NULL != ent->val)
1305 {
1306 char *pos;
1307 char *val = GNUNET_malloc (strlen (ent->val) * 2 + 1);
1308 strcpy (val, ent->val);
1309 while (NULL != (pos = strstr (val, "\n")))
1310 {
1311 memmove (&pos[2], &pos[1], strlen (&pos[1]));
1312 pos[0] = '\\';
1313 pos[1] = 'n';
1314 }
1315 if (NULL != ent->hint_filename)
1316 {
1317 GNUNET_buffer_write_fstr (&buf,
1318 "# %s:%u\n",
1319 ent->hint_filename,
1320 ent->hint_lineno);
1321 }
1322 GNUNET_buffer_write_fstr (&buf,
1323 "%s = %s\n",
1324 ent->key,
1325 val);
1326 GNUNET_free (val);
1327 }
1328 GNUNET_buffer_write_str (&buf, "\n");
1329 }
1330 GNUNET_buffer_write_str (&buf, "\n");
1331 }
1332 return GNUNET_buffer_reap_str (&buf);
1333}
1334
1335
1336enum GNUNET_GenericReturnValue
1337GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg,
1338 const char *filename)
1339{
1340 char *fn;
1341 char *cfg_buf;
1342 size_t size;
1343
1344 fn = GNUNET_STRINGS_filename_expand (filename);
1345 if (fn == NULL)
1346 return GNUNET_SYSERR;
1347 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
1348 {
1349 GNUNET_free (fn);
1350 return GNUNET_SYSERR;
1351 }
1352 cfg_buf = GNUNET_CONFIGURATION_serialize (cfg,
1353 &size);
1354 {
1355 struct GNUNET_DISK_FileHandle *h;
1356
1357 h = GNUNET_DISK_file_open (fn,
1358 GNUNET_DISK_OPEN_WRITE
1359 | GNUNET_DISK_OPEN_TRUNCATE
1360 | GNUNET_DISK_OPEN_CREATE,
1361 GNUNET_DISK_PERM_USER_READ
1362 | GNUNET_DISK_PERM_USER_WRITE
1363 | GNUNET_DISK_PERM_GROUP_READ
1364 | GNUNET_DISK_PERM_GROUP_WRITE);
1365 if (NULL == h)
1366 {
1367 GNUNET_free (fn);
1368 GNUNET_free (cfg_buf);
1369 return GNUNET_SYSERR;
1370 }
1371 if (((ssize_t) size) !=
1372 GNUNET_DISK_file_write (h,
1373 cfg_buf,
1374 size))
1375 {
1376 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1377 "write",
1378 fn);
1379 GNUNET_DISK_file_close (h);
1380 (void) GNUNET_DISK_directory_remove (fn);
1381 GNUNET_free (fn);
1382 GNUNET_free (cfg_buf);
1383 cfg->dirty = GNUNET_SYSERR; /* last write failed */
1384 return GNUNET_SYSERR;
1385 }
1386 GNUNET_assert (GNUNET_OK ==
1387 GNUNET_DISK_file_close (h));
1388 }
1389 GNUNET_free (fn);
1390 GNUNET_free (cfg_buf);
1391 cfg->dirty = GNUNET_NO; /* last write succeeded */
1392 return GNUNET_OK;
1393}
1394
1395
1396void
1397GNUNET_CONFIGURATION_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
1398 GNUNET_CONFIGURATION_Iterator iter,
1399 void *iter_cls)
1400{
1401 for (struct ConfigSection *spos = cfg->sections;
1402 NULL != spos;
1403 spos = spos->next)
1404 for (struct ConfigEntry *epos = spos->entries;
1405 NULL != epos;
1406 epos = epos->next)
1407 if (NULL != epos->val)
1408 iter (iter_cls,
1409 spos->name,
1410 epos->key,
1411 epos->val);
1412}
1413
1414
1415void
1416GNUNET_CONFIGURATION_iterate_section_values (
1417 const struct GNUNET_CONFIGURATION_Handle *cfg,
1418 const char *section,
1419 GNUNET_CONFIGURATION_Iterator iter,
1420 void *iter_cls)
1421{
1422 struct ConfigSection *spos;
1423 struct ConfigEntry *epos;
1424
1425 spos = cfg->sections;
1426 while ((spos != NULL) && (0 != strcasecmp (spos->name, section)))
1427 spos = spos->next;
1428 if (NULL == spos)
1429 return;
1430 if (spos->inaccessible)
1431 {
1432 LOG (GNUNET_ERROR_TYPE_WARNING,
1433 "Section '%s' is marked as inaccessible, because the configuration "
1434 " file that contains the section can't be read.\n",
1435 section);
1436 return;
1437 }
1438 for (epos = spos->entries; NULL != epos; epos = epos->next)
1439 if (NULL != epos->val)
1440 iter (iter_cls, spos->name, epos->key, epos->val);
1441}
1442
1443
1444void
1445GNUNET_CONFIGURATION_iterate_sections (
1446 const struct GNUNET_CONFIGURATION_Handle *cfg,
1447 GNUNET_CONFIGURATION_SectionIterator iter,
1448 void *iter_cls)
1449{
1450 struct ConfigSection *spos;
1451 struct ConfigSection *next;
1452
1453 next = cfg->sections;
1454 while (next != NULL)
1455 {
1456 spos = next;
1457 next = spos->next;
1458 iter (iter_cls, spos->name);
1459 }
1460}
1461
1462
1463void
1464GNUNET_CONFIGURATION_remove_section (struct GNUNET_CONFIGURATION_Handle *cfg,
1465 const char *section)
1466{
1467 struct ConfigSection *spos;
1468 struct ConfigSection *prev;
1469 struct ConfigEntry *ent;
1470
1471 prev = NULL;
1472 spos = cfg->sections;
1473 while (NULL != spos)
1474 {
1475 if (0 == strcasecmp (section, spos->name))
1476 {
1477 if (NULL == prev)
1478 cfg->sections = spos->next;
1479 else
1480 prev->next = spos->next;
1481 while (NULL != (ent = spos->entries))
1482 {
1483 spos->entries = ent->next;
1484 GNUNET_free (ent->key);
1485 GNUNET_free (ent->val);
1486 GNUNET_free (ent->hint_filename);
1487 GNUNET_free (ent);
1488 cfg->dirty = GNUNET_YES;
1489 }
1490 GNUNET_free (spos->name);
1491 GNUNET_free (spos->hint_secret_filename);
1492 GNUNET_free (spos->hint_secret_stat);
1493 GNUNET_free (spos->hint_inlined_from_filename);
1494 GNUNET_free (spos);
1495 return;
1496 }
1497 prev = spos;
1498 spos = spos->next;
1499 }
1500}
1501
1502
1503/**
1504 * Copy a configuration value to the given target configuration.
1505 * Overwrites existing entries.
1506 *
1507 * @param cls the destination configuration (`struct GNUNET_CONFIGURATION_Handle *`)
1508 * @param section section for the value
1509 * @param option option name of the value
1510 * @param value value to copy
1511 */
1512static void
1513copy_entry (void *cls,
1514 const char *section,
1515 const char *option,
1516 const char *value)
1517{
1518 struct GNUNET_CONFIGURATION_Handle *dst = cls;
1519
1520 GNUNET_CONFIGURATION_set_value_string (dst,
1521 section,
1522 option,
1523 value);
1524}
1525
1526
1527struct GNUNET_CONFIGURATION_Handle *
1528GNUNET_CONFIGURATION_dup (const struct GNUNET_CONFIGURATION_Handle *cfg)
1529{
1530 struct GNUNET_CONFIGURATION_Handle *ret;
1531
1532 ret = GNUNET_CONFIGURATION_create ();
1533 GNUNET_CONFIGURATION_iterate (cfg, &copy_entry, ret);
1534 return ret;
1535}
1536
1537
1538/**
1539 * A callback function, compares entries from two configurations
1540 * (default against a new configuration) and write the diffs in a
1541 * diff-configuration object (the callback object).
1542 *
1543 * @param cls the diff configuration (`struct DiffHandle *`)
1544 * @param section section for the value (of the default conf.)
1545 * @param option option name of the value (of the default conf.)
1546 * @param value value to copy (of the default conf.)
1547 */
1548static void
1549compare_entries (void *cls,
1550 const char *section,
1551 const char *option,
1552 const char *value)
1553{
1554 struct DiffHandle *dh = cls;
1555 struct ConfigEntry *entNew;
1556
1557 entNew = find_entry (dh->cfg_default, section, option);
1558 if ((NULL != entNew) && (NULL != entNew->val) &&
1559 (0 == strcmp (entNew->val, value)))
1560 return;
1561 GNUNET_CONFIGURATION_set_value_string (dh->cfgDiff,
1562 section,
1563 option,
1564 value);
1565}
1566
1567
1568struct GNUNET_CONFIGURATION_Handle *
1569GNUNET_CONFIGURATION_get_diff (
1570 const struct GNUNET_CONFIGURATION_Handle *cfg_default,
1571 const struct GNUNET_CONFIGURATION_Handle *cfg_new)
1572{
1573 struct DiffHandle diffHandle;
1574
1575 diffHandle.cfgDiff = GNUNET_CONFIGURATION_create ();
1576 diffHandle.cfg_default = cfg_default;
1577 GNUNET_CONFIGURATION_iterate (cfg_new, &compare_entries, &diffHandle);
1578 return diffHandle.cfgDiff;
1579}
1580
1581
1582enum GNUNET_GenericReturnValue
1583GNUNET_CONFIGURATION_write_diffs (
1584 const struct GNUNET_CONFIGURATION_Handle *cfg_default,
1585 const struct GNUNET_CONFIGURATION_Handle *cfg_new,
1586 const char *filename)
1587{
1588 int ret;
1589 struct GNUNET_CONFIGURATION_Handle *diff;
1590
1591 diff = GNUNET_CONFIGURATION_get_diff (cfg_default, cfg_new);
1592 ret = GNUNET_CONFIGURATION_write (diff, filename);
1593 GNUNET_CONFIGURATION_destroy (diff);
1594 return ret;
1595}
1596
1597
1598void
1599GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle *cfg,
1600 const char *section,
1601 const char *option,
1602 const char *value)
1603{
1604 struct ConfigSection *sec;
1605 struct ConfigEntry *e;
1606 char *nv;
1607
1608 e = find_entry (cfg, section, option);
1609 if (NULL != e)
1610 {
1611 if (NULL == value)
1612 {
1613 GNUNET_free (e->val);
1614 e->val = NULL;
1615 }
1616 else
1617 {
1618 nv = GNUNET_strdup (value);
1619 GNUNET_free (e->val);
1620 e->val = nv;
1621 }
1622 return;
1623 }
1624 sec = find_section (cfg, section);
1625 if (sec == NULL)
1626 {
1627 sec = GNUNET_new (struct ConfigSection);
1628 sec->name = GNUNET_strdup (section);
1629 sec->next = cfg->sections;
1630 cfg->sections = sec;
1631 }
1632 e = GNUNET_new (struct ConfigEntry);
1633 e->key = GNUNET_strdup (option);
1634 e->val = GNUNET_strdup (value);
1635 e->next = sec->entries;
1636 sec->entries = e;
1637}
1638
1639
1640void
1641GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle *cfg,
1642 const char *section,
1643 const char *option,
1644 unsigned long long number)
1645{
1646 char s[64];
1647
1648 GNUNET_snprintf (s,
1649 64,
1650 "%llu",
1651 number);
1652 GNUNET_CONFIGURATION_set_value_string (cfg,
1653 section,
1654 option,
1655 s);
1656}
1657
1658
1659enum GNUNET_GenericReturnValue
1660GNUNET_CONFIGURATION_get_value_number (
1661 const struct GNUNET_CONFIGURATION_Handle *cfg,
1662 const char *section,
1663 const char *option,
1664 unsigned long long *number)
1665{
1666 struct ConfigEntry *e;
1667 char dummy[2];
1668
1669 if (NULL == (e = find_entry (cfg, section, option)))
1670 return GNUNET_SYSERR;
1671 if (NULL == e->val)
1672 return GNUNET_SYSERR;
1673 if (1 != sscanf (e->val, "%llu%1s", number, dummy))
1674 return GNUNET_SYSERR;
1675 return GNUNET_OK;
1676}
1677
1678
1679enum GNUNET_GenericReturnValue
1680GNUNET_CONFIGURATION_get_value_float (
1681 const struct GNUNET_CONFIGURATION_Handle *cfg,
1682 const char *section,
1683 const char *option,
1684 float *number)
1685{
1686 struct ConfigEntry *e;
1687 char dummy[2];
1688
1689 if (NULL == (e = find_entry (cfg, section, option)))
1690 return GNUNET_SYSERR;
1691 if (NULL == e->val)
1692 return GNUNET_SYSERR;
1693 if (1 != sscanf (e->val, "%f%1s", number, dummy))
1694 return GNUNET_SYSERR;
1695 return GNUNET_OK;
1696}
1697
1698
1699enum GNUNET_GenericReturnValue
1700GNUNET_CONFIGURATION_get_value_time (
1701 const struct GNUNET_CONFIGURATION_Handle *cfg,
1702 const char *section,
1703 const char *option,
1704 struct GNUNET_TIME_Relative *time)
1705{
1706 struct ConfigEntry *e;
1707 int ret;
1708
1709 if (NULL == (e = find_entry (cfg, section, option)))
1710 return GNUNET_SYSERR;
1711 if (NULL == e->val)
1712 return GNUNET_SYSERR;
1713 ret = GNUNET_STRINGS_fancy_time_to_relative (e->val, time);
1714 if (GNUNET_OK != ret)
1715 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1716 section,
1717 option,
1718 _ ("Not a valid relative time specification"));
1719 return ret;
1720}
1721
1722
1723enum GNUNET_GenericReturnValue
1724GNUNET_CONFIGURATION_get_value_size (
1725 const struct GNUNET_CONFIGURATION_Handle *cfg,
1726 const char *section,
1727 const char *option,
1728 unsigned long long *size)
1729{
1730 struct ConfigEntry *e;
1731
1732 if (NULL == (e = find_entry (cfg, section, option)))
1733 return GNUNET_SYSERR;
1734 if (NULL == e->val)
1735 return GNUNET_SYSERR;
1736 return GNUNET_STRINGS_fancy_size_to_bytes (e->val, size);
1737}
1738
1739
1740/**
1741 * Get a configuration value that should be a string.
1742 *
1743 * @param cfg configuration to inspect
1744 * @param section section of interest
1745 * @param option option of interest
1746 * @param value will be set to a freshly allocated configuration
1747 * value, or NULL if option is not specified
1748 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1749 */
1750enum GNUNET_GenericReturnValue
1751GNUNET_CONFIGURATION_get_value_string (
1752 const struct GNUNET_CONFIGURATION_Handle *cfg,
1753 const char *section,
1754 const char *option,
1755 char **value)
1756{
1757 struct ConfigEntry *e;
1758
1759 if ((NULL == (e = find_entry (cfg, section, option))) || (NULL == e->val))
1760 {
1761 *value = NULL;
1762 return GNUNET_SYSERR;
1763 }
1764 *value = GNUNET_strdup (e->val);
1765 return GNUNET_OK;
1766}
1767
1768
1769enum GNUNET_GenericReturnValue
1770GNUNET_CONFIGURATION_get_value_choice (
1771 const struct GNUNET_CONFIGURATION_Handle *cfg,
1772 const char *section,
1773 const char *option,
1774 const char *const *choices,
1775 const char **value)
1776{
1777 struct ConfigEntry *e;
1778 unsigned int i;
1779
1780 if (NULL == (e = find_entry (cfg, section, option)))
1781 return GNUNET_SYSERR;
1782 for (i = 0; NULL != choices[i]; i++)
1783 if (0 == strcasecmp (choices[i], e->val))
1784 break;
1785 if (NULL == choices[i])
1786 {
1787 LOG (GNUNET_ERROR_TYPE_ERROR,
1788 _ ("Configuration value '%s' for '%s'"
1789 " in section '%s' is not in set of legal choices\n"),
1790 e->val,
1791 option,
1792 section);
1793 return GNUNET_SYSERR;
1794 }
1795 *value = choices[i];
1796 return GNUNET_OK;
1797}
1798
1799
1800enum GNUNET_GenericReturnValue
1801GNUNET_CONFIGURATION_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg,
1802 const char *section,
1803 const char *option,
1804 void *buf,
1805 size_t buf_size)
1806{
1807 char *enc;
1808 int res;
1809 size_t data_size;
1810
1811 if (GNUNET_OK !=
1812 (res =
1813 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &enc)))
1814 return res;
1815 data_size = (strlen (enc) * 5) / 8;
1816 if (data_size != buf_size)
1817 {
1818 GNUNET_free (enc);
1819 return GNUNET_SYSERR;
1820 }
1821 if (GNUNET_OK !=
1822 GNUNET_STRINGS_string_to_data (enc, strlen (enc), buf, buf_size))
1823 {
1824 GNUNET_free (enc);
1825 return GNUNET_SYSERR;
1826 }
1827 GNUNET_free (enc);
1828 return GNUNET_OK;
1829}
1830
1831
1832enum GNUNET_GenericReturnValue
1833GNUNET_CONFIGURATION_have_value (const struct GNUNET_CONFIGURATION_Handle *cfg,
1834 const char *section,
1835 const char *option)
1836{
1837 struct ConfigEntry *e;
1838
1839 if ((NULL == (e = find_entry (cfg, section, option))) || (NULL == e->val))
1840 return GNUNET_NO;
1841 return GNUNET_YES;
1842}
1843
1844
1845/**
1846 * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR"
1847 * where either in the "PATHS" section or the environment "FOO" is
1848 * set to "DIRECTORY". We also support default expansion,
1849 * i.e. ${VARIABLE:-default} will expand to $VARIABLE if VARIABLE is
1850 * set in PATHS or the environment, and otherwise to "default". Note
1851 * that "default" itself can also be a $-expression, thus
1852 * "${VAR1:-{$VAR2}}" will expand to VAR1 and if that is not defined
1853 * to VAR2.
1854 *
1855 * @param cfg configuration to use for path expansion
1856 * @param orig string to $-expand (will be freed!)
1857 * @param depth recursion depth, used to detect recursive expansions
1858 * @return $-expanded string, never NULL unless @a orig was NULL
1859 */
1860static char *
1861expand_dollar (const struct GNUNET_CONFIGURATION_Handle *cfg,
1862 char *orig,
1863 unsigned int depth)
1864{
1865 char *prefix;
1866 char *result;
1867 char *start;
1868 const char *post;
1869 const char *env;
1870 char *def;
1871 char *end;
1872 unsigned int lopen;
1873 char erased_char;
1874 char *erased_pos;
1875 size_t len;
1876
1877 if (NULL == orig)
1878 return NULL;
1879 if (depth > 128)
1880 {
1881 LOG (GNUNET_ERROR_TYPE_WARNING,
1882 "Recursive expansion suspected, aborting $-expansion for term `%s'\n",
1883 orig);
1884 return orig;
1885 }
1886 LOG (GNUNET_ERROR_TYPE_DEBUG,
1887 "Asked to $-expand %s\n",
1888 orig);
1889 if ('$' != orig[0])
1890 {
1891 LOG (GNUNET_ERROR_TYPE_DEBUG,
1892 "Doesn't start with $ - not expanding\n");
1893 return orig;
1894 }
1895 erased_char = 0;
1896 erased_pos = NULL;
1897 if ('{' == orig[1])
1898 {
1899 start = &orig[2];
1900 lopen = 1;
1901 end = &orig[1];
1902 while (lopen > 0)
1903 {
1904 end++;
1905 switch (*end)
1906 {
1907 case '}':
1908 lopen--;
1909 break;
1910
1911 case '{':
1912 lopen++;
1913 break;
1914
1915 case '\0':
1916 LOG (GNUNET_ERROR_TYPE_WARNING,
1917 "Missing closing `}' in option `%s'\n",
1918 orig);
1919 return orig;
1920
1921 default:
1922 break;
1923 }
1924 }
1925 erased_char = *end;
1926 erased_pos = end;
1927 *end = '\0';
1928 post = end + 1;
1929 def = strchr (orig, ':');
1930 if (NULL != def)
1931 {
1932 *def = '\0';
1933 def++;
1934 if (('-' == *def) || ('=' == *def))
1935 def++;
1936 def = GNUNET_strdup (def);
1937 }
1938 }
1939 else
1940 {
1941 int i;
1942
1943 start = &orig[1];
1944 def = NULL;
1945 i = 0;
1946 while ( (orig[i] != '/') &&
1947 (orig[i] != '\\') &&
1948 (orig[i] != '\0') &&
1949 (orig[i] != ' ') )
1950 i++;
1951 if (orig[i] == '\0')
1952 {
1953 post = "";
1954 }
1955 else
1956 {
1957 erased_char = orig[i];
1958 erased_pos = &orig[i];
1959 orig[i] = '\0';
1960 post = &orig[i + 1];
1961 }
1962 }
1963 LOG (GNUNET_ERROR_TYPE_DEBUG,
1964 "Split into `%s' and `%s' with default %s\n",
1965 start,
1966 post,
1967 def);
1968 if (GNUNET_OK !=
1969 GNUNET_CONFIGURATION_get_value_string (cfg,
1970 "PATHS",
1971 start,
1972 &prefix))
1973 {
1974 if (NULL == (env = getenv (start)))
1975 {
1976 /* try default */
1977 def = expand_dollar (cfg,
1978 def,
1979 depth + 1);
1980 env = def;
1981 }
1982 if (NULL == env)
1983 {
1984 start = GNUNET_strdup (start);
1985 if (erased_pos)
1986 *erased_pos = erased_char;
1987 LOG (GNUNET_ERROR_TYPE_WARNING,
1988 "Failed to expand `%s' in `%s' as it is neither found in [PATHS] nor defined as an environmental variable\n",
1989 start,
1990 orig);
1991 GNUNET_free (start);
1992 return orig;
1993 }
1994 prefix = GNUNET_strdup (env);
1995 }
1996 prefix = GNUNET_CONFIGURATION_expand_dollar (cfg,
1997 prefix);
1998 if ((erased_pos) && ('}' != erased_char))
1999 {
2000 len = strlen (prefix) + 1;
2001 prefix = GNUNET_realloc (prefix, len + 1);
2002 prefix[len - 1] = erased_char;
2003 prefix[len] = '\0';
2004 }
2005 result = GNUNET_malloc (strlen (prefix) + strlen (post) + 1);
2006 strcpy (result, prefix);
2007 strcat (result, post);
2008 GNUNET_free (def);
2009 GNUNET_free (prefix);
2010 GNUNET_free (orig);
2011 return result;
2012}
2013
2014
2015char *
2016GNUNET_CONFIGURATION_expand_dollar (
2017 const struct GNUNET_CONFIGURATION_Handle *cfg,
2018 char *orig)
2019{
2020 char *dup;
2021 size_t i;
2022 size_t len;
2023
2024 for (i = 0; '\0' != orig[i]; i++)
2025 {
2026 if ('$' != orig[i])
2027 continue;
2028 dup = GNUNET_strdup (orig + i);
2029 dup = expand_dollar (cfg, dup, 0);
2030 GNUNET_assert (NULL != dup); /* make compiler happy */
2031 len = strlen (dup) + 1;
2032 orig = GNUNET_realloc (orig, i + len);
2033 GNUNET_memcpy (orig + i, dup, len);
2034 GNUNET_free (dup);
2035 }
2036 return orig;
2037}
2038
2039
2040enum GNUNET_GenericReturnValue
2041GNUNET_CONFIGURATION_get_value_filename (
2042 const struct GNUNET_CONFIGURATION_Handle *cfg,
2043 const char *section,
2044 const char *option,
2045 char **value)
2046{
2047 char *tmp;
2048
2049 if (GNUNET_OK !=
2050 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &tmp))
2051 {
2052 LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to retrieve filename\n");
2053 *value = NULL;
2054 return GNUNET_SYSERR;
2055 }
2056 tmp = GNUNET_CONFIGURATION_expand_dollar (cfg, tmp);
2057 *value = GNUNET_STRINGS_filename_expand (tmp);
2058 GNUNET_free (tmp);
2059 if (*value == NULL)
2060 return GNUNET_SYSERR;
2061 return GNUNET_OK;
2062}
2063
2064
2065enum GNUNET_GenericReturnValue
2066GNUNET_CONFIGURATION_get_value_yesno (
2067 const struct GNUNET_CONFIGURATION_Handle *cfg,
2068 const char *section,
2069 const char *option)
2070{
2071 static const char *yesno[] = { "YES", "NO", NULL };
2072 const char *val;
2073 int ret;
2074
2075 ret =
2076 GNUNET_CONFIGURATION_get_value_choice (cfg, section, option, yesno, &val);
2077 if (ret == GNUNET_SYSERR)
2078 return ret;
2079 if (val == yesno[0])
2080 return GNUNET_YES;
2081 return GNUNET_NO;
2082}
2083
2084
2085int
2086GNUNET_CONFIGURATION_iterate_value_filenames (
2087 const struct GNUNET_CONFIGURATION_Handle *cfg,
2088 const char *section,
2089 const char *option,
2090 GNUNET_FileNameCallback cb,
2091 void *cb_cls)
2092{
2093 char *list;
2094 char *pos;
2095 char *end;
2096 char old;
2097 int ret;
2098
2099 if (GNUNET_OK !=
2100 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
2101 return 0;
2102 GNUNET_assert (list != NULL);
2103 ret = 0;
2104 pos = list;
2105 while (1)
2106 {
2107 while (pos[0] == ' ')
2108 pos++;
2109 if (strlen (pos) == 0)
2110 break;
2111 end = pos + 1;
2112 while ((end[0] != ' ') && (end[0] != '\0'))
2113 {
2114 if (end[0] == '\\')
2115 {
2116 switch (end[1])
2117 {
2118 case '\\':
2119 case ' ':
2120 memmove (end, &end[1], strlen (&end[1]) + 1);
2121
2122 case '\0':
2123 /* illegal, but just keep it */
2124 break;
2125
2126 default:
2127 /* illegal, but just ignore that there was a '/' */
2128 break;
2129 }
2130 }
2131 end++;
2132 }
2133 old = end[0];
2134 end[0] = '\0';
2135 if (strlen (pos) > 0)
2136 {
2137 ret++;
2138 if ((cb != NULL) && (GNUNET_OK != cb (cb_cls, pos)))
2139 {
2140 ret = GNUNET_SYSERR;
2141 break;
2142 }
2143 }
2144 if (old == '\0')
2145 break;
2146 pos = end + 1;
2147 }
2148 GNUNET_free (list);
2149 return ret;
2150}
2151
2152
2153/**
2154 * FIXME.
2155 *
2156 * @param value FIXME
2157 * @return FIXME
2158 */
2159static char *
2160escape_name (const char *value)
2161{
2162 char *escaped;
2163 const char *rpos;
2164 char *wpos;
2165
2166 escaped = GNUNET_malloc (strlen (value) * 2 + 1);
2167 memset (escaped, 0, strlen (value) * 2 + 1);
2168 rpos = value;
2169 wpos = escaped;
2170 while (rpos[0] != '\0')
2171 {
2172 switch (rpos[0])
2173 {
2174 case '\\':
2175 case ' ':
2176 wpos[0] = '\\';
2177 wpos[1] = rpos[0];
2178 wpos += 2;
2179 break;
2180
2181 default:
2182 wpos[0] = rpos[0];
2183 wpos++;
2184 }
2185 rpos++;
2186 }
2187 return escaped;
2188}
2189
2190
2191/**
2192 * FIXME.
2193 *
2194 * @param cls string we compare with (const char*)
2195 * @param fn filename we are currently looking at
2196 * @return #GNUNET_OK if the names do not match, #GNUNET_SYSERR if they do
2197 */
2198static enum GNUNET_GenericReturnValue
2199test_match (void *cls, const char *fn)
2200{
2201 const char *of = cls;
2202
2203 return (0 == strcmp (of, fn)) ? GNUNET_SYSERR : GNUNET_OK;
2204}
2205
2206
2207enum GNUNET_GenericReturnValue
2208GNUNET_CONFIGURATION_append_value_filename (
2209 struct GNUNET_CONFIGURATION_Handle *cfg,
2210 const char *section,
2211 const char *option,
2212 const char *value)
2213{
2214 char *escaped;
2215 char *old;
2216 char *nw;
2217
2218 if (GNUNET_SYSERR ==
2219 GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
2220 section,
2221 option,
2222 &test_match,
2223 (void *) value))
2224 return GNUNET_NO; /* already exists */
2225 if (GNUNET_OK !=
2226 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &old))
2227 old = GNUNET_strdup ("");
2228 escaped = escape_name (value);
2229 nw = GNUNET_malloc (strlen (old) + strlen (escaped) + 2);
2230 strcpy (nw, old);
2231 if (strlen (old) > 0)
2232 strcat (nw, " ");
2233 strcat (nw, escaped);
2234 GNUNET_CONFIGURATION_set_value_string (cfg,
2235 section,
2236 option,
2237 nw);
2238 GNUNET_free (old);
2239 GNUNET_free (nw);
2240 GNUNET_free (escaped);
2241 return GNUNET_OK;
2242}
2243
2244
2245enum GNUNET_GenericReturnValue
2246GNUNET_CONFIGURATION_remove_value_filename (
2247 struct GNUNET_CONFIGURATION_Handle *cfg,
2248 const char *section,
2249 const char *option,
2250 const char *value)
2251{
2252 char *list;
2253 char *pos;
2254 char *end;
2255 char *match;
2256 char old;
2257
2258 if (GNUNET_OK !=
2259 GNUNET_CONFIGURATION_get_value_string (cfg,
2260 section,
2261 option,
2262 &list))
2263 return GNUNET_NO;
2264 match = escape_name (value);
2265 pos = list;
2266 while (1)
2267 {
2268 while (pos[0] == ' ')
2269 pos++;
2270 if (strlen (pos) == 0)
2271 break;
2272 end = pos + 1;
2273 while ((end[0] != ' ') && (end[0] != '\0'))
2274 {
2275 if (end[0] == '\\')
2276 {
2277 switch (end[1])
2278 {
2279 case '\\':
2280 case ' ':
2281 end++;
2282 break;
2283
2284 case '\0':
2285 /* illegal, but just keep it */
2286 break;
2287
2288 default:
2289 /* illegal, but just ignore that there was a '/' */
2290 break;
2291 }
2292 }
2293 end++;
2294 }
2295 old = end[0];
2296 end[0] = '\0';
2297 if (0 == strcmp (pos, match))
2298 {
2299 if (old != '\0')
2300 memmove (pos,
2301 &end[1],
2302 strlen (&end[1]) + 1);
2303 else
2304 {
2305 if (pos != list)
2306 pos[-1] = '\0';
2307 else
2308 pos[0] = '\0';
2309 }
2310 GNUNET_CONFIGURATION_set_value_string (cfg,
2311 section,
2312 option,
2313 list);
2314 GNUNET_free (list);
2315 GNUNET_free (match);
2316 return GNUNET_OK;
2317 }
2318 if (old == '\0')
2319 break;
2320 end[0] = old;
2321 pos = end + 1;
2322 }
2323 GNUNET_free (list);
2324 GNUNET_free (match);
2325 return GNUNET_NO;
2326}
2327
2328
2329enum GNUNET_GenericReturnValue
2330GNUNET_CONFIGURATION_load_from (struct GNUNET_CONFIGURATION_Handle *cfg,
2331 const char *defaults_d)
2332{
2333 struct CollectFilesContext files_context = {
2334 .files = NULL,
2335 .files_length = 0,
2336 };
2337 enum GNUNET_GenericReturnValue fun_ret;
2338
2339 if (GNUNET_SYSERR ==
2340 GNUNET_DISK_directory_scan (defaults_d, &collect_files_cb,
2341 &files_context))
2342 return GNUNET_SYSERR; /* no configuration at all found */
2343 qsort (files_context.files,
2344 files_context.files_length,
2345 sizeof (char *),
2346 pstrcmp);
2347 for (unsigned int i = 0; i < files_context.files_length; i++)
2348 {
2349 char *ext;
2350 const char *filename = files_context.files[i];
2351
2352 /* Examine file extension */
2353 ext = strrchr (filename, '.');
2354 if ((NULL == ext) || (0 != strcmp (ext, ".conf")))
2355 {
2356 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Skipping file `%s'\n", filename);
2357 fun_ret = GNUNET_OK;
2358 goto cleanup;
2359 }
2360 fun_ret = GNUNET_CONFIGURATION_parse (cfg, filename);
2361 if (fun_ret != GNUNET_OK)
2362 break;
2363 }
2364cleanup:
2365 if (files_context.files_length > 0)
2366 {
2367 for (size_t i = 0; i < files_context.files_length; i++)
2368 GNUNET_free (files_context.files[i]);
2369 GNUNET_array_grow (files_context.files,
2370 files_context.files_length,
2371 0);
2372 }
2373 return fun_ret;
2374}
2375
2376
2377char *
2378GNUNET_CONFIGURATION_default_filename (void)
2379{
2380 char *cfg_fn;
2381 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
2382 const char *xdg = getenv ("XDG_CONFIG_HOME");
2383
2384 if (NULL != xdg)
2385 GNUNET_asprintf (&cfg_fn,
2386 "%s%s%s",
2387 xdg,
2388 DIR_SEPARATOR_STR,
2389 pd->config_file);
2390 else
2391 cfg_fn = GNUNET_strdup (pd->user_config_file);
2392
2393 if (GNUNET_OK == GNUNET_DISK_file_test_read (cfg_fn))
2394 return cfg_fn;
2395 GNUNET_free (cfg_fn);
2396
2397 /* Fall back to /etc/ for the default configuration.
2398 Should be okay to use forward slashes here. */
2399
2400 GNUNET_asprintf (&cfg_fn,
2401 "/etc/%s",
2402 pd->config_file);
2403 if (GNUNET_OK == GNUNET_DISK_file_test_read (cfg_fn))
2404 return cfg_fn;
2405 GNUNET_free (cfg_fn);
2406
2407 GNUNET_asprintf (&cfg_fn,
2408 "/etc/%s/%s",
2409 pd->project_dirname,
2410 pd->config_file);
2411 if (GNUNET_OK == GNUNET_DISK_file_test_read (cfg_fn))
2412 return cfg_fn;
2413
2414 GNUNET_free (cfg_fn);
2415 return NULL;
2416}
2417
2418
2419struct GNUNET_CONFIGURATION_Handle *
2420GNUNET_CONFIGURATION_default (void)
2421{
2422 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
2423 const struct GNUNET_OS_ProjectData *dpd = GNUNET_OS_project_data_default ();
2424 const char *xdg = getenv ("XDG_CONFIG_HOME");
2425 char *cfgname = NULL;
2426 struct GNUNET_CONFIGURATION_Handle *cfg;
2427
2428 /* Makes sure function implicitly looking at the installation directory (for
2429 example GNUNET_CONFIGURATION_load further down) use GNUnet's environment
2430 instead of the caller's. It's done at the start to make sure as many
2431 functions as possible are directed to the proper paths. */
2432 GNUNET_OS_init (dpd);
2433
2434 cfg = GNUNET_CONFIGURATION_create ();
2435
2436 /* First, try user configuration. */
2437 if (NULL != xdg)
2438 GNUNET_asprintf (&cfgname, "%s/%s", xdg, dpd->config_file);
2439 else
2440 cfgname = GNUNET_strdup (dpd->user_config_file);
2441
2442 /* If user config doesn't exist, try in
2443 /etc/<projdir>/<cfgfile> and /etc/<cfgfile> */
2444 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
2445 {
2446 GNUNET_free (cfgname);
2447 GNUNET_asprintf (&cfgname, "/etc/%s", dpd->config_file);
2448 }
2449 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
2450 {
2451 GNUNET_free (cfgname);
2452 GNUNET_asprintf (&cfgname,
2453 "/etc/%s/%s",
2454 dpd->project_dirname,
2455 dpd->config_file);
2456 }
2457 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
2458 {
2459 LOG (GNUNET_ERROR_TYPE_ERROR,
2460 "Unable to top-level configuration file.\n");
2461 GNUNET_OS_init (pd);
2462 GNUNET_CONFIGURATION_destroy (cfg);
2463 GNUNET_free (cfgname);
2464 return NULL;
2465 }
2466
2467 /* We found a configuration file that looks good, try to load it. */
2468
2469 LOG (GNUNET_ERROR_TYPE_DEBUG,
2470 "Loading top-level configuration from '%s'\n",
2471 cfgname);
2472 if (GNUNET_OK !=
2473 GNUNET_CONFIGURATION_load (cfg, cfgname))
2474 {
2475 GNUNET_OS_init (pd);
2476 GNUNET_CONFIGURATION_destroy (cfg);
2477 GNUNET_free (cfgname);
2478 return NULL;
2479 }
2480 GNUNET_free (cfgname);
2481 GNUNET_OS_init (pd);
2482 return cfg;
2483}
2484
2485
2486/**
2487 * Load configuration (starts with defaults, then loads
2488 * system-specific configuration).
2489 *
2490 * @param cfg configuration to update
2491 * @param filename name of the configuration file, NULL to load defaults
2492 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
2493 */
2494enum GNUNET_GenericReturnValue
2495GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
2496 const char *filename)
2497{
2498 char *baseconfig;
2499 const char *base_config_varname;
2500
2501 if (cfg->load_called)
2502 {
2503 /* FIXME: Make this a GNUNET_assert later */
2504 GNUNET_break (0);
2505 GNUNET_free (cfg->main_filename);
2506 }
2507 cfg->load_called = true;
2508 if (NULL != filename)
2509 {
2510 GNUNET_free (cfg->main_filename);
2511 cfg->main_filename = GNUNET_strdup (filename);
2512 }
2513
2514 base_config_varname = GNUNET_OS_project_data_get ()->base_config_varname;
2515
2516 if ((NULL != base_config_varname)
2517 && (NULL != (baseconfig = getenv (base_config_varname))))
2518 {
2519 baseconfig = GNUNET_strdup (baseconfig);
2520 }
2521 else
2522 {
2523 char *ipath;
2524
2525 ipath = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
2526 if (NULL == ipath)
2527 {
2528 GNUNET_break (0);
2529 return GNUNET_SYSERR;
2530 }
2531 GNUNET_asprintf (&baseconfig, "%s%s", ipath, "config.d");
2532 GNUNET_free (ipath);
2533 }
2534
2535 char *dname = GNUNET_STRINGS_filename_expand (baseconfig);
2536 GNUNET_free (baseconfig);
2537
2538 if ((GNUNET_YES ==
2539 GNUNET_DISK_directory_test (dname,
2540 GNUNET_YES)) &&
2541 (GNUNET_SYSERR ==
2542 GNUNET_CONFIGURATION_load_from (cfg,
2543 dname)))
2544 {
2545 LOG (GNUNET_ERROR_TYPE_WARNING,
2546 "Failed to load base configuration from '%s'\n",
2547 filename);
2548 GNUNET_free (dname);
2549 return GNUNET_SYSERR; /* no configuration at all found */
2550 }
2551 GNUNET_free (dname);
2552 if ((NULL != filename) &&
2553 (GNUNET_OK !=
2554 GNUNET_CONFIGURATION_parse (cfg,
2555 filename)))
2556 {
2557 /* specified configuration not found */
2558 LOG (GNUNET_ERROR_TYPE_WARNING,
2559 "Failed to load configuration from file '%s'\n",
2560 filename);
2561 return GNUNET_SYSERR;
2562 }
2563 if (((GNUNET_YES !=
2564 GNUNET_CONFIGURATION_have_value (cfg,
2565 "PATHS",
2566 "DEFAULTCONFIG"))) &&
2567 (filename != NULL))
2568 GNUNET_CONFIGURATION_set_value_string (cfg,
2569 "PATHS",
2570 "DEFAULTCONFIG",
2571 filename);
2572 return GNUNET_OK;
2573}
2574
2575
2576/* end of configuration.c */