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