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