chiark / gitweb /
tree-wide: drop !! casts to booleans
[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 //#include "rlimit-util.h"
53 //#include "rlimit-util.h"
54
55 int config_item_table_lookup(
56                 const void *table,
57                 const char *section,
58                 const char *lvalue,
59                 ConfigParserCallback *func,
60                 int *ltype,
61                 void **data,
62                 void *userdata) {
63
64         const ConfigTableItem *t;
65
66         assert(table);
67         assert(lvalue);
68         assert(func);
69         assert(ltype);
70         assert(data);
71
72         for (t = table; t->lvalue; t++) {
73
74                 if (!streq(lvalue, t->lvalue))
75                         continue;
76
77                 if (!streq_ptr(section, t->section))
78                         continue;
79
80                 *func = t->parse;
81                 *ltype = t->ltype;
82                 *data = t->data;
83                 return 1;
84         }
85
86         return 0;
87 }
88
89 int config_item_perf_lookup(
90                 const void *table,
91                 const char *section,
92                 const char *lvalue,
93                 ConfigParserCallback *func,
94                 int *ltype,
95                 void **data,
96                 void *userdata) {
97
98         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
99         const ConfigPerfItem *p;
100
101         assert(table);
102         assert(lvalue);
103         assert(func);
104         assert(ltype);
105         assert(data);
106
107         if (!section)
108                 p = lookup(lvalue, strlen(lvalue));
109         else {
110                 char *key;
111
112                 key = strjoin(section, ".", lvalue);
113                 if (!key)
114                         return -ENOMEM;
115
116                 p = lookup(key, strlen(key));
117                 free(key);
118         }
119
120         if (!p)
121                 return 0;
122
123         *func = p->parse;
124         *ltype = p->ltype;
125         *data = (uint8_t*) userdata + p->offset;
126         return 1;
127 }
128
129 /* Run the user supplied parser for an assignment */
130 static int next_assignment(
131                 const char *unit,
132                 const char *filename,
133                 unsigned line,
134                 ConfigItemLookup lookup,
135                 const void *table,
136                 const char *section,
137                 unsigned section_line,
138                 const char *lvalue,
139                 const char *rvalue,
140                 ConfigParseFlags flags,
141                 void *userdata) {
142
143         ConfigParserCallback func = NULL;
144         int ltype = 0;
145         void *data = NULL;
146         int r;
147
148         assert(filename);
149         assert(line > 0);
150         assert(lookup);
151         assert(lvalue);
152         assert(rvalue);
153
154         r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
155         if (r < 0)
156                 return r;
157
158         if (r > 0) {
159                 if (func)
160                         return func(unit, filename, line, section, section_line,
161                                     lvalue, ltype, rvalue, data, userdata);
162
163                 return 0;
164         }
165
166         /* Warn about unknown non-extension fields. */
167         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
168                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
169
170         return 0;
171 }
172
173 /* Parse a single logical line */
174 static int parse_line(
175                 const char* unit,
176                 const char *filename,
177                 unsigned line,
178                 const char *sections,
179                 ConfigItemLookup lookup,
180                 const void *table,
181                 ConfigParseFlags flags,
182                 char **section,
183                 unsigned *section_line,
184                 bool *section_ignored,
185                 char *l,
186                 void *userdata) {
187
188         char *e, *include;
189
190         assert(filename);
191         assert(line > 0);
192         assert(lookup);
193         assert(l);
194
195         l = strstrip(l);
196         if (!*l)
197                 return 0;
198
199         if (strchr(COMMENTS "\n", *l))
200                 return 0;
201
202         include = first_word(l, ".include");
203         if (include) {
204                 _cleanup_free_ char *fn = NULL;
205
206                 /* .includes are a bad idea, we only support them here
207                  * for historical reasons. They create cyclic include
208                  * problems and make it difficult to detect
209                  * configuration file changes with an easy
210                  * stat(). Better approaches, such as .d/ drop-in
211                  * snippets exist.
212                  *
213                  * Support for them should be eventually removed. */
214
215                 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
216                         log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
217                         return 0;
218                 }
219
220                 log_syntax(unit, LOG_WARNING, filename, line, 0,
221                            ".include directives are deprecated, and support for them will be removed in a future version of elogind. "
222                            "Please use drop-in files instead.");
223
224                 fn = file_in_same_dir(filename, strstrip(include));
225                 if (!fn)
226                         return -ENOMEM;
227
228                 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
229         }
230
231         if (!utf8_is_valid(l))
232                 return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l);
233
234         if (*l == '[') {
235                 size_t k;
236                 char *n;
237
238                 k = strlen(l);
239                 assert(k > 0);
240
241                 if (l[k-1] != ']') {
242                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
243                         return -EBADMSG;
244                 }
245
246                 n = strndup(l+1, k-2);
247                 if (!n)
248                         return -ENOMEM;
249
250                 if (sections && !nulstr_contains(sections, n)) {
251
252                         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
253                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
254
255                         free(n);
256                         *section = mfree(*section);
257                         *section_line = 0;
258                         *section_ignored = true;
259                 } else {
260                         free_and_replace(*section, n);
261                         *section_line = line;
262                         *section_ignored = false;
263                 }
264
265                 return 0;
266         }
267
268         if (sections && !*section) {
269
270                 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
271                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
272
273                 return 0;
274         }
275
276         e = strchr(l, '=');
277         if (!e) {
278                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
279                 return -EINVAL;
280         }
281
282         *e = 0;
283         e++;
284
285         return next_assignment(unit,
286                                filename,
287                                line,
288                                lookup,
289                                table,
290                                *section,
291                                *section_line,
292                                strstrip(l),
293                                strstrip(e),
294                                flags,
295                                userdata);
296 }
297
298 /* Go through the file and parse each line */
299 int config_parse(const char *unit,
300                  const char *filename,
301                  FILE *f,
302                  const char *sections,
303                  ConfigItemLookup lookup,
304                  const void *table,
305                  ConfigParseFlags flags,
306                  void *userdata) {
307
308         _cleanup_free_ char *section = NULL, *continuation = NULL;
309         _cleanup_fclose_ FILE *ours = NULL;
310         unsigned line = 0, section_line = 0;
311         bool section_ignored = false;
312         int r;
313
314         assert(filename);
315         assert(lookup);
316
317         if (!f) {
318                 f = ours = fopen(filename, "re");
319                 if (!f) {
320                         /* Only log on request, except for ENOENT,
321                          * since we return 0 to the caller. */
322                         if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
323                                 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
324                                                "Failed to open configuration file '%s': %m", filename);
325                         return errno == ENOENT ? 0 : -errno;
326                 }
327         }
328
329         fd_warn_permissions(filename, fileno(f));
330
331         for (;;) {
332                 _cleanup_free_ char *buf = NULL;
333                 bool escaped = false;
334                 char *l, *p, *e;
335
336                 r = read_line(f, LONG_LINE_MAX, &buf);
337                 if (r == 0)
338                         break;
339                 if (r == -ENOBUFS) {
340                         if (flags & CONFIG_PARSE_WARN)
341                                 log_error_errno(r, "%s:%u: Line too long", filename, line);
342
343                         return r;
344                 }
345                 if (r < 0) {
346                         if (CONFIG_PARSE_WARN)
347                                 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
348
349                         return r;
350                 }
351
352                 l = buf;
353                 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
354                         char *q;
355
356                         q = startswith(buf, UTF8_BYTE_ORDER_MARK);
357                         if (q) {
358                                 l = q;
359                                 flags |= CONFIG_PARSE_REFUSE_BOM;
360                         }
361                 }
362
363                 if (continuation) {
364                         if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
365                                 if (flags & CONFIG_PARSE_WARN)
366                                         log_error("%s:%u: Continuation line too long", filename, line);
367                                 return -ENOBUFS;
368                         }
369
370                         if (!strextend(&continuation, l, NULL)) {
371                                 if (flags & CONFIG_PARSE_WARN)
372                                         log_oom();
373                                 return -ENOMEM;
374                         }
375
376                         p = continuation;
377                 } else
378                         p = l;
379
380                 for (e = p; *e; e++) {
381                         if (escaped)
382                                 escaped = false;
383                         else if (*e == '\\')
384                                 escaped = true;
385                 }
386
387                 if (escaped) {
388                         *(e-1) = ' ';
389
390                         if (!continuation) {
391                                 continuation = strdup(l);
392                                 if (!continuation) {
393                                         if (flags & CONFIG_PARSE_WARN)
394                                                 log_oom();
395                                         return -ENOMEM;
396                                 }
397                         }
398
399                         continue;
400                 }
401
402                 r = parse_line(unit,
403                                filename,
404                                ++line,
405                                sections,
406                                lookup,
407                                table,
408                                flags,
409                                &section,
410                                &section_line,
411                                &section_ignored,
412                                p,
413                                userdata);
414                 if (r < 0) {
415                         if (flags & CONFIG_PARSE_WARN)
416                                 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
417                         return r;
418                 }
419
420                 continuation = mfree(continuation);
421         }
422
423         if (continuation) {
424                 r = parse_line(unit,
425                                filename,
426                                ++line,
427                                sections,
428                                lookup,
429                                table,
430                                flags,
431                                &section,
432                                &section_line,
433                                &section_ignored,
434                                continuation,
435                                userdata);
436                 if (r < 0) {
437                         if (flags & CONFIG_PARSE_WARN)
438                                 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
439                         return r;
440                 }
441         }
442
443         return 0;
444 }
445
446 static int config_parse_many_files(
447                 const char *conf_file,
448                 char **files,
449                 const char *sections,
450                 ConfigItemLookup lookup,
451                 const void *table,
452                 ConfigParseFlags flags,
453                 void *userdata) {
454
455         char **fn;
456         int r;
457
458         if (conf_file) {
459                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
460                 if (r < 0)
461                         return r;
462         }
463
464         STRV_FOREACH(fn, files) {
465                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
466                 if (r < 0)
467                         return r;
468         }
469
470         return 0;
471 }
472
473 /* Parse each config file in the directories specified as nulstr. */
474 int config_parse_many_nulstr(
475                 const char *conf_file,
476                 const char *conf_file_dirs,
477                 const char *sections,
478                 ConfigItemLookup lookup,
479                 const void *table,
480                 ConfigParseFlags flags,
481                 void *userdata) {
482
483         _cleanup_strv_free_ char **files = NULL;
484         int r;
485
486         r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
487         if (r < 0)
488                 return r;
489
490         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
491 }
492
493 #if 0 /// UNNEEDED by elogind
494 /* Parse each config file in the directories specified as strv. */
495 int config_parse_many(
496                 const char *conf_file,
497                 const char* const* conf_file_dirs,
498                 const char *dropin_dirname,
499                 const char *sections,
500                 ConfigItemLookup lookup,
501                 const void *table,
502                 ConfigParseFlags flags,
503                 void *userdata) {
504
505         _cleanup_strv_free_ char **dropin_dirs = NULL;
506         _cleanup_strv_free_ char **files = NULL;
507         const char *suffix;
508         int r;
509
510         suffix = strjoina("/", dropin_dirname);
511         r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
512         if (r < 0)
513                 return r;
514
515         r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
516         if (r < 0)
517                 return r;
518
519         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
520 }
521 #endif // 0
522
523 #define DEFINE_PARSER(type, vartype, conv_func)                         \
524         DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value")
525
526 DEFINE_PARSER(int, int, safe_atoi);
527 DEFINE_PARSER(long, long, safe_atoli);
528 #if 0 /// UNNEEDED by elogind
529 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
530 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
531 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
532 #endif // 0
533 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
534 DEFINE_PARSER(unsigned, unsigned, safe_atou);
535 DEFINE_PARSER(double, double, safe_atod);
536 #if 0 /// UNNEEDED by elogind
537 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
538 #endif // 0
539 DEFINE_PARSER(sec, usec_t, parse_sec);
540 DEFINE_PARSER(mode, mode_t, parse_mode);
541
542 int config_parse_iec_size(const char* unit,
543                             const char *filename,
544                             unsigned line,
545                             const char *section,
546                             unsigned section_line,
547                             const char *lvalue,
548                             int ltype,
549                             const char *rvalue,
550                             void *data,
551                             void *userdata) {
552
553         size_t *sz = data;
554         uint64_t v;
555         int r;
556
557         assert(filename);
558         assert(lvalue);
559         assert(rvalue);
560         assert(data);
561
562         r = parse_size(rvalue, 1024, &v);
563         if (r < 0 || (uint64_t) (size_t) v != v) {
564                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
565                 return 0;
566         }
567
568         *sz = (size_t) v;
569         return 0;
570 }
571
572 #if 0 /// UNNEEDED by elogind
573 int config_parse_si_size(
574                 const char* unit,
575                 const char *filename,
576                 unsigned line,
577                 const char *section,
578                 unsigned section_line,
579                 const char *lvalue,
580                 int ltype,
581                 const char *rvalue,
582                 void *data,
583                 void *userdata) {
584
585         size_t *sz = data;
586         uint64_t v;
587         int r;
588
589         assert(filename);
590         assert(lvalue);
591         assert(rvalue);
592         assert(data);
593
594         r = parse_size(rvalue, 1000, &v);
595         if (r < 0 || (uint64_t) (size_t) v != v) {
596                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
597                 return 0;
598         }
599
600         *sz = (size_t) v;
601         return 0;
602 }
603
604 int config_parse_iec_uint64(
605                 const char* unit,
606                 const char *filename,
607                 unsigned line,
608                 const char *section,
609                 unsigned section_line,
610                 const char *lvalue,
611                 int ltype,
612                 const char *rvalue,
613                 void *data,
614                 void *userdata) {
615
616         uint64_t *bytes = data;
617         int r;
618
619         assert(filename);
620         assert(lvalue);
621         assert(rvalue);
622         assert(data);
623
624         r = parse_size(rvalue, 1024, bytes);
625         if (r < 0)
626                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
627
628         return 0;
629 }
630 #endif // 0
631
632 int config_parse_bool(const char* unit,
633                       const char *filename,
634                       unsigned line,
635                       const char *section,
636                       unsigned section_line,
637                       const char *lvalue,
638                       int ltype,
639                       const char *rvalue,
640                       void *data,
641                       void *userdata) {
642
643         int k;
644         bool *b = data;
645         bool fatal = ltype;
646
647         assert(filename);
648         assert(lvalue);
649         assert(rvalue);
650         assert(data);
651
652         k = parse_boolean(rvalue);
653         if (k < 0) {
654                 log_syntax(unit, LOG_ERR, filename, line, k,
655                            "Failed to parse boolean value%s: %s",
656                            fatal ? "" : ", ignoring", rvalue);
657                 return fatal ? -ENOEXEC : 0;
658         }
659
660         *b = k;
661         return 0;
662 }
663
664 #if 0 /// UNNEEDED by elogind
665 int config_parse_tristate(
666                 const char* unit,
667                 const char *filename,
668                 unsigned line,
669                 const char *section,
670                 unsigned section_line,
671                 const char *lvalue,
672                 int ltype,
673                 const char *rvalue,
674                 void *data,
675                 void *userdata) {
676
677         int k, *t = data;
678
679         assert(filename);
680         assert(lvalue);
681         assert(rvalue);
682         assert(data);
683
684         /* A tristate is pretty much a boolean, except that it can
685          * also take the special value -1, indicating "uninitialized",
686          * much like NULL is for a pointer type. */
687
688         k = parse_boolean(rvalue);
689         if (k < 0) {
690                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
691                 return 0;
692         }
693
694         *t = !!k;
695         return 0;
696 }
697 #endif // 0
698
699 int config_parse_string(
700                 const char *unit,
701                 const char *filename,
702                 unsigned line,
703                 const char *section,
704                 unsigned section_line,
705                 const char *lvalue,
706                 int ltype,
707                 const char *rvalue,
708                 void *data,
709                 void *userdata) {
710
711         char **s = data;
712
713         assert(filename);
714         assert(lvalue);
715         assert(rvalue);
716         assert(data);
717
718         if (free_and_strdup(s, empty_to_null(rvalue)) < 0)
719                 return log_oom();
720
721         return 0;
722 }
723
724 int config_parse_path(
725                 const char *unit,
726                 const char *filename,
727                 unsigned line,
728                 const char *section,
729                 unsigned section_line,
730                 const char *lvalue,
731                 int ltype,
732                 const char *rvalue,
733                 void *data,
734                 void *userdata) {
735
736         _cleanup_free_ char *n = NULL;
737         bool fatal = ltype;
738         char **s = data;
739         int r;
740
741         assert(filename);
742         assert(lvalue);
743         assert(rvalue);
744         assert(data);
745
746         if (isempty(rvalue))
747                 goto finalize;
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         return free_and_replace(*s, n);
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 }
1233
1234 int config_parse_permille(const char* unit,
1235                           const char *filename,
1236                           unsigned line,
1237                           const char *section,
1238                           unsigned section_line,
1239                           const char *lvalue,
1240                           int ltype,
1241                           const char *rvalue,
1242                           void *data,
1243                           void *userdata) {
1244
1245         unsigned *permille = data;
1246         int r;
1247
1248         assert(filename);
1249         assert(lvalue);
1250         assert(rvalue);
1251         assert(permille);
1252
1253         r = parse_permille(rvalue);
1254         if (r < 0) {
1255                 log_syntax(unit, LOG_ERR, filename, line, r,
1256                            "Failed to parse permille value, ignoring: %s", rvalue);
1257                 return 0;
1258         }
1259
1260         *permille = (unsigned) r;
1261
1262         return 0;
1263 }