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