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