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