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 <netinet/ether.h>
29 #include "conf-parser.h"
35 #include "path-util.h"
37 #include "exit-status.h"
38 #include "sd-messages.h"
40 int log_syntax_internal(const char *unit, int level,
41 const char *file, unsigned line, const char *func,
42 const char *config_file, unsigned config_line,
43 int error, const char *format, ...) {
45 _cleanup_free_ char *msg = NULL;
50 r = vasprintf(&msg, format, ap);
56 r = log_struct_internal(level,
58 getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
59 MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
60 "CONFIG_FILE=%s", config_file,
61 "CONFIG_LINE=%u", config_line,
62 "ERRNO=%d", error > 0 ? error : EINVAL,
63 "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
66 r = log_struct_internal(level,
68 MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
69 "CONFIG_FILE=%s", config_file,
70 "CONFIG_LINE=%u", config_line,
71 "ERRNO=%d", error > 0 ? error : EINVAL,
72 "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
78 int config_item_table_lookup(
82 ConfigParserCallback *func,
95 for (t = table; t->lvalue; t++) {
97 if (!streq(lvalue, t->lvalue))
100 if (!streq_ptr(section, t->section))
112 int config_item_perf_lookup(
116 ConfigParserCallback *func,
121 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
122 const ConfigPerfItem *p;
131 p = lookup(lvalue, strlen(lvalue));
135 key = strjoin(section, ".", lvalue, NULL);
139 p = lookup(key, strlen(key));
148 *data = (uint8_t*) userdata + p->offset;
152 /* Run the user supplied parser for an assignment */
153 static int next_assignment(const char *unit,
154 const char *filename,
156 ConfigItemLookup lookup,
159 unsigned section_line,
165 ConfigParserCallback func = NULL;
176 r = lookup(table, section, lvalue, &func, <ype, &data, userdata);
182 return func(unit, filename, line, section, section_line,
183 lvalue, ltype, rvalue, data, userdata);
188 /* Warn about unknown non-extension fields. */
189 if (!relaxed && !startswith(lvalue, "X-"))
190 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
191 "Unknown lvalue '%s' in section '%s'", lvalue, section);
196 /* Parse a variable assignment line */
197 static int parse_line(const char* unit,
198 const char *filename,
200 const char *sections,
201 ConfigItemLookup lookup,
206 unsigned *section_line,
222 if (strchr(COMMENTS "\n", *l))
225 if (startswith(l, ".include ")) {
226 _cleanup_free_ char *fn = NULL;
228 /* .includes are a bad idea, we only support them here
229 * for historical reasons. They create cyclic include
230 * problems and make it difficult to detect
231 * configuration file changes with an easy
232 * stat(). Better approaches, such as .d/ drop-in
235 * Support for them should be eventually removed. */
237 if (!allow_include) {
238 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
239 ".include not allowed here. Ignoring.");
243 fn = file_in_same_dir(filename, strstrip(l+9));
247 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, userdata);
258 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
259 "Invalid section header '%s'", l);
263 n = strndup(l+1, k-2);
267 if (sections && !nulstr_contains(sections, n)) {
270 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
271 "Unknown section '%s'. Ignoring.", n);
280 *section_line = line;
286 if (sections && !*section) {
289 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
290 "Assignment outside of section. Ignoring.");
297 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
304 return next_assignment(unit,
317 /* Go through the file and parse each line */
318 int config_parse(const char *unit,
319 const char *filename,
321 const char *sections,
322 ConfigItemLookup lookup,
328 _cleanup_free_ char *section = NULL, *continuation = NULL;
329 _cleanup_fclose_ FILE *ours = NULL;
330 unsigned line = 0, section_line = 0;
337 f = ours = fopen(filename, "re");
339 log_error("Failed to open configuration file '%s': %m", filename);
344 fd_warn_permissions(filename, fileno(f));
347 char l[LINE_MAX], *p, *c = NULL, *e;
348 bool escaped = false;
350 if (!fgets(l, sizeof(l), f)) {
354 log_error("Failed to read configuration file '%s': %m", filename);
361 c = strappend(continuation, l);
371 for (e = p; *e; e++) {
384 continuation = strdup(l);
413 #define DEFINE_PARSER(type, vartype, conv_func) \
414 int config_parse_##type(const char *unit, \
415 const char *filename, \
417 const char *section, \
418 unsigned section_line, \
419 const char *lvalue, \
421 const char *rvalue, \
433 r = conv_func(rvalue, i); \
435 log_syntax(unit, LOG_ERR, filename, line, -r, \
436 "Failed to parse %s value, ignoring: %s", \
442 DEFINE_PARSER(int, int, safe_atoi)
443 DEFINE_PARSER(long, long, safe_atoli)
444 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
445 DEFINE_PARSER(unsigned, unsigned, safe_atou)
446 DEFINE_PARSER(double, double, safe_atod)
447 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
448 DEFINE_PARSER(sec, usec_t, parse_sec)
451 int config_parse_bytes_size(const char* unit,
452 const char *filename,
455 unsigned section_line,
471 r = parse_bytes(rvalue, &o);
472 if (r < 0 || (off_t) (size_t) o != o) {
473 log_syntax(unit, LOG_ERR, filename, line, -r,
474 "Failed to parse byte value, ignoring: %s", rvalue);
483 int config_parse_bytes_off(const char* unit,
484 const char *filename,
487 unsigned section_line,
502 assert_cc(sizeof(off_t) == sizeof(uint64_t));
504 r = parse_bytes(rvalue, bytes);
506 log_syntax(unit, LOG_ERR, filename, line, -r,
507 "Failed to parse bytes value, ignoring: %s", rvalue);
512 int config_parse_bool(const char* unit,
513 const char *filename,
516 unsigned section_line,
531 k = parse_boolean(rvalue);
533 log_syntax(unit, LOG_ERR, filename, line, -k,
534 "Failed to parse boolean value, ignoring: %s", rvalue);
542 int config_parse_show_status(const char* unit,
543 const char *filename,
546 unsigned section_line,
554 ShowStatus *b = data;
561 k = parse_show_status(rvalue, b);
563 log_syntax(unit, LOG_ERR, filename, line, -k,
564 "Failed to parse show status setting, ignoring: %s", rvalue);
571 int config_parse_string(const char *unit,
572 const char *filename,
575 unsigned section_line,
594 if (!utf8_is_valid(n)) {
595 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
596 "String is not UTF-8 clean, ignoring assignment: %s", rvalue);
612 int config_parse_path(const char *unit,
613 const char *filename,
616 unsigned section_line,
632 if (!utf8_is_valid(rvalue)) {
633 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
634 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
638 offset = rvalue[0] == '-' && (streq(lvalue, "InaccessibleDirectories") ||
639 streq(lvalue, "ReadOnlyDirectories"));
640 if (!path_is_absolute(rvalue + offset)) {
641 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
642 "Not an absolute path, ignoring: %s", rvalue);
650 path_kill_slashes(n);
658 int config_parse_strv(const char *unit,
659 const char *filename,
662 unsigned section_line,
669 char *** sv = data, *w, *state;
678 if (isempty(rvalue)) {
681 /* Empty assignment resets the list. As a special rule
682 * we actually fill in a real empty array here rather
683 * than NULL, since some code wants to know if
684 * something was set at all... */
685 empty = strv_new(NULL, NULL);
694 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
695 _cleanup_free_ char *n;
697 n = cunescape_length(w, l);
701 if (!utf8_is_valid(n)) {
702 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
703 "String is not UTF-8 clean, ignoring: %s", rvalue);
707 r = strv_extend(sv, n);
715 int config_parse_path_strv(const char *unit,
716 const char *filename,
719 unsigned section_line,
726 char*** sv = data, *w, *state;
735 if (isempty(rvalue)) {
736 /* Empty assignment resets the list */
742 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
743 _cleanup_free_ char *n;
750 if (!utf8_is_valid(n)) {
751 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
752 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
756 offset = n[0] == '-' && (streq(lvalue, "InaccessibleDirectories") ||
757 streq(lvalue, "ReadOnlyDirectories"));
758 if (!path_is_absolute(n + offset)) {
759 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
760 "Not an absolute path, ignoring: %s", rvalue);
764 path_kill_slashes(n);
765 r = strv_extend(sv, n);
773 int config_parse_mode(const char *unit,
774 const char *filename,
777 unsigned section_line,
794 l = strtol(rvalue, &x, 8);
795 if (!x || x == rvalue || *x || errno) {
796 log_syntax(unit, LOG_ERR, filename, line, errno,
797 "Failed to parse mode value, ignoring: %s", rvalue);
801 if (l < 0000 || l > 07777) {
802 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
803 "Mode value out of range, ignoring: %s", rvalue);
811 int config_parse_facility(const char *unit,
812 const char *filename,
815 unsigned section_line,
830 x = log_facility_unshifted_from_string(rvalue);
832 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
833 "Failed to parse log facility, ignoring: %s", rvalue);
837 *o = (x << 3) | LOG_PRI(*o);
842 int config_parse_level(const char *unit,
843 const char *filename,
846 unsigned section_line,
861 x = log_level_from_string(rvalue);
863 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
864 "Failed to parse log level, ignoring: %s", rvalue);
868 *o = (*o & LOG_FACMASK) | x;
872 int config_parse_set_status(const char *unit,
873 const char *filename,
876 unsigned section_line,
887 ExitStatusSet *status_set = data;
894 if (isempty(rvalue)) {
895 /* Empty assignment resets the list */
897 set_free(status_set->signal);
898 set_free(status_set->code);
900 status_set->signal = status_set->code = NULL;
904 FOREACH_WORD(w, l, rvalue, state) {
908 temp = strndup(w, l);
912 r = safe_atoi(temp, &val);
914 val = signal_from_string_try_harder(temp);
918 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
922 r = set_put(status_set->signal, INT_TO_PTR(val));
924 log_syntax(unit, LOG_ERR, filename, line, -r,
925 "Unable to store: %s", w);
929 log_syntax(unit, LOG_ERR, filename, line, -val,
930 "Failed to parse value, ignoring: %s", w);
936 if (val < 0 || val > 255)
937 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
938 "Value %d is outside range 0-255, ignoring", val);
940 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
944 r = set_put(status_set->code, INT_TO_PTR(val));
946 log_syntax(unit, LOG_ERR, filename, line, -r,
947 "Unable to store: %s", w);