chiark / gitweb /
core: move config_parse_limit() to the generic conf-parser.[ch]
[elogind.git] / src / shared / conf-parser.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6 ***/
7
8 #include <errno.h>
9 #include <limits.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/types.h>
15
16 #include "alloc-util.h"
17 #include "conf-files.h"
18 #include "conf-parser.h"
19 #include "def.h"
20 #include "extract-word.h"
21 #include "fd-util.h"
22 #include "fileio.h"
23 #include "fs-util.h"
24 #include "log.h"
25 #include "macro.h"
26 #include "parse-util.h"
27 #include "path-util.h"
28 #include "process-util.h"
29 #include "signal-util.h"
30 #include "socket-util.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "syslog-util.h"
34 #include "time-util.h"
35 #include "utf8.h"
36
37 /// Additional includes needed by elogind
38 #include "def.h"
39 #include "fileio.h"
40 //#include "rlimit-util.h"
41
42 int config_item_table_lookup(
43                 const void *table,
44                 const char *section,
45                 const char *lvalue,
46                 ConfigParserCallback *func,
47                 int *ltype,
48                 void **data,
49                 void *userdata) {
50
51         const ConfigTableItem *t;
52
53         assert(table);
54         assert(lvalue);
55         assert(func);
56         assert(ltype);
57         assert(data);
58
59         for (t = table; t->lvalue; t++) {
60
61                 if (!streq(lvalue, t->lvalue))
62                         continue;
63
64                 if (!streq_ptr(section, t->section))
65                         continue;
66
67                 *func = t->parse;
68                 *ltype = t->ltype;
69                 *data = t->data;
70                 return 1;
71         }
72
73         return 0;
74 }
75
76 int config_item_perf_lookup(
77                 const void *table,
78                 const char *section,
79                 const char *lvalue,
80                 ConfigParserCallback *func,
81                 int *ltype,
82                 void **data,
83                 void *userdata) {
84
85         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
86         const ConfigPerfItem *p;
87
88         assert(table);
89         assert(lvalue);
90         assert(func);
91         assert(ltype);
92         assert(data);
93
94         if (!section)
95                 p = lookup(lvalue, strlen(lvalue));
96         else {
97                 char *key;
98
99                 key = strjoin(section, ".", lvalue);
100                 if (!key)
101                         return -ENOMEM;
102
103                 p = lookup(key, strlen(key));
104                 free(key);
105         }
106
107         if (!p)
108                 return 0;
109
110         *func = p->parse;
111         *ltype = p->ltype;
112         *data = (uint8_t*) userdata + p->offset;
113         return 1;
114 }
115
116 /* Run the user supplied parser for an assignment */
117 static int next_assignment(
118                 const char *unit,
119                 const char *filename,
120                 unsigned line,
121                 ConfigItemLookup lookup,
122                 const void *table,
123                 const char *section,
124                 unsigned section_line,
125                 const char *lvalue,
126                 const char *rvalue,
127                 ConfigParseFlags flags,
128                 void *userdata) {
129
130         ConfigParserCallback func = NULL;
131         int ltype = 0;
132         void *data = NULL;
133         int r;
134
135         assert(filename);
136         assert(line > 0);
137         assert(lookup);
138         assert(lvalue);
139         assert(rvalue);
140
141         r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
142         if (r < 0)
143                 return r;
144
145         if (r > 0) {
146                 if (func)
147                         return func(unit, filename, line, section, section_line,
148                                     lvalue, ltype, rvalue, data, userdata);
149
150                 return 0;
151         }
152
153         /* Warn about unknown non-extension fields. */
154         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
155                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
156
157         return 0;
158 }
159
160 /* Parse a single logical line */
161 static int parse_line(
162                 const char* unit,
163                 const char *filename,
164                 unsigned line,
165                 const char *sections,
166                 ConfigItemLookup lookup,
167                 const void *table,
168                 ConfigParseFlags flags,
169                 char **section,
170                 unsigned *section_line,
171                 bool *section_ignored,
172                 char *l,
173                 void *userdata) {
174
175         char *e, *include;
176
177         assert(filename);
178         assert(line > 0);
179         assert(lookup);
180         assert(l);
181
182         l = strstrip(l);
183         if (!*l)
184                 return 0;
185
186         if (strchr(COMMENTS "\n", *l))
187                 return 0;
188
189         include = first_word(l, ".include");
190         if (include) {
191                 _cleanup_free_ char *fn = NULL;
192
193                 /* .includes are a bad idea, we only support them here
194                  * for historical reasons. They create cyclic include
195                  * problems and make it difficult to detect
196                  * configuration file changes with an easy
197                  * stat(). Better approaches, such as .d/ drop-in
198                  * snippets exist.
199                  *
200                  * Support for them should be eventually removed. */
201
202                 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
203                         log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
204                         return 0;
205                 }
206
207                 log_syntax(unit, LOG_WARNING, filename, line, 0,
208                            ".include directives are deprecated, and support for them will be removed in a future version of elogind. "
209                            "Please use drop-in files instead.");
210
211                 fn = file_in_same_dir(filename, strstrip(include));
212                 if (!fn)
213                         return -ENOMEM;
214
215                 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
216         }
217
218         if (*l == '[') {
219                 size_t k;
220                 char *n;
221
222                 k = strlen(l);
223                 assert(k > 0);
224
225                 if (l[k-1] != ']') {
226                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
227                         return -EBADMSG;
228                 }
229
230                 n = strndup(l+1, k-2);
231                 if (!n)
232                         return -ENOMEM;
233
234                 if (sections && !nulstr_contains(sections, n)) {
235
236                         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
237                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
238
239                         free(n);
240                         *section = mfree(*section);
241                         *section_line = 0;
242                         *section_ignored = true;
243                 } else {
244                         free(*section);
245                         *section = n;
246                         *section_line = line;
247                         *section_ignored = false;
248                 }
249
250                 return 0;
251         }
252
253         if (sections && !*section) {
254
255                 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
256                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
257
258                 return 0;
259         }
260
261         e = strchr(l, '=');
262         if (!e) {
263                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
264                 return -EINVAL;
265         }
266
267         *e = 0;
268         e++;
269
270         return next_assignment(unit,
271                                filename,
272                                line,
273                                lookup,
274                                table,
275                                *section,
276                                *section_line,
277                                strstrip(l),
278                                strstrip(e),
279                                flags,
280                                userdata);
281 }
282
283 /* Go through the file and parse each line */
284 int config_parse(const char *unit,
285                  const char *filename,
286                  FILE *f,
287                  const char *sections,
288                  ConfigItemLookup lookup,
289                  const void *table,
290                  ConfigParseFlags flags,
291                  void *userdata) {
292
293         _cleanup_free_ char *section = NULL, *continuation = NULL;
294         _cleanup_fclose_ FILE *ours = NULL;
295         unsigned line = 0, section_line = 0;
296         bool section_ignored = false;
297         int r;
298
299         assert(filename);
300         assert(lookup);
301
302         if (!f) {
303                 f = ours = fopen(filename, "re");
304                 if (!f) {
305                         /* Only log on request, except for ENOENT,
306                          * since we return 0 to the caller. */
307                         if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
308                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
309                                          "Failed to open configuration file '%s': %m", filename);
310                         return errno == ENOENT ? 0 : -errno;
311                 }
312         }
313
314         fd_warn_permissions(filename, fileno(f));
315
316         for (;;) {
317                 _cleanup_free_ char *buf = NULL;
318                 bool escaped = false;
319                 char *l, *p, *e;
320
321                 r = read_line(f, LONG_LINE_MAX, &buf);
322                 if (r == 0)
323                         break;
324                 if (r == -ENOBUFS) {
325                         if (flags & CONFIG_PARSE_WARN)
326                                 log_error_errno(r, "%s:%u: Line too long", filename, line);
327
328                         return r;
329                 }
330                 if (r < 0) {
331                         if (CONFIG_PARSE_WARN)
332                                 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
333
334                         return r;
335                 }
336
337                 l = buf;
338                 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
339                         char *q;
340
341                         q = startswith(buf, UTF8_BYTE_ORDER_MARK);
342                         if (q) {
343                                 l = q;
344                                 flags |= CONFIG_PARSE_REFUSE_BOM;
345                         }
346                 }
347
348                 if (continuation) {
349                         if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
350                                 if (flags & CONFIG_PARSE_WARN)
351                                         log_error("%s:%u: Continuation line too long", filename, line);
352                                 return -ENOBUFS;
353                         }
354
355                         if (!strextend(&continuation, l, NULL)) {
356                                 if (flags & CONFIG_PARSE_WARN)
357                                         log_oom();
358                                 return -ENOMEM;
359                         }
360
361                         p = continuation;
362                 } else
363                         p = l;
364
365                 for (e = p; *e; e++) {
366                         if (escaped)
367                                 escaped = false;
368                         else if (*e == '\\')
369                                 escaped = true;
370                 }
371
372                 if (escaped) {
373                         *(e-1) = ' ';
374
375                         if (!continuation) {
376                                 continuation = strdup(l);
377                                 if (!continuation) {
378                                         if (flags & CONFIG_PARSE_WARN)
379                                                 log_oom();
380                                         return -ENOMEM;
381                                 }
382                         }
383
384                         continue;
385                 }
386
387                 r = parse_line(unit,
388                                filename,
389                                ++line,
390                                sections,
391                                lookup,
392                                table,
393                                flags,
394                                &section,
395                                &section_line,
396                                &section_ignored,
397                                p,
398                                userdata);
399                 if (r < 0) {
400                         if (flags & CONFIG_PARSE_WARN)
401                                 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
402                         return r;
403
404                 }
405
406                 continuation = mfree(continuation);
407         }
408
409         if (continuation) {
410                 r = parse_line(unit,
411                                filename,
412                                ++line,
413                                sections,
414                                lookup,
415                                table,
416                                flags,
417                                &section,
418                                &section_line,
419                                &section_ignored,
420                                continuation,
421                                userdata);
422                 if (r < 0) {
423                         if (flags & CONFIG_PARSE_WARN)
424                                 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
425                         return r;
426
427                 }
428         }
429
430         return 0;
431 }
432
433 static int config_parse_many_files(
434                 const char *conf_file,
435                 char **files,
436                 const char *sections,
437                 ConfigItemLookup lookup,
438                 const void *table,
439                 ConfigParseFlags flags,
440                 void *userdata) {
441
442         char **fn;
443         int r;
444
445         if (conf_file) {
446                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
447                 if (r < 0)
448                         return r;
449         }
450
451         STRV_FOREACH(fn, files) {
452                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
453                 if (r < 0)
454                         return r;
455         }
456
457         return 0;
458 }
459
460 /* Parse each config file in the directories specified as nulstr. */
461 int config_parse_many_nulstr(
462                 const char *conf_file,
463                 const char *conf_file_dirs,
464                 const char *sections,
465                 ConfigItemLookup lookup,
466                 const void *table,
467                 ConfigParseFlags flags,
468                 void *userdata) {
469
470         _cleanup_strv_free_ char **files = NULL;
471         int r;
472
473         r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
474         if (r < 0)
475                 return r;
476
477         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
478 }
479
480 #if 0 /// UNNEEDED by elogind
481 /* Parse each config file in the directories specified as strv. */
482 int config_parse_many(
483                 const char *conf_file,
484                 const char* const* conf_file_dirs,
485                 const char *dropin_dirname,
486                 const char *sections,
487                 ConfigItemLookup lookup,
488                 const void *table,
489                 ConfigParseFlags flags,
490                 void *userdata) {
491
492         _cleanup_strv_free_ char **dropin_dirs = NULL;
493         _cleanup_strv_free_ char **files = NULL;
494         const char *suffix;
495         int r;
496
497         suffix = strjoina("/", dropin_dirname);
498         r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
499         if (r < 0)
500                 return r;
501
502         r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
503         if (r < 0)
504                 return r;
505
506         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
507 }
508 #endif // 0
509
510 #define DEFINE_PARSER(type, vartype, conv_func)                         \
511         int config_parse_##type(                                        \
512                         const char *unit,                               \
513                         const char *filename,                           \
514                         unsigned line,                                  \
515                         const char *section,                            \
516                         unsigned section_line,                          \
517                         const char *lvalue,                             \
518                         int ltype,                                      \
519                         const char *rvalue,                             \
520                         void *data,                                     \
521                         void *userdata) {                               \
522                                                                         \
523                 vartype *i = data;                                      \
524                 int r;                                                  \
525                                                                         \
526                 assert(filename);                                       \
527                 assert(lvalue);                                         \
528                 assert(rvalue);                                         \
529                 assert(data);                                           \
530                                                                         \
531                 r = conv_func(rvalue, i);                               \
532                 if (r < 0)                                              \
533                         log_syntax(unit, LOG_ERR, filename, line, r,    \
534                                    "Failed to parse %s value, ignoring: %s", \
535                                    #type, rvalue);                      \
536                                                                         \
537                 return 0;                                               \
538         }
539
540 DEFINE_PARSER(int, int, safe_atoi);
541 DEFINE_PARSER(long, long, safe_atoli);
542 #if 0 /// UNNEEDED by elogind
543 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
544 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
545 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
546 #endif // 0
547 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
548 DEFINE_PARSER(unsigned, unsigned, safe_atou);
549 DEFINE_PARSER(double, double, safe_atod);
550 #if 0 /// UNNEEDED by elogind
551 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
552 #endif // 0
553 DEFINE_PARSER(sec, usec_t, parse_sec);
554 DEFINE_PARSER(mode, mode_t, parse_mode);
555
556 int config_parse_iec_size(const char* unit,
557                             const char *filename,
558                             unsigned line,
559                             const char *section,
560                             unsigned section_line,
561                             const char *lvalue,
562                             int ltype,
563                             const char *rvalue,
564                             void *data,
565                             void *userdata) {
566
567         size_t *sz = data;
568         uint64_t v;
569         int r;
570
571         assert(filename);
572         assert(lvalue);
573         assert(rvalue);
574         assert(data);
575
576         r = parse_size(rvalue, 1024, &v);
577         if (r < 0 || (uint64_t) (size_t) v != v) {
578                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
579                 return 0;
580         }
581
582         *sz = (size_t) v;
583         return 0;
584 }
585
586 #if 0 /// UNNEEDED by elogind
587 int config_parse_si_size(
588                 const char* unit,
589                 const char *filename,
590                 unsigned line,
591                 const char *section,
592                 unsigned section_line,
593                 const char *lvalue,
594                 int ltype,
595                 const char *rvalue,
596                 void *data,
597                 void *userdata) {
598
599         size_t *sz = data;
600         uint64_t v;
601         int r;
602
603         assert(filename);
604         assert(lvalue);
605         assert(rvalue);
606         assert(data);
607
608         r = parse_size(rvalue, 1000, &v);
609         if (r < 0 || (uint64_t) (size_t) v != v) {
610                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
611                 return 0;
612         }
613
614         *sz = (size_t) v;
615         return 0;
616 }
617
618 int config_parse_iec_uint64(
619                 const char* unit,
620                 const char *filename,
621                 unsigned line,
622                 const char *section,
623                 unsigned section_line,
624                 const char *lvalue,
625                 int ltype,
626                 const char *rvalue,
627                 void *data,
628                 void *userdata) {
629
630         uint64_t *bytes = data;
631         int r;
632
633         assert(filename);
634         assert(lvalue);
635         assert(rvalue);
636         assert(data);
637
638         r = parse_size(rvalue, 1024, bytes);
639         if (r < 0)
640                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
641
642         return 0;
643 }
644 #endif // 0
645
646 int config_parse_bool(const char* unit,
647                       const char *filename,
648                       unsigned line,
649                       const char *section,
650                       unsigned section_line,
651                       const char *lvalue,
652                       int ltype,
653                       const char *rvalue,
654                       void *data,
655                       void *userdata) {
656
657         int k;
658         bool *b = data;
659         bool fatal = ltype;
660
661         assert(filename);
662         assert(lvalue);
663         assert(rvalue);
664         assert(data);
665
666         k = parse_boolean(rvalue);
667         if (k < 0) {
668                 log_syntax(unit, LOG_ERR, filename, line, k,
669                            "Failed to parse boolean value%s: %s",
670                            fatal ? "" : ", ignoring", rvalue);
671                 return fatal ? -ENOEXEC : 0;
672         }
673
674         *b = !!k;
675         return 0;
676 }
677
678 #if 0 /// UNNEEDED by elogind
679 int config_parse_tristate(
680                 const char* unit,
681                 const char *filename,
682                 unsigned line,
683                 const char *section,
684                 unsigned section_line,
685                 const char *lvalue,
686                 int ltype,
687                 const char *rvalue,
688                 void *data,
689                 void *userdata) {
690
691         int k, *t = data;
692
693         assert(filename);
694         assert(lvalue);
695         assert(rvalue);
696         assert(data);
697
698         /* A tristate is pretty much a boolean, except that it can
699          * also take the special value -1, indicating "uninitialized",
700          * much like NULL is for a pointer type. */
701
702         k = parse_boolean(rvalue);
703         if (k < 0) {
704                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
705                 return 0;
706         }
707
708         *t = !!k;
709         return 0;
710 }
711 #endif // 0
712
713 int config_parse_string(
714                 const char *unit,
715                 const char *filename,
716                 unsigned line,
717                 const char *section,
718                 unsigned section_line,
719                 const char *lvalue,
720                 int ltype,
721                 const char *rvalue,
722                 void *data,
723                 void *userdata) {
724
725         char **s = data, *n;
726
727         assert(filename);
728         assert(lvalue);
729         assert(rvalue);
730         assert(data);
731
732         if (!utf8_is_valid(rvalue)) {
733                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
734                 return 0;
735         }
736
737         if (isempty(rvalue))
738                 n = NULL;
739         else {
740                 n = strdup(rvalue);
741                 if (!n)
742                         return log_oom();
743         }
744
745         free(*s);
746         *s = n;
747
748         return 0;
749 }
750
751 int config_parse_path(
752                 const char *unit,
753                 const char *filename,
754                 unsigned line,
755                 const char *section,
756                 unsigned section_line,
757                 const char *lvalue,
758                 int ltype,
759                 const char *rvalue,
760                 void *data,
761                 void *userdata) {
762
763         char **s = data, *n;
764         bool fatal = ltype;
765
766         assert(filename);
767         assert(lvalue);
768         assert(rvalue);
769         assert(data);
770
771         if (isempty(rvalue)) {
772                 n = NULL;
773                 goto finalize;
774         }
775
776         if (!utf8_is_valid(rvalue)) {
777                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
778                 return fatal ? -ENOEXEC : 0;
779         }
780
781         if (!path_is_absolute(rvalue)) {
782                 log_syntax(unit, LOG_ERR, filename, line, 0,
783                            "Not an absolute path%s: %s",
784                            fatal ? "" : ", ignoring", rvalue);
785                 return fatal ? -ENOEXEC : 0;
786         }
787
788         n = strdup(rvalue);
789         if (!n)
790                 return log_oom();
791
792         path_kill_slashes(n);
793
794 finalize:
795         free(*s);
796         *s = n;
797
798         return 0;
799 }
800
801 int config_parse_strv(
802                 const char *unit,
803                 const char *filename,
804                 unsigned line,
805                 const char *section,
806                 unsigned section_line,
807                 const char *lvalue,
808                 int ltype,
809                 const char *rvalue,
810                 void *data,
811                 void *userdata) {
812
813         char ***sv = data;
814         int r;
815
816         assert(filename);
817         assert(lvalue);
818         assert(rvalue);
819         assert(data);
820
821         if (isempty(rvalue)) {
822                 *sv = strv_free(*sv);
823                 return 0;
824         }
825
826         for (;;) {
827                 char *word = NULL;
828
829                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
830                 if (r == 0)
831                         break;
832                 if (r == -ENOMEM)
833                         return log_oom();
834                 if (r < 0) {
835                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
836                         break;
837                 }
838
839                 if (!utf8_is_valid(word)) {
840                         log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
841                         free(word);
842                         continue;
843                 }
844
845                 r = strv_consume(sv, word);
846                 if (r < 0)
847                         return log_oom();
848         }
849
850         return 0;
851 }
852
853 int config_parse_warn_compat(
854                 const char *unit,
855                 const char *filename,
856                 unsigned line,
857                 const char *section,
858                 unsigned section_line,
859                 const char *lvalue,
860                 int ltype,
861                 const char *rvalue,
862                 void *data,
863                 void *userdata) {
864         Disabled reason = ltype;
865
866         switch(reason) {
867         case DISABLED_CONFIGURATION:
868                 log_syntax(unit, LOG_DEBUG, filename, line, 0,
869                            "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
870                 break;
871         case DISABLED_LEGACY:
872                 log_syntax(unit, LOG_INFO, filename, line, 0,
873                            "Support for option %s= has been removed and it is ignored", lvalue);
874                 break;
875         case DISABLED_EXPERIMENTAL:
876                 log_syntax(unit, LOG_INFO, filename, line, 0,
877                            "Support for option %s= has not yet been enabled and it is ignored", lvalue);
878                 break;
879         };
880
881         return 0;
882 }
883
884 #if 0 /// UNNEEDED by elogind
885 int config_parse_log_facility(
886                 const char *unit,
887                 const char *filename,
888                 unsigned line,
889                 const char *section,
890                 unsigned section_line,
891                 const char *lvalue,
892                 int ltype,
893                 const char *rvalue,
894                 void *data,
895                 void *userdata) {
896
897         int *o = data, x;
898
899         assert(filename);
900         assert(lvalue);
901         assert(rvalue);
902         assert(data);
903
904         x = log_facility_unshifted_from_string(rvalue);
905         if (x < 0) {
906                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
907                 return 0;
908         }
909
910         *o = (x << 3) | LOG_PRI(*o);
911
912         return 0;
913 }
914 #endif // 0
915
916 int config_parse_log_level(
917                 const char *unit,
918                 const char *filename,
919                 unsigned line,
920                 const char *section,
921                 unsigned section_line,
922                 const char *lvalue,
923                 int ltype,
924                 const char *rvalue,
925                 void *data,
926                 void *userdata) {
927
928         int *o = data, x;
929
930         assert(filename);
931         assert(lvalue);
932         assert(rvalue);
933         assert(data);
934
935         x = log_level_from_string(rvalue);
936         if (x < 0) {
937                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
938                 return 0;
939         }
940
941         if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
942                 *o = x;
943         else
944                 *o = (*o & LOG_FACMASK) | x;
945
946         return 0;
947 }
948
949 int config_parse_signal(
950                 const char *unit,
951                 const char *filename,
952                 unsigned line,
953                 const char *section,
954                 unsigned section_line,
955                 const char *lvalue,
956                 int ltype,
957                 const char *rvalue,
958                 void *data,
959                 void *userdata) {
960
961         int *sig = data, r;
962
963         assert(filename);
964         assert(lvalue);
965         assert(rvalue);
966         assert(sig);
967
968         r = signal_from_string(rvalue);
969         if (r <= 0) {
970                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
971                 return 0;
972         }
973
974         *sig = r;
975         return 0;
976 }
977
978 #if 0 /// UNNEEDED by elogind
979 int config_parse_personality(
980                 const char *unit,
981                 const char *filename,
982                 unsigned line,
983                 const char *section,
984                 unsigned section_line,
985                 const char *lvalue,
986                 int ltype,
987                 const char *rvalue,
988                 void *data,
989                 void *userdata) {
990
991         unsigned long *personality = data, p;
992
993         assert(filename);
994         assert(lvalue);
995         assert(rvalue);
996         assert(personality);
997
998         if (isempty(rvalue))
999                 p = PERSONALITY_INVALID;
1000         else {
1001                 p = personality_from_string(rvalue);
1002                 if (p == PERSONALITY_INVALID) {
1003                         log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
1004                         return 0;
1005                 }
1006         }
1007
1008         *personality = p;
1009         return 0;
1010 }
1011
1012 int config_parse_ifname(
1013                 const char *unit,
1014                 const char *filename,
1015                 unsigned line,
1016                 const char *section,
1017                 unsigned section_line,
1018                 const char *lvalue,
1019                 int ltype,
1020                 const char *rvalue,
1021                 void *data,
1022                 void *userdata) {
1023
1024         char **s = data;
1025         int r;
1026
1027         assert(filename);
1028         assert(lvalue);
1029         assert(rvalue);
1030         assert(data);
1031
1032         if (isempty(rvalue)) {
1033                 *s = mfree(*s);
1034                 return 0;
1035         }
1036
1037         if (!ifname_valid(rvalue)) {
1038                 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1039                 return 0;
1040         }
1041
1042         r = free_and_strdup(s, rvalue);
1043         if (r < 0)
1044                 return log_oom();
1045
1046         return 0;
1047 }
1048
1049 int config_parse_ip_port(
1050                 const char *unit,
1051                 const char *filename,
1052                 unsigned line,
1053                 const char *section,
1054                 unsigned section_line,
1055                 const char *lvalue,
1056                 int ltype,
1057                 const char *rvalue,
1058                 void *data,
1059                 void *userdata) {
1060
1061         uint16_t *s = data;
1062         uint16_t port;
1063         int r;
1064
1065         assert(filename);
1066         assert(lvalue);
1067         assert(rvalue);
1068         assert(data);
1069
1070         if (isempty(rvalue)) {
1071                 *s = 0;
1072                 return 0;
1073         }
1074
1075         r = parse_ip_port(rvalue, &port);
1076         if (r < 0) {
1077                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1078                 return 0;
1079         }
1080
1081         *s = port;
1082
1083         return 0;
1084 }
1085
1086 int config_parse_join_controllers(
1087                 const char *unit,
1088                 const char *filename,
1089                 unsigned line,
1090                 const char *section,
1091                 unsigned section_line,
1092                 const char *lvalue,
1093                 int ltype,
1094                 const char *rvalue,
1095                 void *data,
1096                 void *userdata) {
1097
1098         char ****ret = data;
1099         const char *whole_rvalue = rvalue;
1100         unsigned n = 0;
1101         _cleanup_(strv_free_freep) char ***controllers = NULL;
1102
1103         assert(filename);
1104         assert(lvalue);
1105         assert(rvalue);
1106         assert(ret);
1107
1108         for (;;) {
1109                 _cleanup_free_ char *word = NULL;
1110                 char **l;
1111                 int r;
1112
1113                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1114                 if (r < 0) {
1115                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1116                         return r;
1117                 }
1118                 if (r == 0)
1119                         break;
1120
1121                 l = strv_split(word, ",");
1122                 if (!l)
1123                         return log_oom();
1124                 strv_uniq(l);
1125
1126                 if (strv_length(l) <= 1) {
1127                         strv_free(l);
1128                         continue;
1129                 }
1130
1131                 if (!controllers) {
1132                         controllers = new(char**, 2);
1133                         if (!controllers) {
1134                                 strv_free(l);
1135                                 return log_oom();
1136                         }
1137
1138                         controllers[0] = l;
1139                         controllers[1] = NULL;
1140
1141                         n = 1;
1142                 } else {
1143                         char ***a;
1144                         char ***t;
1145
1146                         t = new0(char**, n+2);
1147                         if (!t) {
1148                                 strv_free(l);
1149                                 return log_oom();
1150                         }
1151
1152                         n = 0;
1153
1154                         for (a = controllers; *a; a++)
1155                                 if (strv_overlap(*a, l)) {
1156                                         if (strv_extend_strv(&l, *a, false) < 0) {
1157                                                 strv_free(l);
1158                                                 strv_free_free(t);
1159                                                 return log_oom();
1160                                         }
1161
1162                                 } else {
1163                                         char **c;
1164
1165                                         c = strv_copy(*a);
1166                                         if (!c) {
1167                                                 strv_free(l);
1168                                                 strv_free_free(t);
1169                                                 return log_oom();
1170                                         }
1171
1172                                         t[n++] = c;
1173                                 }
1174
1175                         t[n++] = strv_uniq(l);
1176
1177                         strv_free_free(controllers);
1178                         controllers = t;
1179                 }
1180         }
1181         if (!isempty(rvalue))
1182                 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1183
1184         /* As a special case, return a single empty strv, to override the default */
1185         if (!controllers) {
1186                 controllers = new(char**, 2);
1187                 if (!controllers)
1188                         return log_oom();
1189                 controllers[0] = strv_new(NULL, NULL);
1190                 if (!controllers[0])
1191                         return log_oom();
1192                 controllers[1] = NULL;
1193         }
1194
1195         strv_free_free(*ret);
1196         *ret = TAKE_PTR(controllers);
1197
1198         return 0;
1199 }
1200 #endif // 0
1201
1202 int config_parse_mtu(
1203                 const char *unit,
1204                 const char *filename,
1205                 unsigned line,
1206                 const char *section,
1207                 unsigned section_line,
1208                 const char *lvalue,
1209                 int ltype,
1210                 const char *rvalue,
1211                 void *data,
1212                 void *userdata) {
1213
1214         uint32_t *mtu = data;
1215         int r;
1216
1217         assert(rvalue);
1218         assert(mtu);
1219
1220         r = parse_mtu(ltype, rvalue, mtu);
1221         if (r == -ERANGE) {
1222                 log_syntax(unit, LOG_ERR, filename, line, r,
1223                            "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s",
1224                            (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX,
1225                            rvalue);
1226                 return 0;
1227         }
1228         if (r < 0) {
1229                 log_syntax(unit, LOG_ERR, filename, line, r,
1230                            "Failed to parse MTU value '%s', ignoring: %m", rvalue);
1231                 return 0;
1232         }
1233
1234         return 0;
1235 }
1236
1237 int config_parse_rlimit(
1238                 const char *unit,
1239                 const char *filename,
1240                 unsigned line,
1241                 const char *section,
1242                 unsigned section_line,
1243                 const char *lvalue,
1244                 int ltype,
1245                 const char *rvalue,
1246                 void *data,
1247                 void *userdata) {
1248
1249         struct rlimit **rl = data, d = {};
1250         int r;
1251
1252         assert(rvalue);
1253         assert(rl);
1254
1255         r = rlimit_parse(ltype, rvalue, &d);
1256         if (r == -EILSEQ) {
1257                 log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
1258                 return 0;
1259         }
1260         if (r < 0) {
1261                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
1262                 return 0;
1263         }
1264
1265         if (rl[ltype])
1266                 *rl[ltype] = d;
1267         else {
1268                 rl[ltype] = newdup(struct rlimit, &d, 1);
1269                 if (!rl[ltype])
1270                         return log_oom();
1271         }
1272
1273         return 0;
1274 }