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