chiark / gitweb /
cgroup: never migrate kernel threads out of the root cgroup
[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 <stdlib.h>
26
27 #include "conf-parser.h"
28 #include "conf-files.h"
29 #include "util.h"
30 #include "macro.h"
31 #include "strv.h"
32 #include "log.h"
33 #include "utf8.h"
34 #include "path-util.h"
35 #include "sd-messages.h"
36
37 int config_item_table_lookup(
38                 const void *table,
39                 const char *section,
40                 const char *lvalue,
41                 ConfigParserCallback *func,
42                 int *ltype,
43                 void **data,
44                 void *userdata) {
45
46         const ConfigTableItem *t;
47
48         assert(table);
49         assert(lvalue);
50         assert(func);
51         assert(ltype);
52         assert(data);
53
54         for (t = table; t->lvalue; t++) {
55
56                 if (!streq(lvalue, t->lvalue))
57                         continue;
58
59                 if (!streq_ptr(section, t->section))
60                         continue;
61
62                 *func = t->parse;
63                 *ltype = t->ltype;
64                 *data = t->data;
65                 return 1;
66         }
67
68         return 0;
69 }
70
71 int config_item_perf_lookup(
72                 const void *table,
73                 const char *section,
74                 const char *lvalue,
75                 ConfigParserCallback *func,
76                 int *ltype,
77                 void **data,
78                 void *userdata) {
79
80         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
81         const ConfigPerfItem *p;
82
83         assert(table);
84         assert(lvalue);
85         assert(func);
86         assert(ltype);
87         assert(data);
88
89         if (!section)
90                 p = lookup(lvalue, strlen(lvalue));
91         else {
92                 char *key;
93
94                 key = strjoin(section, ".", lvalue, NULL);
95                 if (!key)
96                         return -ENOMEM;
97
98                 p = lookup(key, strlen(key));
99                 free(key);
100         }
101
102         if (!p)
103                 return 0;
104
105         *func = p->parse;
106         *ltype = p->ltype;
107         *data = (uint8_t*) userdata + p->offset;
108         return 1;
109 }
110
111 /* Run the user supplied parser for an assignment */
112 static int next_assignment(const char *unit,
113                            const char *filename,
114                            unsigned line,
115                            ConfigItemLookup lookup,
116                            const void *table,
117                            const char *section,
118                            unsigned section_line,
119                            const char *lvalue,
120                            const char *rvalue,
121                            bool relaxed,
122                            void *userdata) {
123
124         ConfigParserCallback func = NULL;
125         int ltype = 0;
126         void *data = NULL;
127         int r;
128
129         assert(filename);
130         assert(line > 0);
131         assert(lookup);
132         assert(lvalue);
133         assert(rvalue);
134
135         r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
136         if (r < 0)
137                 return r;
138
139         if (r > 0) {
140                 if (func)
141                         return func(unit, filename, line, section, section_line,
142                                     lvalue, ltype, rvalue, data, userdata);
143
144                 return 0;
145         }
146
147         /* Warn about unknown non-extension fields. */
148         if (!relaxed && !startswith(lvalue, "X-"))
149                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
150                            "Unknown lvalue '%s' in section '%s'", lvalue, section);
151
152         return 0;
153 }
154
155 /* Parse a variable assignment line */
156 static int parse_line(const char* unit,
157                       const char *filename,
158                       unsigned line,
159                       const char *sections,
160                       ConfigItemLookup lookup,
161                       const void *table,
162                       bool relaxed,
163                       bool allow_include,
164                       char **section,
165                       unsigned *section_line,
166                       bool *section_ignored,
167                       char *l,
168                       void *userdata) {
169
170         char *e;
171
172         assert(filename);
173         assert(line > 0);
174         assert(lookup);
175         assert(l);
176
177         l = strstrip(l);
178
179         if (!*l)
180                 return 0;
181
182         if (strchr(COMMENTS "\n", *l))
183                 return 0;
184
185         if (startswith(l, ".include ")) {
186                 _cleanup_free_ char *fn = NULL;
187
188                 /* .includes are a bad idea, we only support them here
189                  * for historical reasons. They create cyclic include
190                  * problems and make it difficult to detect
191                  * configuration file changes with an easy
192                  * stat(). Better approaches, such as .d/ drop-in
193                  * snippets exist.
194                  *
195                  * Support for them should be eventually removed. */
196
197                 if (!allow_include) {
198                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
199                                    ".include not allowed here. Ignoring.");
200                         return 0;
201                 }
202
203                 fn = file_in_same_dir(filename, strstrip(l+9));
204                 if (!fn)
205                         return -ENOMEM;
206
207                 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
208         }
209
210         if (*l == '[') {
211                 size_t k;
212                 char *n;
213
214                 k = strlen(l);
215                 assert(k > 0);
216
217                 if (l[k-1] != ']') {
218                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
219                                    "Invalid section header '%s'", l);
220                         return -EBADMSG;
221                 }
222
223                 n = strndup(l+1, k-2);
224                 if (!n)
225                         return -ENOMEM;
226
227                 if (sections && !nulstr_contains(sections, n)) {
228
229                         if (!relaxed && !startswith(n, "X-"))
230                                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
231                                            "Unknown section '%s'. Ignoring.", n);
232
233                         free(n);
234                         free(*section);
235                         *section = NULL;
236                         *section_line = 0;
237                         *section_ignored = true;
238                 } else {
239                         free(*section);
240                         *section = n;
241                         *section_line = line;
242                         *section_ignored = false;
243                 }
244
245                 return 0;
246         }
247
248         if (sections && !*section) {
249
250                 if (!relaxed && !*section_ignored)
251                         log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
252                                    "Assignment outside of section. Ignoring.");
253
254                 return 0;
255         }
256
257         e = strchr(l, '=');
258         if (!e) {
259                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
260                 return -EBADMSG;
261         }
262
263         *e = 0;
264         e++;
265
266         return next_assignment(unit,
267                                filename,
268                                line,
269                                lookup,
270                                table,
271                                *section,
272                                *section_line,
273                                strstrip(l),
274                                strstrip(e),
275                                relaxed,
276                                userdata);
277 }
278
279 /* Go through the file and parse each line */
280 int config_parse(const char *unit,
281                  const char *filename,
282                  FILE *f,
283                  const char *sections,
284                  ConfigItemLookup lookup,
285                  const void *table,
286                  bool relaxed,
287                  bool allow_include,
288                  bool warn,
289                  void *userdata) {
290
291         _cleanup_free_ char *section = NULL, *continuation = NULL;
292         _cleanup_fclose_ FILE *ours = NULL;
293         unsigned line = 0, section_line = 0;
294         bool section_ignored = false;
295         int r;
296
297         assert(filename);
298         assert(lookup);
299
300         if (!f) {
301                 f = ours = fopen(filename, "re");
302                 if (!f) {
303                         /* Only log on request, except for ENOENT,
304                          * since we return 0 to the caller. */
305                         if (warn || errno == ENOENT)
306                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
307                                          "Failed to open configuration file '%s': %m", filename);
308                         return errno == ENOENT ? 0 : -errno;
309                 }
310         }
311
312         fd_warn_permissions(filename, fileno(f));
313
314         while (!feof(f)) {
315                 char l[LINE_MAX], *p, *c = NULL, *e;
316                 bool escaped = false;
317
318                 if (!fgets(l, sizeof(l), f)) {
319                         if (feof(f))
320                                 break;
321
322                         log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
323                         return -errno;
324                 }
325
326                 truncate_nl(l);
327
328                 if (continuation) {
329                         c = strappend(continuation, l);
330                         if (!c) {
331                                 if (warn)
332                                         log_oom();
333                                 return -ENOMEM;
334                         }
335
336                         continuation = mfree(continuation);
337                         p = c;
338                 } else
339                         p = l;
340
341                 for (e = p; *e; e++) {
342                         if (escaped)
343                                 escaped = false;
344                         else if (*e == '\\')
345                                 escaped = true;
346                 }
347
348                 if (escaped) {
349                         *(e-1) = ' ';
350
351                         if (c)
352                                 continuation = c;
353                         else {
354                                 continuation = strdup(l);
355                                 if (!continuation) {
356                                         if (warn)
357                                                 log_oom();
358                                         return -ENOMEM;
359                                 }
360                         }
361
362                         continue;
363                 }
364
365                 r = parse_line(unit,
366                                filename,
367                                ++line,
368                                sections,
369                                lookup,
370                                table,
371                                relaxed,
372                                allow_include,
373                                &section,
374                                &section_line,
375                                &section_ignored,
376                                p,
377                                userdata);
378                 free(c);
379
380                 if (r < 0) {
381                         if (warn)
382                                 log_warning_errno(r, "Failed to parse file '%s': %m",
383                                                   filename);
384                         return r;
385                 }
386         }
387
388         return 0;
389 }
390
391 /* Parse each config file in the specified directories. */
392 int config_parse_many(const char *conf_file,
393                       const char *conf_file_dirs,
394                       const char *sections,
395                       ConfigItemLookup lookup,
396                       const void *table,
397                       bool relaxed,
398                       void *userdata) {
399         _cleanup_strv_free_ char **files = NULL;
400         char **fn;
401         int r;
402
403         r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
404         if (r < 0)
405                 return r;
406
407         if (conf_file) {
408                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
409                 if (r < 0)
410                         return r;
411         }
412
413         STRV_FOREACH(fn, files) {
414                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
415                 if (r < 0)
416                         return r;
417         }
418
419         return 0;
420 }
421
422 #define DEFINE_PARSER(type, vartype, conv_func)                         \
423         int config_parse_##type(const char *unit,                       \
424                                 const char *filename,                   \
425                                 unsigned line,                          \
426                                 const char *section,                    \
427                                 unsigned section_line,                  \
428                                 const char *lvalue,                     \
429                                 int ltype,                              \
430                                 const char *rvalue,                     \
431                                 void *data,                             \
432                                 void *userdata) {                       \
433                                                                         \
434                 vartype *i = data;                                      \
435                 int r;                                                  \
436                                                                         \
437                 assert(filename);                                       \
438                 assert(lvalue);                                         \
439                 assert(rvalue);                                         \
440                 assert(data);                                           \
441                                                                         \
442                 r = conv_func(rvalue, i);                               \
443                 if (r < 0)                                              \
444                         log_syntax(unit, LOG_ERR, filename, line, -r,   \
445                                    "Failed to parse %s value, ignoring: %s", \
446                                    #type, rvalue);                      \
447                                                                         \
448                 return 0;                                               \
449         }
450
451 DEFINE_PARSER(int, int, safe_atoi)
452 DEFINE_PARSER(long, long, safe_atoli)
453 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
454 DEFINE_PARSER(unsigned, unsigned, safe_atou)
455 DEFINE_PARSER(double, double, safe_atod)
456 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
457 DEFINE_PARSER(sec, usec_t, parse_sec)
458
459 int config_parse_iec_size(const char* unit,
460                             const char *filename,
461                             unsigned line,
462                             const char *section,
463                             unsigned section_line,
464                             const char *lvalue,
465                             int ltype,
466                             const char *rvalue,
467                             void *data,
468                             void *userdata) {
469
470         size_t *sz = data;
471         off_t o;
472         int r;
473
474         assert(filename);
475         assert(lvalue);
476         assert(rvalue);
477         assert(data);
478
479         r = parse_size(rvalue, 1024, &o);
480         if (r < 0 || (off_t) (size_t) o != o) {
481                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
482                 return 0;
483         }
484
485         *sz = (size_t) o;
486         return 0;
487 }
488
489 /// UNNEEDED by elogind
490 #if 0
491 int config_parse_si_size(const char* unit,
492                             const char *filename,
493                             unsigned line,
494                             const char *section,
495                             unsigned section_line,
496                             const char *lvalue,
497                             int ltype,
498                             const char *rvalue,
499                             void *data,
500                             void *userdata) {
501
502         size_t *sz = data;
503         off_t o;
504         int r;
505
506         assert(filename);
507         assert(lvalue);
508         assert(rvalue);
509         assert(data);
510
511         r = parse_size(rvalue, 1000, &o);
512         if (r < 0 || (off_t) (size_t) o != o) {
513                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
514                 return 0;
515         }
516
517         *sz = (size_t) o;
518         return 0;
519 }
520
521 int config_parse_iec_off(const char* unit,
522                            const char *filename,
523                            unsigned line,
524                            const char *section,
525                            unsigned section_line,
526                            const char *lvalue,
527                            int ltype,
528                            const char *rvalue,
529                            void *data,
530                            void *userdata) {
531
532         off_t *bytes = data;
533         int r;
534
535         assert(filename);
536         assert(lvalue);
537         assert(rvalue);
538         assert(data);
539
540         assert_cc(sizeof(off_t) == sizeof(uint64_t));
541
542         r = parse_size(rvalue, 1024, bytes);
543         if (r < 0)
544                 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
545
546         return 0;
547 }
548 #endif // 0
549
550 int config_parse_bool(const char* unit,
551                       const char *filename,
552                       unsigned line,
553                       const char *section,
554                       unsigned section_line,
555                       const char *lvalue,
556                       int ltype,
557                       const char *rvalue,
558                       void *data,
559                       void *userdata) {
560
561         int k;
562         bool *b = data;
563
564         assert(filename);
565         assert(lvalue);
566         assert(rvalue);
567         assert(data);
568
569         k = parse_boolean(rvalue);
570         if (k < 0) {
571                 log_syntax(unit, LOG_ERR, filename, line, -k,
572                            "Failed to parse boolean value, ignoring: %s", rvalue);
573                 return 0;
574         }
575
576         *b = !!k;
577         return 0;
578 }
579
580 int config_parse_string(
581                 const char *unit,
582                 const char *filename,
583                 unsigned line,
584                 const char *section,
585                 unsigned section_line,
586                 const char *lvalue,
587                 int ltype,
588                 const char *rvalue,
589                 void *data,
590                 void *userdata) {
591
592         char **s = data, *n;
593
594         assert(filename);
595         assert(lvalue);
596         assert(rvalue);
597         assert(data);
598
599         if (!utf8_is_valid(rvalue)) {
600                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
601                 return 0;
602         }
603
604         if (isempty(rvalue))
605                 n = NULL;
606         else {
607                 n = strdup(rvalue);
608                 if (!n)
609                         return log_oom();
610         }
611
612         free(*s);
613         *s = n;
614
615         return 0;
616 }
617
618 int config_parse_path(
619                 const char *unit,
620                 const char *filename,
621                 unsigned line,
622                 const char *section,
623                 unsigned section_line,
624                 const char *lvalue,
625                 int ltype,
626                 const char *rvalue,
627                 void *data,
628                 void *userdata) {
629
630         char **s = data, *n;
631
632         assert(filename);
633         assert(lvalue);
634         assert(rvalue);
635         assert(data);
636
637         if (!utf8_is_valid(rvalue)) {
638                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
639                 return 0;
640         }
641
642         if (!path_is_absolute(rvalue)) {
643                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
644                 return 0;
645         }
646
647         n = strdup(rvalue);
648         if (!n)
649                 return log_oom();
650
651         path_kill_slashes(n);
652
653         free(*s);
654         *s = n;
655
656         return 0;
657 }
658
659 int config_parse_strv(const char *unit,
660                       const char *filename,
661                       unsigned line,
662                       const char *section,
663                       unsigned section_line,
664                       const char *lvalue,
665                       int ltype,
666                       const char *rvalue,
667                       void *data,
668                       void *userdata) {
669
670         char ***sv = data;
671         const char *word, *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                 char **empty;
682
683                 /* Empty assignment resets the list. As a special rule
684                  * we actually fill in a real empty array here rather
685                  * than NULL, since some code wants to know if
686                  * something was set at all... */
687                 empty = strv_new(NULL, NULL);
688                 if (!empty)
689                         return log_oom();
690
691                 strv_free(*sv);
692                 *sv = empty;
693                 return 0;
694         }
695
696         FOREACH_WORD_QUOTED(word, l, rvalue, state) {
697                 char *n;
698
699                 n = strndup(word, l);
700                 if (!n)
701                         return log_oom();
702
703                 if (!utf8_is_valid(n)) {
704                         log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
705                         free(n);
706                         continue;
707                 }
708
709                 r = strv_consume(sv, n);
710                 if (r < 0)
711                         return log_oom();
712         }
713         if (!isempty(state))
714                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
715                            "Trailing garbage, ignoring.");
716
717         return 0;
718 }
719
720 int config_parse_mode(
721                 const char *unit,
722                 const char *filename,
723                 unsigned line,
724                 const char *section,
725                       unsigned section_line,
726                 const char *lvalue,
727                 int ltype,
728                 const char *rvalue,
729                 void *data,
730                 void *userdata) {
731
732         mode_t *m = data;
733
734         assert(filename);
735         assert(lvalue);
736         assert(rvalue);
737         assert(data);
738
739         if (parse_mode(rvalue, m) < 0) {
740                 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
741                 return 0;
742         }
743
744         return 0;
745 }
746
747 int config_parse_log_facility(
748                 const char *unit,
749                 const char *filename,
750                 unsigned line,
751                 const char *section,
752                 unsigned section_line,
753                 const char *lvalue,
754                 int ltype,
755                 const char *rvalue,
756                 void *data,
757                 void *userdata) {
758
759
760         int *o = data, x;
761
762         assert(filename);
763         assert(lvalue);
764         assert(rvalue);
765         assert(data);
766
767         x = log_facility_unshifted_from_string(rvalue);
768         if (x < 0) {
769                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
770                 return 0;
771         }
772
773         *o = (x << 3) | LOG_PRI(*o);
774
775         return 0;
776 }
777
778 int config_parse_log_level(
779                 const char *unit,
780                 const char *filename,
781                 unsigned line,
782                 const char *section,
783                 unsigned section_line,
784                 const char *lvalue,
785                 int ltype,
786                 const char *rvalue,
787                 void *data,
788                 void *userdata) {
789
790
791         int *o = data, x;
792
793         assert(filename);
794         assert(lvalue);
795         assert(rvalue);
796         assert(data);
797
798         x = log_level_from_string(rvalue);
799         if (x < 0) {
800                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
801                 return 0;
802         }
803
804         *o = (*o & LOG_FACMASK) | x;
805         return 0;
806 }