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