2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Pwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/types.h>
33 #include "pwmd-error.h"
36 #include "util-misc.h"
38 #include "util-slist.h"
39 #include "util-string.h"
42 #define DEFAULT_CACHE_TIMEOUT "600"
43 #define DEFAULT_KEEPALIVE_INTERVAL "60"
45 #define INVALID_VALUE(file, line) do { \
47 log_write(_("%s(%i): invalid value for parameter."), file, line); \
52 PARAM_INT
, PARAM_CHARP
, PARAM_LONG
, PARAM_LONGLONG
, PARAM_CHARPP
,
53 PARAM_BOOL
, PARAM_ULONG
, PARAM_ULONGLONG
56 static struct config_params_s
62 { "backup", PARAM_BOOL
, "true"},
63 { "socket_path", PARAM_CHARP
, NULL
},
64 { "socket_perms", PARAM_CHARP
, NULL
},
65 { "passphrase", PARAM_CHARP
, NULL
},
66 { "passphrase_file", PARAM_CHARP
, NULL
},
67 { "log_path", PARAM_CHARP
, "~/.pwmd/log"},
68 { "enable_logging", PARAM_BOOL
, "0"},
69 { "log_keepopen", PARAM_BOOL
, "true"},
70 { "log_level", PARAM_INT
, "0"},
71 { "disable_mlockall", PARAM_BOOL
, "true"},
72 { "cache_timeout", PARAM_INT
, DEFAULT_CACHE_TIMEOUT
},
73 { "cache_push", PARAM_CHARPP
, NULL
},
74 { "disable_list_and_dump", PARAM_BOOL
, "false"},
75 { "recursion_depth", PARAM_INT
, "100"},
76 { "syslog", PARAM_BOOL
, "false"},
77 { "xfer_progress", PARAM_INT
, "8196"},
78 { "allowed", PARAM_CHARPP
, NULL
},
79 { "allowed_file", PARAM_CHARP
, NULL
},
80 { "priority", PARAM_INT
, INVALID_PRIORITY
},
81 { "keepalive_interval", PARAM_INT
, DEFAULT_KEEPALIVE_INTERVAL
},
82 { "tcp_port", PARAM_INT
, "6466"},
83 { "enable_tcp", PARAM_BOOL
, "false"},
84 { "tcp_require_key", PARAM_BOOL
, "false"},
85 { "tcp_wait", PARAM_INT
, "0"},
86 { "tcp_bind", PARAM_CHARP
, "any"},
87 { "tcp_interface", PARAM_CHARP
, NULL
},
88 { "tls_timeout", PARAM_INT
, "300"},
89 { "tls_cipher_suite", PARAM_CHARP
, "SECURE256"},
90 { "require_save_key", PARAM_BOOL
, "true"},
91 { "invoking_user", PARAM_CHARP
, NULL
},
92 { "invoking_tls", PARAM_CHARP
, NULL
},
93 { "encrypt_to", PARAM_BOOL
, "false"},
94 { "always_trust", PARAM_BOOL
, "false"},
95 { "gpg_homedir", PARAM_CHARP
, NULL
},
110 unsigned long ultype
;
111 unsigned long long ulltype
;
115 static struct config_section_s
*config_find_section (struct slist_s
*config
,
117 static int new_param (struct config_section_s
*section
, const char *filename
,
118 int lineno
, const char *name
, const char *value
,
120 static void free_section (struct config_section_s
*s
);
121 static int set_defaults (struct slist_s
**config
);
124 section_remove_param (struct config_section_s
*section
, const char *name
)
126 unsigned i
, t
= slist_length (section
->params
);
128 for (i
= 0; i
< t
; i
++)
130 struct config_param_s
*p
= slist_nth_data (section
->params
, i
);
135 if (!strcmp (p
->name
, name
))
140 xfree (p
->value
.cptype
);
143 strv_free (p
->value
.cpptype
);
147 section
->params
= slist_remove (section
->params
, p
);
158 MUTEX_LOCK (&rcfile_mutex
);
159 unsigned i
, t
= slist_length (global_config
);
161 for (i
= 0; i
< t
; i
++)
163 struct config_section_s
*s
= slist_nth_data (global_config
, i
);
167 section_remove_param (s
, "passphrase");
170 MUTEX_UNLOCK (&rcfile_mutex
);
173 static struct config_param_s
*
174 config_has_param (struct config_section_s
*s
, const char *what
)
176 unsigned i
, t
= slist_length (s
->params
);
178 for (i
= 0; i
< t
; i
++)
180 struct config_param_s
*p
= slist_nth_data (s
->params
, i
);
184 if (!strcmp (p
->name
, what
))
191 static struct config_param_s
*
192 config_get_param (struct slist_s
*config
,
193 const char *section
, const char *what
, int *exists
)
195 unsigned i
, t
= slist_length (config
);
199 for (i
= 0; i
< t
; i
++)
201 struct config_param_s
*p
;
202 struct config_section_s
*s
= slist_nth_data (config
, i
);
207 if (strcmp (s
->name
, section
))
210 p
= config_has_param (s
, what
);
221 static struct config_section_s
*
222 new_section (struct slist_s
**config
, const char *name
)
225 struct config_section_s
*s
= xcalloc (1, sizeof (struct config_section_s
));
230 s
->name
= str_dup (name
);
233 log_write ("%s", pwmd_strerror (ENOMEM
));
238 tmp
= slist_append (*config
, s
);
241 log_write ("%s", pwmd_strerror (ENOMEM
));
252 config_set_string_param (struct slist_s
**config
, const char *section
,
253 const char *name
, const char *value
)
255 struct config_section_s
*s
= config_find_section (*config
, section
);
259 s
= new_section (config
, section
);
264 return new_param (s
, NULL
, 0, name
, value
, PARAM_CHARP
);
268 config_get_string_param (struct slist_s
*config
, const char *section
,
269 const char *what
, int *exists
)
271 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
272 return *exists
&& p
->value
.cptype
? str_dup (p
->value
.cptype
) : NULL
;
276 config_set_int_param (struct slist_s
**config
, const char *section
,
277 const char *name
, const char *value
)
279 struct config_section_s
*s
= config_find_section (*config
, section
);
283 s
= new_section (config
, section
);
288 return new_param (s
, NULL
, 0, name
, value
, PARAM_INT
);
292 config_get_int_param (struct slist_s
*config
, const char *section
,
293 const char *what
, int *exists
)
295 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
296 return *exists
? p
->value
.itype
: -1;
300 config_set_bool_param (struct slist_s
**config
, const char *section
,
301 const char *name
, const char *value
)
303 struct config_section_s
*s
= config_find_section (*config
, section
);
307 s
= new_section (config
, section
);
312 return new_param (s
, NULL
, 0, name
, value
, PARAM_BOOL
);
316 config_get_bool_param (struct slist_s
*config
, const char *section
,
317 const char *what
, int *exists
)
319 return config_get_int_param (config
, section
, what
, exists
);
323 config_set_long_param (struct slist_s
**config
, const char *section
,
324 const char *name
, const char *value
)
326 struct config_section_s
*s
= config_find_section (*config
, section
);
330 s
= new_section (config
, section
);
335 return new_param (s
, NULL
, 0, name
, value
, PARAM_LONG
);
339 config_get_ulong_param (struct slist_s
*config
, const char *section
,
340 const char *what
, int *exists
)
342 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
343 return *exists
? p
->value
.ultype
: 0;
347 config_get_long_param (struct slist_s
*config
, const char *section
,
348 const char *what
, int *exists
)
350 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
351 return *exists
? p
->value
.ltype
: -1;
355 config_set_longlong_param (struct slist_s
**config
, const char *section
,
356 const char *name
, const char *value
)
358 struct config_section_s
*s
= config_find_section (*config
, section
);
362 s
= new_section (config
, section
);
367 return new_param (s
, NULL
, 0, name
, value
, PARAM_LONGLONG
);
371 config_get_longlong_param (struct slist_s
*config
,
372 const char *section
, const char *what
, int *exists
)
374 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
375 return *exists
? p
->value
.lltype
: -1;
379 config_get_ulonglong_param (struct slist_s
*config
,
381 const char *what
, int *exists
)
383 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
384 return *exists
? p
->value
.ulltype
: 0;
388 config_set_list_param (struct slist_s
**config
, const char *section
,
389 const char *name
, const char *value
)
391 struct config_section_s
*s
= config_find_section (*config
, section
);
395 s
= new_section (config
, section
);
400 return new_param (s
, NULL
, 0, name
, value
, PARAM_CHARPP
);
404 config_get_list_param (struct slist_s
*config
, const char *section
,
405 const char *what
, int *exists
)
407 struct config_param_s
*p
= config_get_param (config
, section
, what
, exists
);
408 return *exists
&& p
->value
.cpptype
? strv_dup (p
->value
.cpptype
) : NULL
;
412 config_get_string (const char *section
, const char *what
)
415 const char *where
= section
? section
: "global";
418 MUTEX_LOCK (&rcfile_mutex
);
419 val
= config_get_string_param (global_config
, where
, what
, &exists
);
420 if (!exists
&& strcmp (section
? section
: "", "global"))
421 val
= config_get_string_param (global_config
, "global", what
, &exists
);
423 MUTEX_UNLOCK (&rcfile_mutex
);
428 config_get_list (const char *section
, const char *what
)
431 const char *where
= section
? section
: "global";
434 MUTEX_LOCK (&rcfile_mutex
);
435 val
= config_get_list_param (global_config
, where
, what
, &exists
);
436 if (!exists
&& strcmp (section
? section
: "", "global"))
437 val
= config_get_list_param (global_config
, "global", what
, &exists
);
439 MUTEX_UNLOCK (&rcfile_mutex
);
444 config_get_integer (const char *section
, const char *what
)
447 const char *where
= section
? section
: "global";
450 MUTEX_LOCK (&rcfile_mutex
);
451 val
= config_get_int_param (global_config
, where
, what
, &exists
);
452 if (!exists
&& strcmp (section
? section
: "", "global"))
453 val
= config_get_int_param (global_config
, "global", what
, &exists
);
455 MUTEX_UNLOCK (&rcfile_mutex
);
460 config_get_longlong (const char *section
, const char *what
)
463 const char *where
= section
? section
: "global";
466 MUTEX_LOCK (&rcfile_mutex
);
467 val
= config_get_longlong_param (global_config
, where
, what
, &exists
);
468 if (!exists
&& strcmp (section
? section
: "", "global"))
469 val
= config_get_longlong_param (global_config
, "global", what
, &exists
);
471 MUTEX_UNLOCK (&rcfile_mutex
);
476 config_get_ulonglong (const char *section
, const char *what
)
478 unsigned long long val
= 0;
479 const char *where
= section
? section
: "global";
482 MUTEX_LOCK (&rcfile_mutex
);
483 val
= config_get_ulonglong_param (global_config
, where
, what
, &exists
);
484 if (!exists
&& strcmp (section
? section
: "", "global"))
485 val
= config_get_ulonglong_param (global_config
, "global", what
, &exists
);
487 MUTEX_UNLOCK (&rcfile_mutex
);
492 config_get_long (const char *section
, const char *what
)
495 const char *where
= section
? section
: "global";
498 MUTEX_LOCK (&rcfile_mutex
);
499 val
= config_get_long_param (global_config
, where
, what
, &exists
);
500 if (!exists
&& strcmp (section
? section
: "", "global"))
501 val
= config_get_long_param (global_config
, "global", what
, &exists
);
503 MUTEX_UNLOCK (&rcfile_mutex
);
508 config_get_ulong (const char *section
, const char *what
)
510 unsigned long val
= 0;
511 const char *where
= section
? section
: "global";
514 MUTEX_LOCK (&rcfile_mutex
);
515 val
= config_get_ulong_param (global_config
, where
, what
, &exists
);
516 if (!exists
&& strcmp (section
? section
: "", "global"))
517 val
= config_get_ulong_param (global_config
, "global", what
, &exists
);
519 MUTEX_UNLOCK (&rcfile_mutex
);
524 config_get_boolean (const char *section
, const char *what
)
526 return config_get_integer (section
, what
);
530 config_get_value (const char *section
, const char *what
)
532 const char *where
= section
? section
: "global";
539 unsigned long long ullval
;
544 MUTEX_LOCK (&rcfile_mutex
);
546 for (i
= 0; config_params
[i
].name
; i
++)
548 if (!strcmp (config_params
[i
].name
, what
))
550 switch (config_params
[i
].type
)
554 ival
= config_get_int_param (global_config
, where
, what
,
556 if (!exists
&& strcmp (section
? section
: "", "global"))
557 ival
= config_get_int_param (global_config
, "global", what
,
559 result
= str_asprintf ("%i", ival
);
562 cpval
= config_get_string_param (global_config
, where
, what
,
564 if (!exists
&& strcmp (section
? section
: "", "global"))
566 config_get_string_param (global_config
, "global", what
,
571 lval
= config_get_long_param (global_config
, where
, what
,
573 if (!exists
&& strcmp (section
? section
: "", "global"))
574 lval
= config_get_long_param (global_config
, "global", what
,
576 result
= str_asprintf ("%li", lval
);
579 ulval
= config_get_ulong_param (global_config
, where
, what
,
581 if (!exists
&& strcmp (section
? section
: "", "global"))
582 ulval
= config_get_ulong_param (global_config
, "global", what
,
584 result
= str_asprintf ("%lu", ulval
);
587 llval
= config_get_longlong_param (global_config
, where
, what
,
589 if (!exists
&& strcmp (section
? section
: "", "global"))
590 llval
= config_get_longlong_param (global_config
, "global",
592 result
= str_asprintf ("%lli", llval
);
594 case PARAM_ULONGLONG
:
595 ullval
= config_get_ulonglong_param (global_config
, where
, what
,
597 if (!exists
&& strcmp (section
? section
: "", "global"))
598 ullval
= config_get_ulonglong_param (global_config
, "global",
600 result
= str_asprintf ("%llu", ullval
);
603 cppval
= config_get_list_param (global_config
, where
, what
,
605 if (!exists
&& strcmp (section
? section
: "", "global"))
606 cppval
= config_get_list_param (global_config
, "global", what
,
610 result
= strv_join (",", cppval
);
618 MUTEX_UNLOCK (&rcfile_mutex
);
623 parse_allowed_file (struct slist_s
*config
, const char *section
)
630 char *p
= config_get_string_param (config
, section
, "allowed_file", &exists
);
638 tmp
= expand_homedir (p
);
644 log_write ("%s: %s", p
, strerror (errno
));
650 list
= config_get_list_param (config
, section
, "allowed", &exists
);
654 log_write ("%s", strerror (ENOMEM
));
658 while ((p
= fgets (buf
, sizeof (buf
), fp
)))
662 if (p
[strlen(p
)-1] == '\n')
665 while (*p
&& isspace (*p
))
673 pp
= strv_cat (list
, str_dup (p
));
680 log_write ("%s", strerror (ENOMEM
));
692 p
= strv_join (",", list
);
697 log_write ("%s", strerror (ENOMEM
));
701 config_set_list_param (&config
, section
, "allowed", p
);
706 fixup_allowed_once (struct slist_s
**config
, const char *section
)
708 char **list
, **pp
, *p
;
711 parse_allowed_file (*config
, section
);
712 list
= config_get_list_param (*config
, section
, "allowed", &exists
);
713 for (pp
= list
; pp
&& *pp
; pp
++)
717 for (p
= *pp
; p
&& *p
; p
++)
725 if (!strcmp (section
, "global"))
727 p
= get_username (getuid());
729 if (config_set_list_param (config
, section
, "allowed", p
))
739 list
= config_get_list_param (*config
, "global", "allowed", &exists
);
742 p
= strv_join (",", list
);
744 if (config_set_list_param (config
, section
, "allowed", p
))
759 fixup_allowed (struct slist_s
**config
)
761 int n
, t
= slist_length (*config
);
763 for (n
= 0; n
< t
; n
++)
765 struct config_section_s
*section
;
767 section
= slist_nth_data (*config
, n
);
768 if (fixup_allowed_once (config
, section
->name
))
776 set_defaults (struct slist_s
**config
)
784 for (i
= 0; config_params
[i
].name
; i
++)
786 switch (config_params
[i
].type
)
789 config_get_bool_param (*config
, "global", config_params
[i
].name
,
793 if (config_set_bool_param
794 (config
, "global", config_params
[i
].name
,
795 config_params
[i
].value
))
800 config_get_int_param (*config
, "global", config_params
[i
].name
,
804 if (config_set_int_param
805 (config
, "global", config_params
[i
].name
,
806 config_params
[i
].value
))
811 s
= config_get_string_param (*config
, "global",
812 config_params
[i
].name
, &exists
);
814 if (!exists
&& config_params
[i
].value
)
816 if (config_set_string_param (config
, "global",
817 config_params
[i
].name
,
818 config_params
[i
].value
))
823 list
= config_get_list_param (*config
, "global",
824 config_params
[i
].name
, &exists
);
826 if (!exists
&& config_params
[i
].value
)
828 if (config_set_list_param (config
, "global",
829 config_params
[i
].name
,
830 config_params
[i
].value
))
835 config_get_long_param (*config
, "global", config_params
[i
].name
,
839 if (config_set_long_param
840 (config
, "global", config_params
[i
].name
,
841 config_params
[i
].value
))
846 config_get_longlong_param (*config
, "global", config_params
[i
].name
,
850 if (config_set_longlong_param (config
, "global",
851 config_params
[i
].name
,
852 config_params
[i
].value
))
860 if (fixup_allowed (config
))
863 max_recursion_depth
= config_get_int_param (*config
, "global",
864 "recursion_depth", &exists
);
865 disable_list_and_dump
= config_get_bool_param (*config
, "global",
866 "disable_list_and_dump",
870 config_get_bool_param (*config
, "global", "disable_mlockall", &exists
);
873 s
= config_get_string_param(*config
, "global", "invoking_user", &exists
);
876 pwd
= getpwuid(getuid());
882 log_write (_("could not set invoking user: user '%s' is invalid"), s
);
887 config_set_string_param(config
, "global", "invoking_user", pwd
->pw_name
);
889 invoking_tls
= config_get_string_param(*config
, "global", "invoking_tls",
891 for (char *p
= invoking_tls
; p
&& *p
; p
++)
894 config_set_string_param(config
, "global", "invoking_tls", invoking_tls
);
896 invoking_uid
= pwd
->pw_uid
;
897 invoking_gid
= pwd
->pw_gid
;
905 static struct config_section_s
*
906 config_find_section (struct slist_s
*config
, const char *name
)
908 unsigned i
, t
= slist_length (config
);
910 for (i
= 0; i
< t
; i
++)
912 struct config_section_s
*s
= slist_nth_data (config
, i
);
914 if (!strcmp (s
->name
, name
))
921 /* Append a new parameter to the list of parameters for a file
922 * section. When an existing parameter of the same name exists, its
926 new_param (struct config_section_s
*section
, const char *filename
, int lineno
,
927 const char *name
, const char *value
, int type
)
929 struct config_param_s
*param
= NULL
;
932 unsigned i
, t
= slist_length (section
->params
);
935 for (i
= 0; i
< t
; i
++)
937 struct config_param_s
*p
= slist_nth_data (section
->params
, i
);
941 if (!strcmp (name
, p
->name
))
951 param
= xcalloc (1, sizeof (struct config_param_s
));
954 log_write ("%s", pwmd_strerror (ENOMEM
));
958 param
->name
= str_dup (name
);
962 log_write ("%s", pwmd_strerror (ENOMEM
));
972 if (!strcasecmp (value
, "no") || !strcasecmp (value
, "0")
973 || !strcasecmp (value
, "false"))
974 param
->value
.itype
= 0;
975 else if (!strcasecmp (value
, "yes") || !strcasecmp (value
, "1")
976 || !strcasecmp (value
, "true"))
977 param
->value
.itype
= 1;
980 INVALID_VALUE (filename
, lineno
);
983 param
->type
= PARAM_INT
;
986 xfree (param
->value
.cptype
);
987 param
->value
.cptype
= NULL
;
988 param
->value
.cptype
= value
&& *value
? str_dup (value
) : NULL
;
989 if (value
&& *value
&& !param
->value
.cptype
)
991 log_write ("%s", pwmd_strerror (ENOMEM
));
996 strv_free (param
->value
.cpptype
);
997 param
->value
.cpptype
= NULL
;
998 param
->value
.cpptype
= value
&& *value
? str_split (value
, ",", 0) : NULL
;
999 if (value
&& *value
&& !param
->value
.cpptype
)
1001 log_write ("%s", pwmd_strerror (ENOMEM
));
1006 param
->value
.itype
= strtol (value
, &e
, 10);
1009 INVALID_VALUE (filename
, lineno
);
1014 param
->value
.ltype
= strtol (value
, &e
, 10);
1017 INVALID_VALUE (filename
, lineno
);
1021 case PARAM_LONGLONG
:
1022 param
->value
.lltype
= strtoll (value
, &e
, 10);
1025 INVALID_VALUE (filename
, lineno
);
1029 case PARAM_ULONGLONG
:
1030 param
->value
.ulltype
= strtoull (value
, &e
, 10);
1033 INVALID_VALUE (filename
, lineno
);
1038 param
->value
.ultype
= strtoul (value
, &e
, 10);
1041 INVALID_VALUE (filename
, lineno
);
1050 tmp
= slist_append (section
->params
, param
);
1053 log_write ("%s", pwmd_strerror (ENOMEM
));
1057 section
->params
= tmp
;
1061 xfree (param
->name
);
1067 config_parse (const char *filename
)
1069 struct slist_s
*tmpconfig
= NULL
, *tmp
;
1070 struct config_section_s
*cur_section
= NULL
;
1074 int have_global
= 0;
1075 FILE *fp
= fopen (filename
, "r");
1079 log_write ("%s: %s", filename
,
1080 pwmd_strerror (gpg_error_from_errno (errno
)));
1082 if (errno
!= ENOENT
)
1085 log_write (_("Using defaults!"));
1089 for (; (s
= fgets (buf
, sizeof (buf
), fp
)); lineno
++)
1091 char line
[LINE_MAX
] = { 0 };
1098 for (; s
&& *s
; s
++)
1102 /* New file section. */
1105 struct config_section_s
*section
;
1106 char *p
= strchr (++s
, ']');
1110 log_write (_("%s(%i): unbalanced braces"), filename
,
1115 len
= strlen (s
) - strlen (p
);
1116 memcpy (line
, s
, len
);
1119 section
= config_find_section (tmpconfig
, line
);
1122 log_write (_("%s(%i): section '%s' already exists!"),
1123 filename
, lineno
, line
);
1127 if (!strcmp (line
, "global"))
1130 section
= xcalloc (1, sizeof (struct config_section_s
));
1131 section
->name
= str_dup (line
);
1135 tmp
= slist_append (tmpconfig
, cur_section
);
1138 log_write ("%s", pwmd_strerror (ENOMEM
));
1145 cur_section
= section
;
1151 log_write (_("%s(%i): parameter outside of section!"), filename
,
1156 /* Parameters for each section. */
1157 for (int m
= 0; config_params
[m
].name
; m
++)
1159 size_t len
= strlen (config_params
[m
].name
);
1161 if (!strncmp (s
, config_params
[m
].name
, len
))
1165 while (*p
&& *p
== ' ')
1168 if (!*p
|| *p
!= '=')
1172 while (*p
&& isspace (*p
))
1176 if (new_param (cur_section
, filename
, lineno
, s
, p
,
1177 config_params
[m
].type
))
1187 log_write (_("%s(%i): unknown parameter"), filename
, lineno
);
1197 tmp
= slist_append (tmpconfig
, cur_section
);
1200 log_write ("%s", pwmd_strerror (ENOMEM
));
1210 ("WARNING: %s: could not find a [global] configuration section!"),
1214 if (set_defaults (&tmpconfig
))
1226 config_free (tmpconfig
);
1227 free_section (cur_section
);
1232 free_section (struct config_section_s
*s
)
1239 struct config_param_s
*p
= slist_nth_data (s
->params
, 0);
1244 section_remove_param (s
, p
->name
);
1253 config_free (struct slist_s
*config
)
1257 struct config_section_s
*s
= slist_nth_data (config
, 0);
1262 config
= slist_remove (config
, s
);