chiark / gitweb /
shared/conf-parser: fix outdated comment
[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                 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         return 0;
417 }
418
419 static int config_parse_many_files(
420                 const char *conf_file,
421                 char **files,
422                 const char *sections,
423                 ConfigItemLookup lookup,
424                 const void *table,
425                 ConfigParseFlags flags,
426                 void *userdata) {
427
428         char **fn;
429         int r;
430
431         if (conf_file) {
432                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
433                 if (r < 0)
434                         return r;
435         }
436
437         STRV_FOREACH(fn, files) {
438                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
439                 if (r < 0)
440                         return r;
441         }
442
443         return 0;
444 }
445
446 /* Parse each config file in the directories specified as nulstr. */
447 int config_parse_many_nulstr(
448                 const char *conf_file,
449                 const char *conf_file_dirs,
450                 const char *sections,
451                 ConfigItemLookup lookup,
452                 const void *table,
453                 ConfigParseFlags flags,
454                 void *userdata) {
455
456         _cleanup_strv_free_ char **files = NULL;
457         int r;
458
459         r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
460         if (r < 0)
461                 return r;
462
463         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
464 }
465
466 #if 0 /// UNNEEDED by elogind
467 /* Parse each config file in the directories specified as strv. */
468 int config_parse_many(
469                 const char *conf_file,
470                 const char* const* conf_file_dirs,
471                 const char *dropin_dirname,
472                 const char *sections,
473                 ConfigItemLookup lookup,
474                 const void *table,
475                 ConfigParseFlags flags,
476                 void *userdata) {
477
478         _cleanup_strv_free_ char **dropin_dirs = NULL;
479         _cleanup_strv_free_ char **files = NULL;
480         const char *suffix;
481         int r;
482
483         suffix = strjoina("/", dropin_dirname);
484         r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
485         if (r < 0)
486                 return r;
487
488         r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
489         if (r < 0)
490                 return r;
491
492         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
493 }
494 #endif // 0
495
496 #define DEFINE_PARSER(type, vartype, conv_func)                         \
497         int config_parse_##type(                                        \
498                         const char *unit,                               \
499                         const char *filename,                           \
500                         unsigned line,                                  \
501                         const char *section,                            \
502                         unsigned section_line,                          \
503                         const char *lvalue,                             \
504                         int ltype,                                      \
505                         const char *rvalue,                             \
506                         void *data,                                     \
507                         void *userdata) {                               \
508                                                                         \
509                 vartype *i = data;                                      \
510                 int r;                                                  \
511                                                                         \
512                 assert(filename);                                       \
513                 assert(lvalue);                                         \
514                 assert(rvalue);                                         \
515                 assert(data);                                           \
516                                                                         \
517                 r = conv_func(rvalue, i);                               \
518                 if (r < 0)                                              \
519                         log_syntax(unit, LOG_ERR, filename, line, r,    \
520                                    "Failed to parse %s value, ignoring: %s", \
521                                    #type, rvalue);                      \
522                                                                         \
523                 return 0;                                               \
524         }
525
526 DEFINE_PARSER(int, int, safe_atoi);
527 DEFINE_PARSER(long, long, safe_atoli);
528 #if 0 /// UNNEEDED by elogind
529 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
530 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
531 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
532 #endif // 0
533 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
534 DEFINE_PARSER(unsigned, unsigned, safe_atou);
535 DEFINE_PARSER(double, double, safe_atod);
536 #if 0 /// UNNEEDED by elogind
537 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
538 #endif // 0
539 DEFINE_PARSER(sec, usec_t, parse_sec);
540 DEFINE_PARSER(mode, mode_t, parse_mode);
541
542 int config_parse_iec_size(const char* unit,
543                             const char *filename,
544                             unsigned line,
545                             const char *section,
546                             unsigned section_line,
547                             const char *lvalue,
548                             int ltype,
549                             const char *rvalue,
550                             void *data,
551                             void *userdata) {
552
553         size_t *sz = data;
554         uint64_t v;
555         int r;
556
557         assert(filename);
558         assert(lvalue);
559         assert(rvalue);
560         assert(data);
561
562         r = parse_size(rvalue, 1024, &v);
563         if (r < 0 || (uint64_t) (size_t) v != v) {
564                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
565                 return 0;
566         }
567
568         *sz = (size_t) v;
569         return 0;
570 }
571
572 #if 0 /// UNNEEDED by elogind
573 int config_parse_si_size(
574                 const char* unit,
575                 const char *filename,
576                 unsigned line,
577                 const char *section,
578                 unsigned section_line,
579                 const char *lvalue,
580                 int ltype,
581                 const char *rvalue,
582                 void *data,
583                 void *userdata) {
584
585         size_t *sz = data;
586         uint64_t v;
587         int r;
588
589         assert(filename);
590         assert(lvalue);
591         assert(rvalue);
592         assert(data);
593
594         r = parse_size(rvalue, 1000, &v);
595         if (r < 0 || (uint64_t) (size_t) v != v) {
596                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
597                 return 0;
598         }
599
600         *sz = (size_t) v;
601         return 0;
602 }
603
604 int config_parse_iec_uint64(
605                 const char* unit,
606                 const char *filename,
607                 unsigned line,
608                 const char *section,
609                 unsigned section_line,
610                 const char *lvalue,
611                 int ltype,
612                 const char *rvalue,
613                 void *data,
614                 void *userdata) {
615
616         uint64_t *bytes = data;
617         int r;
618
619         assert(filename);
620         assert(lvalue);
621         assert(rvalue);
622         assert(data);
623
624         r = parse_size(rvalue, 1024, bytes);
625         if (r < 0)
626                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
627
628         return 0;
629 }
630 #endif // 0
631
632 int config_parse_bool(const char* unit,
633                       const char *filename,
634                       unsigned line,
635                       const char *section,
636                       unsigned section_line,
637                       const char *lvalue,
638                       int ltype,
639                       const char *rvalue,
640                       void *data,
641                       void *userdata) {
642
643         int k;
644         bool *b = data;
645         bool fatal = ltype;
646
647         assert(filename);
648         assert(lvalue);
649         assert(rvalue);
650         assert(data);
651
652         k = parse_boolean(rvalue);
653         if (k < 0) {
654                 log_syntax(unit, LOG_ERR, filename, line, k,
655                            "Failed to parse boolean value%s: %s",
656                            fatal ? "" : ", ignoring", rvalue);
657                 return fatal ? -ENOEXEC : 0;
658         }
659
660         *b = !!k;
661         return 0;
662 }
663
664 #if 0 /// UNNEEDED by elogind
665 int config_parse_tristate(
666                 const char* unit,
667                 const char *filename,
668                 unsigned line,
669                 const char *section,
670                 unsigned section_line,
671                 const char *lvalue,
672                 int ltype,
673                 const char *rvalue,
674                 void *data,
675                 void *userdata) {
676
677         int k, *t = data;
678
679         assert(filename);
680         assert(lvalue);
681         assert(rvalue);
682         assert(data);
683
684         /* A tristate is pretty much a boolean, except that it can
685          * also take the special value -1, indicating "uninitialized",
686          * much like NULL is for a pointer type. */
687
688         k = parse_boolean(rvalue);
689         if (k < 0) {
690                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
691                 return 0;
692         }
693
694         *t = !!k;
695         return 0;
696 }
697 #endif // 0
698
699 int config_parse_string(
700                 const char *unit,
701                 const char *filename,
702                 unsigned line,
703                 const char *section,
704                 unsigned section_line,
705                 const char *lvalue,
706                 int ltype,
707                 const char *rvalue,
708                 void *data,
709                 void *userdata) {
710
711         char **s = data, *n;
712
713         assert(filename);
714         assert(lvalue);
715         assert(rvalue);
716         assert(data);
717
718         if (!utf8_is_valid(rvalue)) {
719                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
720                 return 0;
721         }
722
723         if (isempty(rvalue))
724                 n = NULL;
725         else {
726                 n = strdup(rvalue);
727                 if (!n)
728                         return log_oom();
729         }
730
731         free(*s);
732         *s = n;
733
734         return 0;
735 }
736
737 int config_parse_path(
738                 const char *unit,
739                 const char *filename,
740                 unsigned line,
741                 const char *section,
742                 unsigned section_line,
743                 const char *lvalue,
744                 int ltype,
745                 const char *rvalue,
746                 void *data,
747                 void *userdata) {
748
749         char **s = data, *n;
750         bool fatal = ltype;
751
752         assert(filename);
753         assert(lvalue);
754         assert(rvalue);
755         assert(data);
756
757         if (isempty(rvalue)) {
758                 n = NULL;
759                 goto finalize;
760         }
761
762         if (!utf8_is_valid(rvalue)) {
763                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
764                 return fatal ? -ENOEXEC : 0;
765         }
766
767         if (!path_is_absolute(rvalue)) {
768                 log_syntax(unit, LOG_ERR, filename, line, 0,
769                            "Not an absolute path%s: %s",
770                            fatal ? "" : ", ignoring", rvalue);
771                 return fatal ? -ENOEXEC : 0;
772         }
773
774         n = strdup(rvalue);
775         if (!n)
776                 return log_oom();
777
778         path_kill_slashes(n);
779
780 finalize:
781         free(*s);
782         *s = n;
783
784         return 0;
785 }
786
787 int config_parse_strv(
788                 const char *unit,
789                 const char *filename,
790                 unsigned line,
791                 const char *section,
792                 unsigned section_line,
793                 const char *lvalue,
794                 int ltype,
795                 const char *rvalue,
796                 void *data,
797                 void *userdata) {
798
799         char ***sv = data;
800         int r;
801
802         assert(filename);
803         assert(lvalue);
804         assert(rvalue);
805         assert(data);
806
807         if (isempty(rvalue)) {
808                 *sv = strv_free(*sv);
809                 return 0;
810         }
811
812         for (;;) {
813                 char *word = NULL;
814
815                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
816                 if (r == 0)
817                         break;
818                 if (r == -ENOMEM)
819                         return log_oom();
820                 if (r < 0) {
821                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
822                         break;
823                 }
824
825                 if (!utf8_is_valid(word)) {
826                         log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
827                         free(word);
828                         continue;
829                 }
830
831                 r = strv_consume(sv, word);
832                 if (r < 0)
833                         return log_oom();
834         }
835
836         return 0;
837 }
838
839 int config_parse_warn_compat(
840                 const char *unit,
841                 const char *filename,
842                 unsigned line,
843                 const char *section,
844                 unsigned section_line,
845                 const char *lvalue,
846                 int ltype,
847                 const char *rvalue,
848                 void *data,
849                 void *userdata) {
850         Disabled reason = ltype;
851
852         switch(reason) {
853         case DISABLED_CONFIGURATION:
854                 log_syntax(unit, LOG_DEBUG, filename, line, 0,
855                            "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
856                 break;
857         case DISABLED_LEGACY:
858                 log_syntax(unit, LOG_INFO, filename, line, 0,
859                            "Support for option %s= has been removed and it is ignored", lvalue);
860                 break;
861         case DISABLED_EXPERIMENTAL:
862                 log_syntax(unit, LOG_INFO, filename, line, 0,
863                            "Support for option %s= has not yet been enabled and it is ignored", lvalue);
864                 break;
865         };
866
867         return 0;
868 }
869
870 #if 0 /// UNNEEDED by elogind
871 int config_parse_log_facility(
872                 const char *unit,
873                 const char *filename,
874                 unsigned line,
875                 const char *section,
876                 unsigned section_line,
877                 const char *lvalue,
878                 int ltype,
879                 const char *rvalue,
880                 void *data,
881                 void *userdata) {
882
883         int *o = data, x;
884
885         assert(filename);
886         assert(lvalue);
887         assert(rvalue);
888         assert(data);
889
890         x = log_facility_unshifted_from_string(rvalue);
891         if (x < 0) {
892                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
893                 return 0;
894         }
895
896         *o = (x << 3) | LOG_PRI(*o);
897
898         return 0;
899 }
900 #endif // 0
901
902 int config_parse_log_level(
903                 const char *unit,
904                 const char *filename,
905                 unsigned line,
906                 const char *section,
907                 unsigned section_line,
908                 const char *lvalue,
909                 int ltype,
910                 const char *rvalue,
911                 void *data,
912                 void *userdata) {
913
914         int *o = data, x;
915
916         assert(filename);
917         assert(lvalue);
918         assert(rvalue);
919         assert(data);
920
921         x = log_level_from_string(rvalue);
922         if (x < 0) {
923                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
924                 return 0;
925         }
926
927         if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
928                 *o = x;
929         else
930                 *o = (*o & LOG_FACMASK) | x;
931
932         return 0;
933 }
934
935 int config_parse_signal(
936                 const char *unit,
937                 const char *filename,
938                 unsigned line,
939                 const char *section,
940                 unsigned section_line,
941                 const char *lvalue,
942                 int ltype,
943                 const char *rvalue,
944                 void *data,
945                 void *userdata) {
946
947         int *sig = data, r;
948
949         assert(filename);
950         assert(lvalue);
951         assert(rvalue);
952         assert(sig);
953
954         r = signal_from_string_try_harder(rvalue);
955         if (r <= 0) {
956                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
957                 return 0;
958         }
959
960         *sig = r;
961         return 0;
962 }
963
964 #if 0 /// UNNEEDED by elogind
965 int config_parse_personality(
966                 const char *unit,
967                 const char *filename,
968                 unsigned line,
969                 const char *section,
970                 unsigned section_line,
971                 const char *lvalue,
972                 int ltype,
973                 const char *rvalue,
974                 void *data,
975                 void *userdata) {
976
977         unsigned long *personality = data, p;
978
979         assert(filename);
980         assert(lvalue);
981         assert(rvalue);
982         assert(personality);
983
984         if (isempty(rvalue))
985                 p = PERSONALITY_INVALID;
986         else {
987                 p = personality_from_string(rvalue);
988                 if (p == PERSONALITY_INVALID) {
989                         log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
990                         return 0;
991                 }
992         }
993
994         *personality = p;
995         return 0;
996 }
997
998 int config_parse_ifname(
999                 const char *unit,
1000                 const char *filename,
1001                 unsigned line,
1002                 const char *section,
1003                 unsigned section_line,
1004                 const char *lvalue,
1005                 int ltype,
1006                 const char *rvalue,
1007                 void *data,
1008                 void *userdata) {
1009
1010         char **s = data;
1011         int r;
1012
1013         assert(filename);
1014         assert(lvalue);
1015         assert(rvalue);
1016         assert(data);
1017
1018         if (isempty(rvalue)) {
1019                 *s = mfree(*s);
1020                 return 0;
1021         }
1022
1023         if (!ifname_valid(rvalue)) {
1024                 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1025                 return 0;
1026         }
1027
1028         r = free_and_strdup(s, rvalue);
1029         if (r < 0)
1030                 return log_oom();
1031
1032         return 0;
1033 }
1034
1035 int config_parse_ip_port(
1036                 const char *unit,
1037                 const char *filename,
1038                 unsigned line,
1039                 const char *section,
1040                 unsigned section_line,
1041                 const char *lvalue,
1042                 int ltype,
1043                 const char *rvalue,
1044                 void *data,
1045                 void *userdata) {
1046
1047         uint16_t *s = data;
1048         uint16_t port;
1049         int r;
1050
1051         assert(filename);
1052         assert(lvalue);
1053         assert(rvalue);
1054         assert(data);
1055
1056         if (isempty(rvalue)) {
1057                 *s = 0;
1058                 return 0;
1059         }
1060
1061         r = parse_ip_port(rvalue, &port);
1062         if (r < 0) {
1063                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1064                 return 0;
1065         }
1066
1067         *s = port;
1068
1069         return 0;
1070 }
1071
1072 int config_parse_join_controllers(
1073                 const char *unit,
1074                 const char *filename,
1075                 unsigned line,
1076                 const char *section,
1077                 unsigned section_line,
1078                 const char *lvalue,
1079                 int ltype,
1080                 const char *rvalue,
1081                 void *data,
1082                 void *userdata) {
1083
1084         char ****ret = data;
1085         const char *whole_rvalue = rvalue;
1086         unsigned n = 0;
1087         _cleanup_(strv_free_freep) char ***controllers = NULL;
1088
1089         assert(filename);
1090         assert(lvalue);
1091         assert(rvalue);
1092         assert(ret);
1093
1094         for (;;) {
1095                 _cleanup_free_ char *word = NULL;
1096                 char **l;
1097                 int r;
1098
1099                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1100                 if (r < 0) {
1101                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1102                         return r;
1103                 }
1104                 if (r == 0)
1105                         break;
1106
1107                 l = strv_split(word, ",");
1108                 if (!l)
1109                         return log_oom();
1110                 strv_uniq(l);
1111
1112                 if (strv_length(l) <= 1) {
1113                         strv_free(l);
1114                         continue;
1115                 }
1116
1117                 if (!controllers) {
1118                         controllers = new(char**, 2);
1119                         if (!controllers) {
1120                                 strv_free(l);
1121                                 return log_oom();
1122                         }
1123
1124                         controllers[0] = l;
1125                         controllers[1] = NULL;
1126
1127                         n = 1;
1128                 } else {
1129                         char ***a;
1130                         char ***t;
1131
1132                         t = new0(char**, n+2);
1133                         if (!t) {
1134                                 strv_free(l);
1135                                 return log_oom();
1136                         }
1137
1138                         n = 0;
1139
1140                         for (a = controllers; *a; a++)
1141                                 if (strv_overlap(*a, l)) {
1142                                         if (strv_extend_strv(&l, *a, false) < 0) {
1143                                                 strv_free(l);
1144                                                 strv_free_free(t);
1145                                                 return log_oom();
1146                                         }
1147
1148                                 } else {
1149                                         char **c;
1150
1151                                         c = strv_copy(*a);
1152                                         if (!c) {
1153                                                 strv_free(l);
1154                                                 strv_free_free(t);
1155                                                 return log_oom();
1156                                         }
1157
1158                                         t[n++] = c;
1159                                 }
1160
1161                         t[n++] = strv_uniq(l);
1162
1163                         strv_free_free(controllers);
1164                         controllers = t;
1165                 }
1166         }
1167         if (!isempty(rvalue))
1168                 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1169
1170         /* As a special case, return a single empty strv, to override the default */
1171         if (!controllers) {
1172                 controllers = new(char**, 2);
1173                 if (!controllers)
1174                         return log_oom();
1175                 controllers[0] = strv_new(NULL, NULL);
1176                 if (!controllers[0])
1177                         return log_oom();
1178                 controllers[1] = NULL;
1179         }
1180
1181         strv_free_free(*ret);
1182         *ret = controllers;
1183         controllers = NULL;
1184
1185         return 0;
1186 }
1187 #endif // 0