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