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