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