chiark / gitweb /
Remove dead code and unexport some calls
[elogind.git] / src / shared / conf-parser.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <netinet/ether.h>
28
29 #include "conf-parser.h"
30 #include "util.h"
31 #include "macro.h"
32 #include "strv.h"
33 #include "log.h"
34 #include "utf8.h"
35 #include "path-util.h"
36 #include "set.h"
37 #include "exit-status.h"
38 #include "sd-messages.h"
39
40 int log_syntax_internal(const char *unit, int level,
41                         const char *file, unsigned line, const char *func,
42                         const char *config_file, unsigned config_line,
43                         int error, const char *format, ...) {
44
45         _cleanup_free_ char *msg = NULL;
46         int r;
47         va_list ap;
48
49         va_start(ap, format);
50         r = vasprintf(&msg, format, ap);
51         va_end(ap);
52         if (r < 0)
53                 return log_oom();
54
55         if (unit)
56                 r = log_struct_internal(level,
57                                         file, line, func,
58                                         getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
59                                         MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
60                                         "CONFIG_FILE=%s", config_file,
61                                         "CONFIG_LINE=%u", config_line,
62                                         "ERRNO=%d", error > 0 ? error : EINVAL,
63                                         "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
64                                         NULL);
65         else
66                 r = log_struct_internal(level,
67                                         file, line, func,
68                                         MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
69                                         "CONFIG_FILE=%s", config_file,
70                                         "CONFIG_LINE=%u", config_line,
71                                         "ERRNO=%d", error > 0 ? error : EINVAL,
72                                         "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
73                                         NULL);
74
75         return r;
76 }
77
78 int config_item_table_lookup(
79                 void *table,
80                 const char *section,
81                 const char *lvalue,
82                 ConfigParserCallback *func,
83                 int *ltype,
84                 void **data,
85                 void *userdata) {
86
87         ConfigTableItem *t;
88
89         assert(table);
90         assert(lvalue);
91         assert(func);
92         assert(ltype);
93         assert(data);
94
95         for (t = table; t->lvalue; t++) {
96
97                 if (!streq(lvalue, t->lvalue))
98                         continue;
99
100                 if (!streq_ptr(section, t->section))
101                         continue;
102
103                 *func = t->parse;
104                 *ltype = t->ltype;
105                 *data = t->data;
106                 return 1;
107         }
108
109         return 0;
110 }
111
112 int config_item_perf_lookup(
113                 void *table,
114                 const char *section,
115                 const char *lvalue,
116                 ConfigParserCallback *func,
117                 int *ltype,
118                 void **data,
119                 void *userdata) {
120
121         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
122         const ConfigPerfItem *p;
123
124         assert(table);
125         assert(lvalue);
126         assert(func);
127         assert(ltype);
128         assert(data);
129
130         if (!section)
131                 p = lookup(lvalue, strlen(lvalue));
132         else {
133                 char *key;
134
135                 key = strjoin(section, ".", lvalue, NULL);
136                 if (!key)
137                         return -ENOMEM;
138
139                 p = lookup(key, strlen(key));
140                 free(key);
141         }
142
143         if (!p)
144                 return 0;
145
146         *func = p->parse;
147         *ltype = p->ltype;
148         *data = (uint8_t*) userdata + p->offset;
149         return 1;
150 }
151
152 /* Run the user supplied parser for an assignment */
153 static int next_assignment(const char *unit,
154                            const char *filename,
155                            unsigned line,
156                            ConfigItemLookup lookup,
157                            void *table,
158                            const char *section,
159                            const char *lvalue,
160                            const char *rvalue,
161                            bool relaxed,
162                            void *userdata) {
163
164         ConfigParserCallback func = NULL;
165         int ltype = 0;
166         void *data = NULL;
167         int r;
168
169         assert(filename);
170         assert(line > 0);
171         assert(lookup);
172         assert(lvalue);
173         assert(rvalue);
174
175         r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
176         if (r < 0)
177                 return r;
178
179         if (r > 0) {
180                 if (func)
181                         return func(unit, filename, line, section, lvalue, ltype,
182                                     rvalue, data, userdata);
183
184                 return 0;
185         }
186
187         /* Warn about unknown non-extension fields. */
188         if (!relaxed && !startswith(lvalue, "X-"))
189                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
190                            "Unknown lvalue '%s' in section '%s'", lvalue, section);
191
192         return 0;
193 }
194
195 /* Parse a variable assignment line */
196 static int parse_line(const char* unit,
197                       const char *filename,
198                       unsigned line,
199                       const char *sections,
200                       ConfigItemLookup lookup,
201                       void *table,
202                       bool relaxed,
203                       bool allow_include,
204                       char **section,
205                       char *l,
206                       void *userdata) {
207
208         char *e;
209
210         assert(filename);
211         assert(line > 0);
212         assert(lookup);
213         assert(l);
214
215         l = strstrip(l);
216
217         if (!*l)
218                 return 0;
219
220         if (strchr(COMMENTS "\n", *l))
221                 return 0;
222
223         if (startswith(l, ".include ")) {
224                 _cleanup_free_ char *fn = NULL;
225
226                 if (!allow_include) {
227                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
228                                    ".include not allowed here. Ignoring.");
229                         return 0;
230                 }
231
232                 fn = file_in_same_dir(filename, strstrip(l+9));
233                 if (!fn)
234                         return -ENOMEM;
235
236                 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, userdata);
237         }
238
239         if (*l == '[') {
240                 size_t k;
241                 char *n;
242
243                 k = strlen(l);
244                 assert(k > 0);
245
246                 if (l[k-1] != ']') {
247                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
248                                    "Invalid section header '%s'", l);
249                         return -EBADMSG;
250                 }
251
252                 n = strndup(l+1, k-2);
253                 if (!n)
254                         return -ENOMEM;
255
256                 if (sections && !nulstr_contains(sections, n)) {
257
258                         if (!relaxed)
259                                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
260                                            "Unknown section '%s'. Ignoring.", n);
261
262                         free(n);
263                         *section = NULL;
264                 } else {
265                         free(*section);
266                         *section = n;
267                 }
268
269                 return 0;
270         }
271
272         if (sections && !*section) {
273
274                 if (!relaxed)
275                         log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
276                                    "Assignment outside of section. Ignoring.");
277
278                 return 0;
279         }
280
281         e = strchr(l, '=');
282         if (!e) {
283                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
284                 return -EBADMSG;
285         }
286
287         *e = 0;
288         e++;
289
290         return next_assignment(unit,
291                                filename,
292                                line,
293                                lookup,
294                                table,
295                                *section,
296                                strstrip(l),
297                                strstrip(e),
298                                relaxed,
299                                userdata);
300 }
301
302 /* Go through the file and parse each line */
303 int config_parse(const char *unit,
304                  const char *filename,
305                  FILE *f,
306                  const char *sections,
307                  ConfigItemLookup lookup,
308                  void *table,
309                  bool relaxed,
310                  bool allow_include,
311                  void *userdata) {
312
313         _cleanup_free_ char *section = NULL, *continuation = NULL;
314         _cleanup_fclose_ FILE *ours = NULL;
315         unsigned line = 0;
316         int r;
317
318         assert(filename);
319         assert(lookup);
320
321         if (!f) {
322                 f = ours = fopen(filename, "re");
323                 if (!f) {
324                         log_error("Failed to open configuration file '%s': %m", filename);
325                         return -errno;
326                 }
327         }
328
329         while (!feof(f)) {
330                 char l[LINE_MAX], *p, *c = NULL, *e;
331                 bool escaped = false;
332
333                 if (!fgets(l, sizeof(l), f)) {
334                         if (feof(f))
335                                 break;
336
337                         log_error("Failed to read configuration file '%s': %m", filename);
338                         return -errno;
339                 }
340
341                 truncate_nl(l);
342
343                 if (continuation) {
344                         c = strappend(continuation, l);
345                         if (!c)
346                                 return -ENOMEM;
347
348                         free(continuation);
349                         continuation = NULL;
350                         p = c;
351                 } else
352                         p = l;
353
354                 for (e = p; *e; e++) {
355                         if (escaped)
356                                 escaped = false;
357                         else if (*e == '\\')
358                                 escaped = true;
359                 }
360
361                 if (escaped) {
362                         *(e-1) = ' ';
363
364                         if (c)
365                                 continuation = c;
366                         else {
367                                 continuation = strdup(l);
368                                 if (!continuation)
369                                         return -ENOMEM;
370                         }
371
372                         continue;
373                 }
374
375                 r = parse_line(unit,
376                                filename,
377                                ++line,
378                                sections,
379                                lookup,
380                                table,
381                                relaxed,
382                                allow_include,
383                                &section,
384                                p,
385                                userdata);
386                 free(c);
387
388                 if (r < 0)
389                         return r;
390         }
391
392         return 0;
393 }
394
395 #define DEFINE_PARSER(type, vartype, conv_func)                         \
396         int config_parse_##type(const char *unit,                       \
397                                 const char *filename,                   \
398                                 unsigned line,                          \
399                                 const char *section,                    \
400                                 const char *lvalue,                     \
401                                 int ltype,                              \
402                                 const char *rvalue,                     \
403                                 void *data,                             \
404                                 void *userdata) {                       \
405                                                                         \
406                 vartype *i = data;                                      \
407                 int r;                                                  \
408                                                                         \
409                 assert(filename);                                       \
410                 assert(lvalue);                                         \
411                 assert(rvalue);                                         \
412                 assert(data);                                           \
413                                                                         \
414                 r = conv_func(rvalue, i);                               \
415                 if (r < 0)                                              \
416                         log_syntax(unit, LOG_ERR, filename, line, -r,   \
417                                    "Failed to parse %s value, ignoring: %s", \
418                                    #vartype, rvalue);                   \
419                                                                         \
420                 return 0;                                               \
421         }
422
423 DEFINE_PARSER(int, int, safe_atoi)
424 DEFINE_PARSER(long, long, safe_atoli)
425 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
426 DEFINE_PARSER(unsigned, unsigned, safe_atou)
427 DEFINE_PARSER(double, double, safe_atod)
428 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
429 DEFINE_PARSER(sec, usec_t, parse_sec)
430
431
432 int config_parse_bytes_size(const char* unit,
433                             const char *filename,
434                             unsigned line,
435                             const char *section,
436                             const char *lvalue,
437                             int ltype,
438                             const char *rvalue,
439                             void *data,
440                             void *userdata) {
441
442         size_t *sz = data;
443         off_t o;
444         int r;
445
446         assert(filename);
447         assert(lvalue);
448         assert(rvalue);
449         assert(data);
450
451         r = parse_bytes(rvalue, &o);
452         if (r < 0 || (off_t) (size_t) o != o) {
453                 log_syntax(unit, LOG_ERR, filename, line, -r,
454                            "Failed to parse byte value, ignoring: %s", rvalue);
455                 return 0;
456         }
457
458         *sz = (size_t) o;
459         return 0;
460 }
461
462
463 int config_parse_bytes_off(const char* unit,
464                            const char *filename,
465                            unsigned line,
466                            const char *section,
467                            const char *lvalue,
468                            int ltype,
469                            const char *rvalue,
470                            void *data,
471                            void *userdata) {
472
473         off_t *bytes = data;
474         int r;
475
476         assert(filename);
477         assert(lvalue);
478         assert(rvalue);
479         assert(data);
480
481         assert_cc(sizeof(off_t) == sizeof(uint64_t));
482
483         r = parse_bytes(rvalue, bytes);
484         if (r < 0)
485                 log_syntax(unit, LOG_ERR, filename, line, -r,
486                            "Failed to parse bytes value, ignoring: %s", rvalue);
487
488         return 0;
489 }
490
491 int config_parse_bool(const char* unit,
492                       const char *filename,
493                       unsigned line,
494                       const char *section,
495                       const char *lvalue,
496                       int ltype,
497                       const char *rvalue,
498                       void *data,
499                       void *userdata) {
500
501         int k;
502         bool *b = data;
503
504         assert(filename);
505         assert(lvalue);
506         assert(rvalue);
507         assert(data);
508
509         k = parse_boolean(rvalue);
510         if (k < 0) {
511                 log_syntax(unit, LOG_ERR, filename, line, -k,
512                            "Failed to parse boolean value, ignoring: %s", rvalue);
513                 return 0;
514         }
515
516         *b = !!k;
517         return 0;
518 }
519
520 int config_parse_string(const char *unit,
521                         const char *filename,
522                         unsigned line,
523                         const char *section,
524                         const char *lvalue,
525                         int ltype,
526                         const char *rvalue,
527                         void *data,
528                         void *userdata) {
529
530         char **s = data;
531         char *n;
532
533         assert(filename);
534         assert(lvalue);
535         assert(rvalue);
536         assert(data);
537
538         n = strdup(rvalue);
539         if (!n)
540                 return log_oom();
541
542         if (!utf8_is_valid(n)) {
543                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
544                            "String is not UTF-8 clean, ignoring assignment: %s", rvalue);
545                 free(n);
546                 return 0;
547         }
548
549         free(*s);
550         if (*n)
551                 *s = n;
552         else {
553                 free(n);
554                 *s = NULL;
555         }
556
557         return 0;
558 }
559
560 int config_parse_path(const char *unit,
561                       const char *filename,
562                       unsigned line,
563                       const char *section,
564                       const char *lvalue,
565                       int ltype,
566                       const char *rvalue,
567                       void *data,
568                       void *userdata) {
569
570         char **s = data;
571         char *n;
572         int offset;
573
574         assert(filename);
575         assert(lvalue);
576         assert(rvalue);
577         assert(data);
578
579         if (!utf8_is_valid(rvalue)) {
580                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
581                            "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
582                 return 0;
583         }
584
585         offset = rvalue[0] == '-' && (streq(lvalue, "InaccessibleDirectories") ||
586                                       streq(lvalue, "ReadOnlyDirectories"));
587         if (!path_is_absolute(rvalue + offset)) {
588                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
589                            "Not an absolute path, ignoring: %s", rvalue);
590                 return 0;
591         }
592
593         n = strdup(rvalue);
594         if (!n)
595                 return log_oom();
596
597         path_kill_slashes(n);
598
599         free(*s);
600         *s = n;
601
602         return 0;
603 }
604
605 int config_parse_strv(const char *unit,
606                       const char *filename,
607                       unsigned line,
608                       const char *section,
609                       const char *lvalue,
610                       int ltype,
611                       const char *rvalue,
612                       void *data,
613                       void *userdata) {
614
615         char *** sv = data, *w, *state;
616         size_t l;
617         int r;
618
619         assert(filename);
620         assert(lvalue);
621         assert(rvalue);
622         assert(data);
623
624         if (isempty(rvalue)) {
625                 char **empty;
626
627                 /* Empty assignment resets the list. As a special rule
628                  * we actually fill in a real empty array here rather
629                  * than NULL, since some code wants to know if
630                  * something was set at all... */
631                 empty = strv_new(NULL, NULL);
632                 if (!empty)
633                         return log_oom();
634
635                 strv_free(*sv);
636                 *sv = empty;
637                 return 0;
638         }
639
640         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641                 _cleanup_free_ char *n;
642
643                 n = cunescape_length(w, l);
644                 if (!n)
645                         return log_oom();
646
647                 if (!utf8_is_valid(n)) {
648                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
649                                    "String is not UTF-8 clean, ignoring: %s", rvalue);
650                         continue;
651                 }
652
653                 r = strv_extend(sv, n);
654                 if (r < 0)
655                         return log_oom();
656         }
657
658         return 0;
659 }
660
661 int config_parse_path_strv(const char *unit,
662                            const char *filename,
663                            unsigned line,
664                            const char *section,
665                            const char *lvalue,
666                            int ltype,
667                            const char *rvalue,
668                            void *data,
669                            void *userdata) {
670
671         char*** sv = data, *w, *state;
672         size_t l;
673         int r;
674
675         assert(filename);
676         assert(lvalue);
677         assert(rvalue);
678         assert(data);
679
680         if (isempty(rvalue)) {
681                 /* Empty assignment resets the list */
682                 strv_free(*sv);
683                 *sv = NULL;
684                 return 0;
685         }
686
687         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
688                 _cleanup_free_ char *n;
689                 int offset;
690
691                 n = strndup(w, l);
692                 if (!n)
693                         return log_oom();
694
695                 if (!utf8_is_valid(n)) {
696                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
697                                    "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
698                         continue;
699                 }
700
701                 offset = n[0] == '-' && (streq(lvalue, "InaccessibleDirectories") ||
702                                          streq(lvalue, "ReadOnlyDirectories"));
703                 if (!path_is_absolute(n + offset)) {
704                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
705                                    "Not an absolute path, ignoring: %s", rvalue);
706                         continue;
707                 }
708
709                 path_kill_slashes(n);
710                 r = strv_extend(sv, n);
711                 if (r < 0)
712                         return log_oom();
713         }
714
715         return 0;
716 }
717
718 int config_parse_mode(const char *unit,
719                       const char *filename,
720                       unsigned line,
721                       const char *section,
722                       const char *lvalue,
723                       int ltype,
724                       const char *rvalue,
725                       void *data,
726                       void *userdata) {
727
728         mode_t *m = data;
729         long l;
730         char *x = NULL;
731
732         assert(filename);
733         assert(lvalue);
734         assert(rvalue);
735         assert(data);
736
737         errno = 0;
738         l = strtol(rvalue, &x, 8);
739         if (!x || x == rvalue || *x || errno) {
740                 log_syntax(unit, LOG_ERR, filename, line, errno,
741                            "Failed to parse mode value, ignoring: %s", rvalue);
742                 return 0;
743         }
744
745         if (l < 0000 || l > 07777) {
746                 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
747                            "Mode value out of range, ignoring: %s", rvalue);
748                 return 0;
749         }
750
751         *m = (mode_t) l;
752         return 0;
753 }
754
755 int config_parse_facility(const char *unit,
756                           const char *filename,
757                           unsigned line,
758                           const char *section,
759                           const char *lvalue,
760                           int ltype,
761                           const char *rvalue,
762                           void *data,
763                           void *userdata) {
764
765
766         int *o = data, x;
767
768         assert(filename);
769         assert(lvalue);
770         assert(rvalue);
771         assert(data);
772
773         x = log_facility_unshifted_from_string(rvalue);
774         if (x < 0) {
775                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
776                            "Failed to parse log facility, ignoring: %s", rvalue);
777                 return 0;
778         }
779
780         *o = (x << 3) | LOG_PRI(*o);
781
782         return 0;
783 }
784
785 int config_parse_level(const char *unit,
786                        const char *filename,
787                        unsigned line,
788                        const char *section,
789                        const char *lvalue,
790                        int ltype,
791                        const char *rvalue,
792                        void *data,
793                        void *userdata) {
794
795
796         int *o = data, x;
797
798         assert(filename);
799         assert(lvalue);
800         assert(rvalue);
801         assert(data);
802
803         x = log_level_from_string(rvalue);
804         if (x < 0) {
805                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
806                            "Failed to parse log level, ignoring: %s", rvalue);
807                 return 0;
808         }
809
810         *o = (*o & LOG_FACMASK) | x;
811         return 0;
812 }
813
814 int config_parse_set_status(const char *unit,
815                             const char *filename,
816                             unsigned line,
817                             const char *section,
818                             const char *lvalue,
819                             int ltype,
820                             const char *rvalue,
821                             void *data,
822                             void *userdata) {
823
824         char *w;
825         size_t l;
826         char *state;
827         int r;
828         ExitStatusSet *status_set = data;
829
830         assert(filename);
831         assert(lvalue);
832         assert(rvalue);
833         assert(data);
834
835         if (isempty(rvalue)) {
836                 /* Empty assignment resets the list */
837
838                 set_free(status_set->signal);
839                 set_free(status_set->code);
840
841                 status_set->signal = status_set->code = NULL;
842                 return 0;
843         }
844
845         FOREACH_WORD(w, l, rvalue, state) {
846                 int val;
847                 char *temp;
848
849                 temp = strndup(w, l);
850                 if (!temp)
851                         return log_oom();
852
853                 r = safe_atoi(temp, &val);
854                 if (r < 0) {
855                         val = signal_from_string_try_harder(temp);
856                         free(temp);
857
858                         if (val > 0) {
859                                 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
860                                 if (r < 0)
861                                         return log_oom();
862
863                                 r = set_put(status_set->signal, INT_TO_PTR(val));
864                                 if (r < 0) {
865                                         log_syntax(unit, LOG_ERR, filename, line, -r,
866                                                    "Unable to store: %s", w);
867                                         return r;
868                                 }
869                         } else {
870                                 log_syntax(unit, LOG_ERR, filename, line, -val,
871                                            "Failed to parse value, ignoring: %s", w);
872                                 return 0;
873                         }
874                 } else {
875                         free(temp);
876
877                         if (val < 0 || val > 255)
878                                 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
879                                            "Value %d is outside range 0-255, ignoring", val);
880                         else {
881                                 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
882                                 if (r < 0)
883                                         return log_oom();
884
885                                 r = set_put(status_set->code, INT_TO_PTR(val));
886                                 if (r < 0) {
887                                         log_syntax(unit, LOG_ERR, filename, line, -r,
888                                                    "Unable to store: %s", w);
889                                         return r;
890                                 }
891                         }
892                 }
893         }
894
895         return 0;
896 }