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