1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "sd-messages.h"
29 #include "alloc-util.h"
30 #include "conf-files.h"
31 #include "conf-parser.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "signal-util.h"
40 #include "string-util.h"
42 #include "syslog-util.h"
46 int config_item_table_lookup(
50 ConfigParserCallback
*func
,
55 const ConfigTableItem
*t
;
63 for (t
= table
; t
->lvalue
; t
++) {
65 if (!streq(lvalue
, t
->lvalue
))
68 if (!streq_ptr(section
, t
->section
))
80 int config_item_perf_lookup(
84 ConfigParserCallback
*func
,
89 ConfigPerfItemLookup lookup
= (ConfigPerfItemLookup
) table
;
90 const ConfigPerfItem
*p
;
99 p
= lookup(lvalue
, strlen(lvalue
));
103 key
= strjoin(section
, ".", lvalue
, NULL
);
107 p
= lookup(key
, strlen(key
));
116 *data
= (uint8_t*) userdata
+ p
->offset
;
120 /* Run the user supplied parser for an assignment */
121 static int next_assignment(const char *unit
,
122 const char *filename
,
124 ConfigItemLookup lookup
,
127 unsigned section_line
,
133 ConfigParserCallback func
= NULL
;
144 r
= lookup(table
, section
, lvalue
, &func
, <ype
, &data
, userdata
);
150 return func(unit
, filename
, line
, section
, section_line
,
151 lvalue
, ltype
, rvalue
, data
, userdata
);
156 /* Warn about unknown non-extension fields. */
157 if (!relaxed
&& !startswith(lvalue
, "X-"))
158 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Unknown lvalue '%s' in section '%s'", lvalue
, section
);
163 /* Parse a variable assignment line */
164 static int parse_line(const char* unit
,
165 const char *filename
,
167 const char *sections
,
168 ConfigItemLookup lookup
,
173 unsigned *section_line
,
174 bool *section_ignored
,
190 if (strchr(COMMENTS
"\n", *l
))
193 if (startswith(l
, ".include ")) {
194 _cleanup_free_
char *fn
= NULL
;
196 /* .includes are a bad idea, we only support them here
197 * for historical reasons. They create cyclic include
198 * problems and make it difficult to detect
199 * configuration file changes with an easy
200 * stat(). Better approaches, such as .d/ drop-in
203 * Support for them should be eventually removed. */
205 if (!allow_include
) {
206 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, ".include not allowed here. Ignoring.");
210 fn
= file_in_same_dir(filename
, strstrip(l
+9));
214 return config_parse(unit
, fn
, NULL
, sections
, lookup
, table
, relaxed
, false, false, userdata
);
225 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid section header '%s'", l
);
229 n
= strndup(l
+1, k
-2);
233 if (sections
&& !nulstr_contains(sections
, n
)) {
235 if (!relaxed
&& !startswith(n
, "X-"))
236 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Unknown section '%s'. Ignoring.", n
);
239 *section
= mfree(*section
);
241 *section_ignored
= true;
245 *section_line
= line
;
246 *section_ignored
= false;
252 if (sections
&& !*section
) {
254 if (!relaxed
&& !*section_ignored
)
255 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Assignment outside of section. Ignoring.");
262 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Missing '='.");
269 return next_assignment(unit
,
282 /* Go through the file and parse each line */
283 int config_parse(const char *unit
,
284 const char *filename
,
286 const char *sections
,
287 ConfigItemLookup lookup
,
294 _cleanup_free_
char *section
= NULL
, *continuation
= NULL
;
295 _cleanup_fclose_
FILE *ours
= NULL
;
296 unsigned line
= 0, section_line
= 0;
297 bool section_ignored
= false;
304 f
= ours
= fopen(filename
, "re");
306 /* Only log on request, except for ENOENT,
307 * since we return 0 to the caller. */
308 if (warn
|| errno
== ENOENT
)
309 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
,
310 "Failed to open configuration file '%s': %m", filename
);
311 return errno
== ENOENT
? 0 : -errno
;
315 fd_warn_permissions(filename
, fileno(f
));
318 char l
[LINE_MAX
], *p
, *c
= NULL
, *e
;
319 bool escaped
= false;
321 if (!fgets(l
, sizeof(l
), f
)) {
325 log_error_errno(errno
, "Failed to read configuration file '%s': %m", filename
);
332 c
= strappend(continuation
, l
);
339 continuation
= mfree(continuation
);
344 for (e
= p
; *e
; e
++) {
357 continuation
= strdup(l
);
385 log_warning_errno(r
, "Failed to parse file '%s': %m",
394 /* Parse each config file in the specified directories. */
395 int config_parse_many(const char *conf_file
,
396 const char *conf_file_dirs
,
397 const char *sections
,
398 ConfigItemLookup lookup
,
402 _cleanup_strv_free_
char **files
= NULL
;
406 r
= conf_files_list_nulstr(&files
, ".conf", NULL
, conf_file_dirs
);
411 r
= config_parse(NULL
, conf_file
, NULL
, sections
, lookup
, table
, relaxed
, false, true, userdata
);
416 STRV_FOREACH(fn
, files
) {
417 r
= config_parse(NULL
, *fn
, NULL
, sections
, lookup
, table
, relaxed
, false, true, userdata
);
425 #define DEFINE_PARSER(type, vartype, conv_func) \
426 int config_parse_##type( \
428 const char *filename, \
430 const char *section, \
431 unsigned section_line, \
432 const char *lvalue, \
434 const char *rvalue, \
446 r = conv_func(rvalue, i); \
448 log_syntax(unit, LOG_ERR, filename, line, r, \
449 "Failed to parse %s value, ignoring: %s", \
454 struct __useless_struct_to_allow_trailing_semicolon__
456 DEFINE_PARSER(int, int, safe_atoi
);
457 DEFINE_PARSER(long, long, safe_atoli
);
458 DEFINE_PARSER(uint32
, uint32_t, safe_atou32
);
459 DEFINE_PARSER(uint64
, uint64_t, safe_atou64
);
460 DEFINE_PARSER(unsigned, unsigned, safe_atou
);
461 DEFINE_PARSER(double, double, safe_atod
);
462 DEFINE_PARSER(nsec
, nsec_t
, parse_nsec
);
463 DEFINE_PARSER(sec
, usec_t
, parse_sec
);
464 DEFINE_PARSER(mode
, mode_t
, parse_mode
);
466 int config_parse_iec_size(const char* unit
,
467 const char *filename
,
470 unsigned section_line
,
486 r
= parse_size(rvalue
, 1024, &v
);
487 if (r
< 0 || (uint64_t) (size_t) v
!= v
) {
488 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse size value, ignoring: %s", rvalue
);
496 int config_parse_si_size(const char* unit
,
497 const char *filename
,
500 unsigned section_line
,
516 r
= parse_size(rvalue
, 1000, &v
);
517 if (r
< 0 || (uint64_t) (size_t) v
!= v
) {
518 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse size value, ignoring: %s", rvalue
);
526 int config_parse_iec_uint64(const char* unit
,
527 const char *filename
,
530 unsigned section_line
,
537 uint64_t *bytes
= data
;
545 r
= parse_size(rvalue
, 1024, bytes
);
547 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse size value, ignoring: %s", rvalue
);
552 int config_parse_bool(const char* unit
,
553 const char *filename
,
556 unsigned section_line
,
571 k
= parse_boolean(rvalue
);
573 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
581 int config_parse_tristate(
583 const char *filename
,
586 unsigned section_line
,
600 /* A tristate is pretty much a boolean, except that it can
601 * also take the special value -1, indicating "uninitialized",
602 * much like NULL is for a pointer type. */
604 k
= parse_boolean(rvalue
);
606 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
614 int config_parse_string(
616 const char *filename
,
619 unsigned section_line
,
633 if (!utf8_is_valid(rvalue
)) {
634 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
652 int config_parse_path(
654 const char *filename
,
657 unsigned section_line
,
671 if (!utf8_is_valid(rvalue
)) {
672 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
676 if (!path_is_absolute(rvalue
)) {
677 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", rvalue
);
685 path_kill_slashes(n
);
693 int config_parse_strv(const char *unit
,
694 const char *filename
,
697 unsigned section_line
,
711 if (isempty(rvalue
)) {
714 /* Empty assignment resets the list. As a special rule
715 * we actually fill in a real empty array here rather
716 * than NULL, since some code wants to know if
717 * something was set at all... */
718 empty
= strv_new(NULL
, NULL
);
730 r
= extract_first_word(&rvalue
, &word
, WHITESPACE
, EXTRACT_QUOTES
);
736 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
740 if (!utf8_is_valid(word
)) {
741 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
745 r
= strv_consume(sv
, word
);
753 int config_parse_log_facility(
755 const char *filename
,
758 unsigned section_line
,
773 x
= log_facility_unshifted_from_string(rvalue
);
775 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse log facility, ignoring: %s", rvalue
);
779 *o
= (x
<< 3) | LOG_PRI(*o
);
784 int config_parse_log_level(
786 const char *filename
,
789 unsigned section_line
,
804 x
= log_level_from_string(rvalue
);
806 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse log level, ignoring: %s", rvalue
);
810 *o
= (*o
& LOG_FACMASK
) | x
;
814 int config_parse_signal(
816 const char *filename
,
819 unsigned section_line
,
833 r
= signal_from_string_try_harder(rvalue
);
835 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse signal name, ignoring: %s", rvalue
);
843 int config_parse_personality(
845 const char *filename
,
848 unsigned section_line
,
855 unsigned long *personality
= data
, p
;
862 p
= personality_from_string(rvalue
);
863 if (p
== PERSONALITY_INVALID
) {
864 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse personality, ignoring: %s", rvalue
);