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