chiark / gitweb /
conf-parser: let's explicitly deprecate .include in unit files
[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;
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         if (startswith(l, ".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(l+9));
223                 if (!fn)
224                         return -ENOMEM;
225
226                 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
227         }
228
229         if (*l == '[') {
230                 size_t k;
231                 char *n;
232
233                 k = strlen(l);
234                 assert(k > 0);
235
236                 if (l[k-1] != ']') {
237                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
238                         return -EBADMSG;
239                 }
240
241                 n = strndup(l+1, k-2);
242                 if (!n)
243                         return -ENOMEM;
244
245                 if (sections && !nulstr_contains(sections, n)) {
246
247                         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
248                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
249
250                         free(n);
251                         *section = mfree(*section);
252                         *section_line = 0;
253                         *section_ignored = true;
254                 } else {
255                         free(*section);
256                         *section = n;
257                         *section_line = line;
258                         *section_ignored = false;
259                 }
260
261                 return 0;
262         }
263
264         if (sections && !*section) {
265
266                 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
267                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
268
269                 return 0;
270         }
271
272         e = strchr(l, '=');
273         if (!e) {
274                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
275                 return -EINVAL;
276         }
277
278         *e = 0;
279         e++;
280
281         return next_assignment(unit,
282                                filename,
283                                line,
284                                lookup,
285                                table,
286                                *section,
287                                *section_line,
288                                strstrip(l),
289                                strstrip(e),
290                                flags,
291                                userdata);
292 }
293
294 /* Go through the file and parse each line */
295 int config_parse(const char *unit,
296                  const char *filename,
297                  FILE *f,
298                  const char *sections,
299                  ConfigItemLookup lookup,
300                  const void *table,
301                  ConfigParseFlags flags,
302                  void *userdata) {
303
304         _cleanup_free_ char *section = NULL, *continuation = NULL;
305         _cleanup_fclose_ FILE *ours = NULL;
306         unsigned line = 0, section_line = 0;
307         bool section_ignored = false;
308         int r;
309
310         assert(filename);
311         assert(lookup);
312
313         if (!f) {
314                 f = ours = fopen(filename, "re");
315                 if (!f) {
316                         /* Only log on request, except for ENOENT,
317                          * since we return 0 to the caller. */
318                         if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
319                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
320                                          "Failed to open configuration file '%s': %m", filename);
321                         return errno == ENOENT ? 0 : -errno;
322                 }
323         }
324
325         fd_warn_permissions(filename, fileno(f));
326
327         for (;;) {
328                 _cleanup_free_ char *buf = NULL;
329                 bool escaped = false;
330                 char *l, *p, *e;
331
332                 r = read_line(f, LONG_LINE_MAX, &buf);
333                 if (r == 0)
334                         break;
335                 if (r == -ENOBUFS) {
336                         if (flags & CONFIG_PARSE_WARN)
337                                 log_error_errno(r, "%s:%u: Line too long", filename, line);
338
339                         return r;
340                 }
341                 if (r < 0) {
342                         if (CONFIG_PARSE_WARN)
343                                 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
344
345                         return r;
346                 }
347
348                 l = buf;
349                 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
350                         char *q;
351
352                         q = startswith(buf, UTF8_BYTE_ORDER_MARK);
353                         if (q) {
354                                 l = q;
355                                 flags |= CONFIG_PARSE_REFUSE_BOM;
356                         }
357                 }
358
359                 if (continuation) {
360                         if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
361                                 if (flags & CONFIG_PARSE_WARN)
362                                         log_error("%s:%u: Continuation line too long", filename, line);
363                                 return -ENOBUFS;
364                         }
365
366                         if (!strextend(&continuation, l, NULL)) {
367                                 if (flags & CONFIG_PARSE_WARN)
368                                         log_oom();
369                                 return -ENOMEM;
370                         }
371
372                         p = continuation;
373                 } else
374                         p = l;
375
376                 for (e = p; *e; e++) {
377                         if (escaped)
378                                 escaped = false;
379                         else if (*e == '\\')
380                                 escaped = true;
381                 }
382
383                 if (escaped) {
384                         *(e-1) = ' ';
385
386                         if (!continuation) {
387                                 continuation = strdup(l);
388                                 if (!continuation) {
389                                         if (flags & CONFIG_PARSE_WARN)
390                                                 log_oom();
391                                         return -ENOMEM;
392                                 }
393                         }
394
395                         continue;
396                 }
397
398                 r = parse_line(unit,
399                                filename,
400                                ++line,
401                                sections,
402                                lookup,
403                                table,
404                                flags,
405                                &section,
406                                &section_line,
407                                &section_ignored,
408                                p,
409                                userdata);
410                 if (r < 0) {
411                         if (flags & CONFIG_PARSE_WARN)
412                                 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
413                         return r;
414
415                 }
416
417                 continuation = mfree(continuation);
418         }
419
420         return 0;
421 }
422
423 static int config_parse_many_files(
424                 const char *conf_file,
425                 char **files,
426                 const char *sections,
427                 ConfigItemLookup lookup,
428                 const void *table,
429                 ConfigParseFlags flags,
430                 void *userdata) {
431
432         char **fn;
433         int r;
434
435         if (conf_file) {
436                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
437                 if (r < 0)
438                         return r;
439         }
440
441         STRV_FOREACH(fn, files) {
442                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
443                 if (r < 0)
444                         return r;
445         }
446
447         return 0;
448 }
449
450 /* Parse each config file in the directories specified as nulstr. */
451 int config_parse_many_nulstr(
452                 const char *conf_file,
453                 const char *conf_file_dirs,
454                 const char *sections,
455                 ConfigItemLookup lookup,
456                 const void *table,
457                 ConfigParseFlags flags,
458                 void *userdata) {
459
460         _cleanup_strv_free_ char **files = NULL;
461         int r;
462
463         r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
464         if (r < 0)
465                 return r;
466
467         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
468 }
469
470 #if 0 /// UNNEEDED by elogind
471 /* Parse each config file in the directories specified as strv. */
472 int config_parse_many(
473                 const char *conf_file,
474                 const char* const* conf_file_dirs,
475                 const char *dropin_dirname,
476                 const char *sections,
477                 ConfigItemLookup lookup,
478                 const void *table,
479                 ConfigParseFlags flags,
480                 void *userdata) {
481
482         _cleanup_strv_free_ char **dropin_dirs = NULL;
483         _cleanup_strv_free_ char **files = NULL;
484         const char *suffix;
485         int r;
486
487         suffix = strjoina("/", dropin_dirname);
488         r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
489         if (r < 0)
490                 return r;
491
492         r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
493         if (r < 0)
494                 return r;
495
496         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
497 }
498 #endif // 0
499
500 #define DEFINE_PARSER(type, vartype, conv_func)                         \
501         int config_parse_##type(                                        \
502                         const char *unit,                               \
503                         const char *filename,                           \
504                         unsigned line,                                  \
505                         const char *section,                            \
506                         unsigned section_line,                          \
507                         const char *lvalue,                             \
508                         int ltype,                                      \
509                         const char *rvalue,                             \
510                         void *data,                                     \
511                         void *userdata) {                               \
512                                                                         \
513                 vartype *i = data;                                      \
514                 int r;                                                  \
515                                                                         \
516                 assert(filename);                                       \
517                 assert(lvalue);                                         \
518                 assert(rvalue);                                         \
519                 assert(data);                                           \
520                                                                         \
521                 r = conv_func(rvalue, i);                               \
522                 if (r < 0)                                              \
523                         log_syntax(unit, LOG_ERR, filename, line, r,    \
524                                    "Failed to parse %s value, ignoring: %s", \
525                                    #type, rvalue);                      \
526                                                                         \
527                 return 0;                                               \
528         }
529
530 DEFINE_PARSER(int, int, safe_atoi);
531 DEFINE_PARSER(long, long, safe_atoli);
532 #if 0 /// UNNEEDED by elogind
533 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
534 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
535 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
536 #endif // 0
537 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
538 DEFINE_PARSER(unsigned, unsigned, safe_atou);
539 DEFINE_PARSER(double, double, safe_atod);
540 #if 0 /// UNNEEDED by elogind
541 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
542 #endif // 0
543 DEFINE_PARSER(sec, usec_t, parse_sec);
544 DEFINE_PARSER(mode, mode_t, parse_mode);
545
546 int config_parse_iec_size(const char* unit,
547                             const char *filename,
548                             unsigned line,
549                             const char *section,
550                             unsigned section_line,
551                             const char *lvalue,
552                             int ltype,
553                             const char *rvalue,
554                             void *data,
555                             void *userdata) {
556
557         size_t *sz = data;
558         uint64_t v;
559         int r;
560
561         assert(filename);
562         assert(lvalue);
563         assert(rvalue);
564         assert(data);
565
566         r = parse_size(rvalue, 1024, &v);
567         if (r < 0 || (uint64_t) (size_t) v != v) {
568                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
569                 return 0;
570         }
571
572         *sz = (size_t) v;
573         return 0;
574 }
575
576 #if 0 /// UNNEEDED by elogind
577 int config_parse_si_size(
578                 const char* unit,
579                 const char *filename,
580                 unsigned line,
581                 const char *section,
582                 unsigned section_line,
583                 const char *lvalue,
584                 int ltype,
585                 const char *rvalue,
586                 void *data,
587                 void *userdata) {
588
589         size_t *sz = data;
590         uint64_t v;
591         int r;
592
593         assert(filename);
594         assert(lvalue);
595         assert(rvalue);
596         assert(data);
597
598         r = parse_size(rvalue, 1000, &v);
599         if (r < 0 || (uint64_t) (size_t) v != v) {
600                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
601                 return 0;
602         }
603
604         *sz = (size_t) v;
605         return 0;
606 }
607
608 int config_parse_iec_uint64(
609                 const char* unit,
610                 const char *filename,
611                 unsigned line,
612                 const char *section,
613                 unsigned section_line,
614                 const char *lvalue,
615                 int ltype,
616                 const char *rvalue,
617                 void *data,
618                 void *userdata) {
619
620         uint64_t *bytes = data;
621         int r;
622
623         assert(filename);
624         assert(lvalue);
625         assert(rvalue);
626         assert(data);
627
628         r = parse_size(rvalue, 1024, bytes);
629         if (r < 0)
630                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
631
632         return 0;
633 }
634 #endif // 0
635
636 int config_parse_bool(const char* unit,
637                       const char *filename,
638                       unsigned line,
639                       const char *section,
640                       unsigned section_line,
641                       const char *lvalue,
642                       int ltype,
643                       const char *rvalue,
644                       void *data,
645                       void *userdata) {
646
647         int k;
648         bool *b = data;
649         bool fatal = ltype;
650
651         assert(filename);
652         assert(lvalue);
653         assert(rvalue);
654         assert(data);
655
656         k = parse_boolean(rvalue);
657         if (k < 0) {
658                 log_syntax(unit, LOG_ERR, filename, line, k,
659                            "Failed to parse boolean value%s: %s",
660                            fatal ? "" : ", ignoring", rvalue);
661                 return fatal ? -ENOEXEC : 0;
662         }
663
664         *b = !!k;
665         return 0;
666 }
667
668 #if 0 /// UNNEEDED by elogind
669 int config_parse_tristate(
670                 const char* unit,
671                 const char *filename,
672                 unsigned line,
673                 const char *section,
674                 unsigned section_line,
675                 const char *lvalue,
676                 int ltype,
677                 const char *rvalue,
678                 void *data,
679                 void *userdata) {
680
681         int k, *t = data;
682
683         assert(filename);
684         assert(lvalue);
685         assert(rvalue);
686         assert(data);
687
688         /* A tristate is pretty much a boolean, except that it can
689          * also take the special value -1, indicating "uninitialized",
690          * much like NULL is for a pointer type. */
691
692         k = parse_boolean(rvalue);
693         if (k < 0) {
694                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
695                 return 0;
696         }
697
698         *t = !!k;
699         return 0;
700 }
701 #endif // 0
702
703 int config_parse_string(
704                 const char *unit,
705                 const char *filename,
706                 unsigned line,
707                 const char *section,
708                 unsigned section_line,
709                 const char *lvalue,
710                 int ltype,
711                 const char *rvalue,
712                 void *data,
713                 void *userdata) {
714
715         char **s = data, *n;
716
717         assert(filename);
718         assert(lvalue);
719         assert(rvalue);
720         assert(data);
721
722         if (!utf8_is_valid(rvalue)) {
723                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
724                 return 0;
725         }
726
727         if (isempty(rvalue))
728                 n = NULL;
729         else {
730                 n = strdup(rvalue);
731                 if (!n)
732                         return log_oom();
733         }
734
735         free(*s);
736         *s = n;
737
738         return 0;
739 }
740
741 int config_parse_path(
742                 const char *unit,
743                 const char *filename,
744                 unsigned line,
745                 const char *section,
746                 unsigned section_line,
747                 const char *lvalue,
748                 int ltype,
749                 const char *rvalue,
750                 void *data,
751                 void *userdata) {
752
753         char **s = data, *n;
754         bool fatal = ltype;
755
756         assert(filename);
757         assert(lvalue);
758         assert(rvalue);
759         assert(data);
760
761         if (isempty(rvalue)) {
762                 n = NULL;
763                 goto finalize;
764         }
765
766         if (!utf8_is_valid(rvalue)) {
767                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
768                 return fatal ? -ENOEXEC : 0;
769         }
770
771         if (!path_is_absolute(rvalue)) {
772                 log_syntax(unit, LOG_ERR, filename, line, 0,
773                            "Not an absolute path%s: %s",
774                            fatal ? "" : ", ignoring", rvalue);
775                 return fatal ? -ENOEXEC : 0;
776         }
777
778         n = strdup(rvalue);
779         if (!n)
780                 return log_oom();
781
782         path_kill_slashes(n);
783
784 finalize:
785         free(*s);
786         *s = n;
787
788         return 0;
789 }
790
791 int config_parse_strv(
792                 const char *unit,
793                 const char *filename,
794                 unsigned line,
795                 const char *section,
796                 unsigned section_line,
797                 const char *lvalue,
798                 int ltype,
799                 const char *rvalue,
800                 void *data,
801                 void *userdata) {
802
803         char ***sv = data;
804         int r;
805
806         assert(filename);
807         assert(lvalue);
808         assert(rvalue);
809         assert(data);
810
811         if (isempty(rvalue)) {
812                 *sv = strv_free(*sv);
813                 return 0;
814         }
815
816         for (;;) {
817                 char *word = NULL;
818
819                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
820                 if (r == 0)
821                         break;
822                 if (r == -ENOMEM)
823                         return log_oom();
824                 if (r < 0) {
825                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
826                         break;
827                 }
828
829                 if (!utf8_is_valid(word)) {
830                         log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
831                         free(word);
832                         continue;
833                 }
834
835                 r = strv_consume(sv, word);
836                 if (r < 0)
837                         return log_oom();
838         }
839
840         return 0;
841 }
842
843 int config_parse_warn_compat(
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         Disabled reason = ltype;
855
856         switch(reason) {
857         case DISABLED_CONFIGURATION:
858                 log_syntax(unit, LOG_DEBUG, filename, line, 0,
859                            "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
860                 break;
861         case DISABLED_LEGACY:
862                 log_syntax(unit, LOG_INFO, filename, line, 0,
863                            "Support for option %s= has been removed and it is ignored", lvalue);
864                 break;
865         case DISABLED_EXPERIMENTAL:
866                 log_syntax(unit, LOG_INFO, filename, line, 0,
867                            "Support for option %s= has not yet been enabled and it is ignored", lvalue);
868                 break;
869         };
870
871         return 0;
872 }
873
874 #if 0 /// UNNEEDED by elogind
875 int config_parse_log_facility(
876                 const char *unit,
877                 const char *filename,
878                 unsigned line,
879                 const char *section,
880                 unsigned section_line,
881                 const char *lvalue,
882                 int ltype,
883                 const char *rvalue,
884                 void *data,
885                 void *userdata) {
886
887         int *o = data, x;
888
889         assert(filename);
890         assert(lvalue);
891         assert(rvalue);
892         assert(data);
893
894         x = log_facility_unshifted_from_string(rvalue);
895         if (x < 0) {
896                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
897                 return 0;
898         }
899
900         *o = (x << 3) | LOG_PRI(*o);
901
902         return 0;
903 }
904 #endif // 0
905
906 int config_parse_log_level(
907                 const char *unit,
908                 const char *filename,
909                 unsigned line,
910                 const char *section,
911                 unsigned section_line,
912                 const char *lvalue,
913                 int ltype,
914                 const char *rvalue,
915                 void *data,
916                 void *userdata) {
917
918         int *o = data, x;
919
920         assert(filename);
921         assert(lvalue);
922         assert(rvalue);
923         assert(data);
924
925         x = log_level_from_string(rvalue);
926         if (x < 0) {
927                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
928                 return 0;
929         }
930
931         if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
932                 *o = x;
933         else
934                 *o = (*o & LOG_FACMASK) | x;
935
936         return 0;
937 }
938
939 int config_parse_signal(
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         int *sig = data, r;
952
953         assert(filename);
954         assert(lvalue);
955         assert(rvalue);
956         assert(sig);
957
958         r = signal_from_string_try_harder(rvalue);
959         if (r <= 0) {
960                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
961                 return 0;
962         }
963
964         *sig = r;
965         return 0;
966 }
967
968 #if 0 /// UNNEEDED by elogind
969 int config_parse_personality(
970                 const char *unit,
971                 const char *filename,
972                 unsigned line,
973                 const char *section,
974                 unsigned section_line,
975                 const char *lvalue,
976                 int ltype,
977                 const char *rvalue,
978                 void *data,
979                 void *userdata) {
980
981         unsigned long *personality = data, p;
982
983         assert(filename);
984         assert(lvalue);
985         assert(rvalue);
986         assert(personality);
987
988         if (isempty(rvalue))
989                 p = PERSONALITY_INVALID;
990         else {
991                 p = personality_from_string(rvalue);
992                 if (p == PERSONALITY_INVALID) {
993                         log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
994                         return 0;
995                 }
996         }
997
998         *personality = p;
999         return 0;
1000 }
1001
1002 int config_parse_ifname(
1003                 const char *unit,
1004                 const char *filename,
1005                 unsigned line,
1006                 const char *section,
1007                 unsigned section_line,
1008                 const char *lvalue,
1009                 int ltype,
1010                 const char *rvalue,
1011                 void *data,
1012                 void *userdata) {
1013
1014         char **s = data;
1015         int r;
1016
1017         assert(filename);
1018         assert(lvalue);
1019         assert(rvalue);
1020         assert(data);
1021
1022         if (isempty(rvalue)) {
1023                 *s = mfree(*s);
1024                 return 0;
1025         }
1026
1027         if (!ifname_valid(rvalue)) {
1028                 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1029                 return 0;
1030         }
1031
1032         r = free_and_strdup(s, rvalue);
1033         if (r < 0)
1034                 return log_oom();
1035
1036         return 0;
1037 }
1038
1039 int config_parse_ip_port(
1040                 const char *unit,
1041                 const char *filename,
1042                 unsigned line,
1043                 const char *section,
1044                 unsigned section_line,
1045                 const char *lvalue,
1046                 int ltype,
1047                 const char *rvalue,
1048                 void *data,
1049                 void *userdata) {
1050
1051         uint16_t *s = data;
1052         uint16_t port;
1053         int r;
1054
1055         assert(filename);
1056         assert(lvalue);
1057         assert(rvalue);
1058         assert(data);
1059
1060         if (isempty(rvalue)) {
1061                 *s = 0;
1062                 return 0;
1063         }
1064
1065         r = parse_ip_port(rvalue, &port);
1066         if (r < 0) {
1067                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1068                 return 0;
1069         }
1070
1071         *s = port;
1072
1073         return 0;
1074 }
1075
1076 int config_parse_join_controllers(
1077                 const char *unit,
1078                 const char *filename,
1079                 unsigned line,
1080                 const char *section,
1081                 unsigned section_line,
1082                 const char *lvalue,
1083                 int ltype,
1084                 const char *rvalue,
1085                 void *data,
1086                 void *userdata) {
1087
1088         char ****ret = data;
1089         const char *whole_rvalue = rvalue;
1090         unsigned n = 0;
1091         _cleanup_(strv_free_freep) char ***controllers = NULL;
1092
1093         assert(filename);
1094         assert(lvalue);
1095         assert(rvalue);
1096         assert(ret);
1097
1098         for (;;) {
1099                 _cleanup_free_ char *word = NULL;
1100                 char **l;
1101                 int r;
1102
1103                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1104                 if (r < 0) {
1105                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1106                         return r;
1107                 }
1108                 if (r == 0)
1109                         break;
1110
1111                 l = strv_split(word, ",");
1112                 if (!l)
1113                         return log_oom();
1114                 strv_uniq(l);
1115
1116                 if (strv_length(l) <= 1) {
1117                         strv_free(l);
1118                         continue;
1119                 }
1120
1121                 if (!controllers) {
1122                         controllers = new(char**, 2);
1123                         if (!controllers) {
1124                                 strv_free(l);
1125                                 return log_oom();
1126                         }
1127
1128                         controllers[0] = l;
1129                         controllers[1] = NULL;
1130
1131                         n = 1;
1132                 } else {
1133                         char ***a;
1134                         char ***t;
1135
1136                         t = new0(char**, n+2);
1137                         if (!t) {
1138                                 strv_free(l);
1139                                 return log_oom();
1140                         }
1141
1142                         n = 0;
1143
1144                         for (a = controllers; *a; a++)
1145                                 if (strv_overlap(*a, l)) {
1146                                         if (strv_extend_strv(&l, *a, false) < 0) {
1147                                                 strv_free(l);
1148                                                 strv_free_free(t);
1149                                                 return log_oom();
1150                                         }
1151
1152                                 } else {
1153                                         char **c;
1154
1155                                         c = strv_copy(*a);
1156                                         if (!c) {
1157                                                 strv_free(l);
1158                                                 strv_free_free(t);
1159                                                 return log_oom();
1160                                         }
1161
1162                                         t[n++] = c;
1163                                 }
1164
1165                         t[n++] = strv_uniq(l);
1166
1167                         strv_free_free(controllers);
1168                         controllers = t;
1169                 }
1170         }
1171         if (!isempty(rvalue))
1172                 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1173
1174         /* As a special case, return a single empty strv, to override the default */
1175         if (!controllers) {
1176                 controllers = new(char**, 2);
1177                 if (!controllers)
1178                         return log_oom();
1179                 controllers[0] = strv_new(NULL, NULL);
1180                 if (!controllers[0])
1181                         return log_oom();
1182                 controllers[1] = NULL;
1183         }
1184
1185         strv_free_free(*ret);
1186         *ret = controllers;
1187         controllers = NULL;
1188
1189         return 0;
1190 }
1191 #endif // 0