chiark / gitweb /
Prep v236 : Add missing SPDX-License-Identifier (6/9) src/shared
[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   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28
29 #include "alloc-util.h"
30 #include "conf-files.h"
31 #include "conf-parser.h"
32 #include "def.h"
33 #include "extract-word.h"
34 #include "fd-util.h"
35 #include "fileio.h"
36 #include "fs-util.h"
37 #include "log.h"
38 #include "macro.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "signal-util.h"
43 #include "socket-util.h"
44 #include "string-util.h"
45 #include "strv.h"
46 #include "syslog-util.h"
47 #include "time-util.h"
48 #include "utf8.h"
49
50 /// Additional includes needed by elogind
51 #include "def.h"
52 #include "fileio.h"
53
54 int config_item_table_lookup(
55                 const void *table,
56                 const char *section,
57                 const char *lvalue,
58                 ConfigParserCallback *func,
59                 int *ltype,
60                 void **data,
61                 void *userdata) {
62
63         const ConfigTableItem *t;
64
65         assert(table);
66         assert(lvalue);
67         assert(func);
68         assert(ltype);
69         assert(data);
70
71         for (t = table; t->lvalue; t++) {
72
73                 if (!streq(lvalue, t->lvalue))
74                         continue;
75
76                 if (!streq_ptr(section, t->section))
77                         continue;
78
79                 *func = t->parse;
80                 *ltype = t->ltype;
81                 *data = t->data;
82                 return 1;
83         }
84
85         return 0;
86 }
87
88 int config_item_perf_lookup(
89                 const void *table,
90                 const char *section,
91                 const char *lvalue,
92                 ConfigParserCallback *func,
93                 int *ltype,
94                 void **data,
95                 void *userdata) {
96
97         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
98         const ConfigPerfItem *p;
99
100         assert(table);
101         assert(lvalue);
102         assert(func);
103         assert(ltype);
104         assert(data);
105
106         if (!section)
107                 p = lookup(lvalue, strlen(lvalue));
108         else {
109                 char *key;
110
111                 key = strjoin(section, ".", lvalue);
112                 if (!key)
113                         return -ENOMEM;
114
115                 p = lookup(key, strlen(key));
116                 free(key);
117         }
118
119         if (!p)
120                 return 0;
121
122         *func = p->parse;
123         *ltype = p->ltype;
124         *data = (uint8_t*) userdata + p->offset;
125         return 1;
126 }
127
128 /* Run the user supplied parser for an assignment */
129 static int next_assignment(
130                 const char *unit,
131                 const char *filename,
132                 unsigned line,
133                 ConfigItemLookup lookup,
134                 const void *table,
135                 const char *section,
136                 unsigned section_line,
137                 const char *lvalue,
138                 const char *rvalue,
139                 ConfigParseFlags flags,
140                 void *userdata) {
141
142         ConfigParserCallback func = NULL;
143         int ltype = 0;
144         void *data = NULL;
145         int r;
146
147         assert(filename);
148         assert(line > 0);
149         assert(lookup);
150         assert(lvalue);
151         assert(rvalue);
152
153         r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
154         if (r < 0)
155                 return r;
156
157         if (r > 0) {
158                 if (func)
159                         return func(unit, filename, line, section, section_line,
160                                     lvalue, ltype, rvalue, data, userdata);
161
162                 return 0;
163         }
164
165         /* Warn about unknown non-extension fields. */
166         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
167                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
168
169         return 0;
170 }
171
172 /* Parse a variable assignment line */
173 static int parse_line(
174                 const char* unit,
175                 const char *filename,
176                 unsigned line,
177                 const char *sections,
178                 ConfigItemLookup lookup,
179                 const void *table,
180                 ConfigParseFlags flags,
181                 char **section,
182                 unsigned *section_line,
183                 bool *section_ignored,
184                 char *l,
185                 void *userdata) {
186
187         char *e;
188
189         assert(filename);
190         assert(line > 0);
191         assert(lookup);
192         assert(l);
193
194         l = strstrip(l);
195         if (!*l)
196                 return 0;
197
198         if (strchr(COMMENTS "\n", *l))
199                 return 0;
200
201         if (startswith(l, ".include ")) {
202                 _cleanup_free_ char *fn = NULL;
203
204                 /* .includes are a bad idea, we only support them here
205                  * for historical reasons. They create cyclic include
206                  * problems and make it difficult to detect
207                  * configuration file changes with an easy
208                  * stat(). Better approaches, such as .d/ drop-in
209                  * snippets exist.
210                  *
211                  * Support for them should be eventually removed. */
212
213                 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
214                         log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
215                         return 0;
216                 }
217
218                 fn = file_in_same_dir(filename, strstrip(l+9));
219                 if (!fn)
220                         return -ENOMEM;
221
222                 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
223         }
224
225         if (*l == '[') {
226                 size_t k;
227                 char *n;
228
229                 k = strlen(l);
230                 assert(k > 0);
231
232                 if (l[k-1] != ']') {
233                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
234                         return -EBADMSG;
235                 }
236
237                 n = strndup(l+1, k-2);
238                 if (!n)
239                         return -ENOMEM;
240
241                 if (sections && !nulstr_contains(sections, n)) {
242
243                         if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
244                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
245
246                         free(n);
247                         *section = mfree(*section);
248                         *section_line = 0;
249                         *section_ignored = true;
250                 } else {
251                         free(*section);
252                         *section = n;
253                         *section_line = line;
254                         *section_ignored = false;
255                 }
256
257                 return 0;
258         }
259
260         if (sections && !*section) {
261
262                 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
263                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
264
265                 return 0;
266         }
267
268         e = strchr(l, '=');
269         if (!e) {
270                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
271                 return -EINVAL;
272         }
273
274         *e = 0;
275         e++;
276
277         return next_assignment(unit,
278                                filename,
279                                line,
280                                lookup,
281                                table,
282                                *section,
283                                *section_line,
284                                strstrip(l),
285                                strstrip(e),
286                                flags,
287                                userdata);
288 }
289
290 /* Go through the file and parse each line */
291 int config_parse(const char *unit,
292                  const char *filename,
293                  FILE *f,
294                  const char *sections,
295                  ConfigItemLookup lookup,
296                  const void *table,
297                  ConfigParseFlags flags,
298                  void *userdata) {
299
300         _cleanup_free_ char *section = NULL, *continuation = NULL;
301         _cleanup_fclose_ FILE *ours = NULL;
302         unsigned line = 0, section_line = 0;
303         bool section_ignored = false;
304         int r;
305
306         assert(filename);
307         assert(lookup);
308
309         if (!f) {
310                 f = ours = fopen(filename, "re");
311                 if (!f) {
312                         /* Only log on request, except for ENOENT,
313                          * since we return 0 to the caller. */
314                         if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
315                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
316                                          "Failed to open configuration file '%s': %m", filename);
317                         return errno == ENOENT ? 0 : -errno;
318                 }
319         }
320
321         fd_warn_permissions(filename, fileno(f));
322
323         for (;;) {
324                 _cleanup_free_ char *buf = NULL;
325                 bool escaped = false;
326                 char *l, *p, *e;
327
328                 r = read_line(f, LONG_LINE_MAX, &buf);
329                 if (r == 0)
330                         break;
331                 if (r == -ENOBUFS) {
332                         if (flags & CONFIG_PARSE_WARN)
333                                 log_error_errno(r, "%s:%u: Line too long", filename, line);
334
335                         return r;
336                 }
337                 if (r < 0) {
338                         if (CONFIG_PARSE_WARN)
339                                 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
340
341                         return r;
342                 }
343
344                 l = buf;
345                 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
346                         char *q;
347
348                         q = startswith(buf, UTF8_BYTE_ORDER_MARK);
349                         if (q) {
350                                 l = q;
351                                 flags |= CONFIG_PARSE_REFUSE_BOM;
352                         }
353                 }
354
355                 if (continuation) {
356                         if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
357                                 if (flags & CONFIG_PARSE_WARN)
358                                         log_error("%s:%u: Continuation line too long", filename, line);
359                                 return -ENOBUFS;
360                         }
361
362                         if (!strextend(&continuation, l, NULL)) {
363                                 if (flags & CONFIG_PARSE_WARN)
364                                         log_oom();
365                                 return -ENOMEM;
366                         }
367
368                         p = continuation;
369                 } else
370                         p = l;
371
372                 for (e = p; *e; e++) {
373                         if (escaped)
374                                 escaped = false;
375                         else if (*e == '\\')
376                                 escaped = true;
377                 }
378
379                 if (escaped) {
380                         *(e-1) = ' ';
381
382                         if (!continuation) {
383                                 continuation = strdup(l);
384                                 if (!continuation) {
385                                         if (flags & CONFIG_PARSE_WARN)
386                                                 log_oom();
387                                         return -ENOMEM;
388                                 }
389                         }
390
391                         continue;
392                 }
393
394                 r = parse_line(unit,
395                                filename,
396                                ++line,
397                                sections,
398                                lookup,
399                                table,
400                                flags,
401                                &section,
402                                &section_line,
403                                &section_ignored,
404                                p,
405                                userdata);
406                 if (r < 0) {
407                         if (flags & CONFIG_PARSE_WARN)
408                                 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
409                         return r;
410
411                 }
412
413                 continuation = mfree(continuation);
414         }
415
416         return 0;
417 }
418
419 static int config_parse_many_files(
420                 const char *conf_file,
421                 char **files,
422                 const char *sections,
423                 ConfigItemLookup lookup,
424                 const void *table,
425                 ConfigParseFlags flags,
426                 void *userdata) {
427
428         char **fn;
429         int r;
430
431         if (conf_file) {
432                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
433                 if (r < 0)
434                         return r;
435         }
436
437         STRV_FOREACH(fn, files) {
438                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
439                 if (r < 0)
440                         return r;
441         }
442
443         return 0;
444 }
445
446 /* Parse each config file in the directories specified as nulstr. */
447 int config_parse_many_nulstr(
448                 const char *conf_file,
449                 const char *conf_file_dirs,
450                 const char *sections,
451                 ConfigItemLookup lookup,
452                 const void *table,
453                 ConfigParseFlags flags,
454                 void *userdata) {
455
456         _cleanup_strv_free_ char **files = NULL;
457         int r;
458
459         r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
460         if (r < 0)
461                 return r;
462
463         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
464 }
465
466 #if 0 /// UNNEEDED by elogind
467 /* Parse each config file in the directories specified as strv. */
468 int config_parse_many(
469                 const char *conf_file,
470                 const char* const* conf_file_dirs,
471                 const char *dropin_dirname,
472                 const char *sections,
473                 ConfigItemLookup lookup,
474                 const void *table,
475                 ConfigParseFlags flags,
476                 void *userdata) {
477
478         _cleanup_strv_free_ char **dropin_dirs = NULL;
479         _cleanup_strv_free_ char **files = NULL;
480         const char *suffix;
481         int r;
482
483         suffix = strjoina("/", dropin_dirname);
484         r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
485         if (r < 0)
486                 return r;
487
488         r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
489         if (r < 0)
490                 return r;
491
492         return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
493 }
494 #endif // 0
495
496 #define DEFINE_PARSER(type, vartype, conv_func)                         \
497         int config_parse_##type(                                        \
498                         const char *unit,                               \
499                         const char *filename,                           \
500                         unsigned line,                                  \
501                         const char *section,                            \
502                         unsigned section_line,                          \
503                         const char *lvalue,                             \
504                         int ltype,                                      \
505                         const char *rvalue,                             \
506                         void *data,                                     \
507                         void *userdata) {                               \
508                                                                         \
509                 vartype *i = data;                                      \
510                 int r;                                                  \
511                                                                         \
512                 assert(filename);                                       \
513                 assert(lvalue);                                         \
514                 assert(rvalue);                                         \
515                 assert(data);                                           \
516                                                                         \
517                 r = conv_func(rvalue, i);                               \
518                 if (r < 0)                                              \
519                         log_syntax(unit, LOG_ERR, filename, line, r,    \
520                                    "Failed to parse %s value, ignoring: %s", \
521                                    #type, rvalue);                      \
522                                                                         \
523                 return 0;                                               \
524         }                                                               \
525         struct __useless_struct_to_allow_trailing_semicolon__
526
527 DEFINE_PARSER(int, int, safe_atoi);
528 DEFINE_PARSER(long, long, safe_atoli);
529 #if 0 /// UNNEEDED by elogind
530 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
531 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
532 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
533 #endif // 0
534 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
535 DEFINE_PARSER(unsigned, unsigned, safe_atou);
536 DEFINE_PARSER(double, double, safe_atod);
537 #if 0 /// UNNEEDED by elogind
538 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
539 #endif // 0
540 DEFINE_PARSER(sec, usec_t, parse_sec);
541 DEFINE_PARSER(mode, mode_t, parse_mode);
542
543 int config_parse_iec_size(const char* unit,
544                             const char *filename,
545                             unsigned line,
546                             const char *section,
547                             unsigned section_line,
548                             const char *lvalue,
549                             int ltype,
550                             const char *rvalue,
551                             void *data,
552                             void *userdata) {
553
554         size_t *sz = data;
555         uint64_t v;
556         int r;
557
558         assert(filename);
559         assert(lvalue);
560         assert(rvalue);
561         assert(data);
562
563         r = parse_size(rvalue, 1024, &v);
564         if (r < 0 || (uint64_t) (size_t) v != v) {
565                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
566                 return 0;
567         }
568
569         *sz = (size_t) v;
570         return 0;
571 }
572
573 #if 0 /// UNNEEDED by elogind
574 int config_parse_si_size(
575                 const char* unit,
576                 const char *filename,
577                 unsigned line,
578                 const char *section,
579                 unsigned section_line,
580                 const char *lvalue,
581                 int ltype,
582                 const char *rvalue,
583                 void *data,
584                 void *userdata) {
585
586         size_t *sz = data;
587         uint64_t v;
588         int r;
589
590         assert(filename);
591         assert(lvalue);
592         assert(rvalue);
593         assert(data);
594
595         r = parse_size(rvalue, 1000, &v);
596         if (r < 0 || (uint64_t) (size_t) v != v) {
597                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
598                 return 0;
599         }
600
601         *sz = (size_t) v;
602         return 0;
603 }
604
605 int config_parse_iec_uint64(
606                 const char* unit,
607                 const char *filename,
608                 unsigned line,
609                 const char *section,
610                 unsigned section_line,
611                 const char *lvalue,
612                 int ltype,
613                 const char *rvalue,
614                 void *data,
615                 void *userdata) {
616
617         uint64_t *bytes = data;
618         int r;
619
620         assert(filename);
621         assert(lvalue);
622         assert(rvalue);
623         assert(data);
624
625         r = parse_size(rvalue, 1024, bytes);
626         if (r < 0)
627                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
628
629         return 0;
630 }
631 #endif // 0
632
633 int config_parse_bool(const char* unit,
634                       const char *filename,
635                       unsigned line,
636                       const char *section,
637                       unsigned section_line,
638                       const char *lvalue,
639                       int ltype,
640                       const char *rvalue,
641                       void *data,
642                       void *userdata) {
643
644         int k;
645         bool *b = data;
646         bool fatal = ltype;
647
648         assert(filename);
649         assert(lvalue);
650         assert(rvalue);
651         assert(data);
652
653         k = parse_boolean(rvalue);
654         if (k < 0) {
655                 log_syntax(unit, LOG_ERR, filename, line, k,
656                            "Failed to parse boolean value%s: %s",
657                            fatal ? "" : ", ignoring", rvalue);
658                 return fatal ? -ENOEXEC : 0;
659         }
660
661         *b = !!k;
662         return 0;
663 }
664
665 #if 0 /// UNNEEDED by elogind
666 int config_parse_tristate(
667                 const char* unit,
668                 const char *filename,
669                 unsigned line,
670                 const char *section,
671                 unsigned section_line,
672                 const char *lvalue,
673                 int ltype,
674                 const char *rvalue,
675                 void *data,
676                 void *userdata) {
677
678         int k, *t = data;
679
680         assert(filename);
681         assert(lvalue);
682         assert(rvalue);
683         assert(data);
684
685         /* A tristate is pretty much a boolean, except that it can
686          * also take the special value -1, indicating "uninitialized",
687          * much like NULL is for a pointer type. */
688
689         k = parse_boolean(rvalue);
690         if (k < 0) {
691                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
692                 return 0;
693         }
694
695         *t = !!k;
696         return 0;
697 }
698 #endif // 0
699
700 int config_parse_string(
701                 const char *unit,
702                 const char *filename,
703                 unsigned line,
704                 const char *section,
705                 unsigned section_line,
706                 const char *lvalue,
707                 int ltype,
708                 const char *rvalue,
709                 void *data,
710                 void *userdata) {
711
712         char **s = data, *n;
713
714         assert(filename);
715         assert(lvalue);
716         assert(rvalue);
717         assert(data);
718
719         if (!utf8_is_valid(rvalue)) {
720                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
721                 return 0;
722         }
723
724         if (isempty(rvalue))
725                 n = NULL;
726         else {
727                 n = strdup(rvalue);
728                 if (!n)
729                         return log_oom();
730         }
731
732         free(*s);
733         *s = n;
734
735         return 0;
736 }
737
738 int config_parse_path(
739                 const char *unit,
740                 const char *filename,
741                 unsigned line,
742                 const char *section,
743                 unsigned section_line,
744                 const char *lvalue,
745                 int ltype,
746                 const char *rvalue,
747                 void *data,
748                 void *userdata) {
749
750         char **s = data, *n;
751         bool fatal = ltype;
752
753         assert(filename);
754         assert(lvalue);
755         assert(rvalue);
756         assert(data);
757
758         if (isempty(rvalue)) {
759                 n = NULL;
760                 goto finalize;
761         }
762
763         if (!utf8_is_valid(rvalue)) {
764                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
765                 return fatal ? -ENOEXEC : 0;
766         }
767
768         if (!path_is_absolute(rvalue)) {
769                 log_syntax(unit, LOG_ERR, filename, line, 0,
770                            "Not an absolute path%s: %s",
771                            fatal ? "" : ", ignoring", rvalue);
772                 return fatal ? -ENOEXEC : 0;
773         }
774
775         n = strdup(rvalue);
776         if (!n)
777                 return log_oom();
778
779         path_kill_slashes(n);
780
781 finalize:
782         free(*s);
783         *s = n;
784
785         return 0;
786 }
787
788 int config_parse_strv(
789                 const char *unit,
790                 const char *filename,
791                 unsigned line,
792                 const char *section,
793                 unsigned section_line,
794                 const char *lvalue,
795                 int ltype,
796                 const char *rvalue,
797                 void *data,
798                 void *userdata) {
799
800         char ***sv = data;
801         int r;
802
803         assert(filename);
804         assert(lvalue);
805         assert(rvalue);
806         assert(data);
807
808         if (isempty(rvalue)) {
809                 *sv = strv_free(*sv);
810                 return 0;
811         }
812
813         for (;;) {
814                 char *word = NULL;
815
816                 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
817                 if (r == 0)
818                         break;
819                 if (r == -ENOMEM)
820                         return log_oom();
821                 if (r < 0) {
822                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
823                         break;
824                 }
825
826                 if (!utf8_is_valid(word)) {
827                         log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
828                         free(word);
829                         continue;
830                 }
831
832                 r = strv_consume(sv, word);
833                 if (r < 0)
834                         return log_oom();
835         }
836
837         return 0;
838 }
839
840 #if 0 /// UNNEEDED by elogind
841 int config_parse_log_facility(
842                 const char *unit,
843                 const char *filename,
844                 unsigned line,
845                 const char *section,
846                 unsigned section_line,
847                 const char *lvalue,
848                 int ltype,
849                 const char *rvalue,
850                 void *data,
851                 void *userdata) {
852
853         int *o = data, x;
854
855         assert(filename);
856         assert(lvalue);
857         assert(rvalue);
858         assert(data);
859
860         x = log_facility_unshifted_from_string(rvalue);
861         if (x < 0) {
862                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
863                 return 0;
864         }
865
866         *o = (x << 3) | LOG_PRI(*o);
867
868         return 0;
869 }
870 #endif // 0
871
872 int config_parse_log_level(
873                 const char *unit,
874                 const char *filename,
875                 unsigned line,
876                 const char *section,
877                 unsigned section_line,
878                 const char *lvalue,
879                 int ltype,
880                 const char *rvalue,
881                 void *data,
882                 void *userdata) {
883
884         int *o = data, x;
885
886         assert(filename);
887         assert(lvalue);
888         assert(rvalue);
889         assert(data);
890
891         x = log_level_from_string(rvalue);
892         if (x < 0) {
893                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
894                 return 0;
895         }
896
897         if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
898                 *o = x;
899         else
900                 *o = (*o & LOG_FACMASK) | x;
901
902         return 0;
903 }
904
905 int config_parse_signal(
906                 const char *unit,
907                 const char *filename,
908                 unsigned line,
909                 const char *section,
910                 unsigned section_line,
911                 const char *lvalue,
912                 int ltype,
913                 const char *rvalue,
914                 void *data,
915                 void *userdata) {
916
917         int *sig = data, r;
918
919         assert(filename);
920         assert(lvalue);
921         assert(rvalue);
922         assert(sig);
923
924         r = signal_from_string_try_harder(rvalue);
925         if (r <= 0) {
926                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
927                 return 0;
928         }
929
930         *sig = r;
931         return 0;
932 }
933
934 #if 0 /// UNNEEDED by elogind
935 int config_parse_personality(
936                 const char *unit,
937                 const char *filename,
938                 unsigned line,
939                 const char *section,
940                 unsigned section_line,
941                 const char *lvalue,
942                 int ltype,
943                 const char *rvalue,
944                 void *data,
945                 void *userdata) {
946
947         unsigned long *personality = data, p;
948
949         assert(filename);
950         assert(lvalue);
951         assert(rvalue);
952         assert(personality);
953
954         if (isempty(rvalue))
955                 p = PERSONALITY_INVALID;
956         else {
957                 p = personality_from_string(rvalue);
958                 if (p == PERSONALITY_INVALID) {
959                         log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
960                         return 0;
961                 }
962         }
963
964         *personality = p;
965         return 0;
966 }
967
968 int config_parse_ifname(
969                 const char *unit,
970                 const char *filename,
971                 unsigned line,
972                 const char *section,
973                 unsigned section_line,
974                 const char *lvalue,
975                 int ltype,
976                 const char *rvalue,
977                 void *data,
978                 void *userdata) {
979
980         char **s = data;
981         int r;
982
983         assert(filename);
984         assert(lvalue);
985         assert(rvalue);
986         assert(data);
987
988         if (isempty(rvalue)) {
989                 *s = mfree(*s);
990                 return 0;
991         }
992
993         if (!ifname_valid(rvalue)) {
994                 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
995                 return 0;
996         }
997
998         r = free_and_strdup(s, rvalue);
999         if (r < 0)
1000                 return log_oom();
1001
1002         return 0;
1003 }
1004
1005 int config_parse_ip_port(
1006                 const char *unit,
1007                 const char *filename,
1008                 unsigned line,
1009                 const char *section,
1010                 unsigned section_line,
1011                 const char *lvalue,
1012                 int ltype,
1013                 const char *rvalue,
1014                 void *data,
1015                 void *userdata) {
1016
1017         uint16_t *s = data;
1018         uint16_t port;
1019         int r;
1020
1021         assert(filename);
1022         assert(lvalue);
1023         assert(rvalue);
1024         assert(data);
1025
1026         if (isempty(rvalue)) {
1027                 *s = 0;
1028                 return 0;
1029         }
1030
1031         r = parse_ip_port(rvalue, &port);
1032         if (r < 0) {
1033                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1034                 return 0;
1035         }
1036
1037         *s = port;
1038
1039         return 0;
1040 }
1041 #endif // 0