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