]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
conf-parse: rename config_parse_level() to config_parse_log_level()
[thirdparty/systemd.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 unsigned section_line,
160 const char *lvalue,
161 const char *rvalue,
162 bool relaxed,
163 void *userdata) {
164
165 ConfigParserCallback func = NULL;
166 int ltype = 0;
167 void *data = NULL;
168 int r;
169
170 assert(filename);
171 assert(line > 0);
172 assert(lookup);
173 assert(lvalue);
174 assert(rvalue);
175
176 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
177 if (r < 0)
178 return r;
179
180 if (r > 0) {
181 if (func)
182 return func(unit, filename, line, section, section_line,
183 lvalue, ltype, rvalue, data, userdata);
184
185 return 0;
186 }
187
188 /* Warn about unknown non-extension fields. */
189 if (!relaxed && !startswith(lvalue, "X-"))
190 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
191 "Unknown lvalue '%s' in section '%s'", lvalue, section);
192
193 return 0;
194 }
195
196 /* Parse a variable assignment line */
197 static int parse_line(const char* unit,
198 const char *filename,
199 unsigned line,
200 const char *sections,
201 ConfigItemLookup lookup,
202 void *table,
203 bool relaxed,
204 bool allow_include,
205 char **section,
206 unsigned *section_line,
207 char *l,
208 void *userdata) {
209
210 char *e;
211
212 assert(filename);
213 assert(line > 0);
214 assert(lookup);
215 assert(l);
216
217 l = strstrip(l);
218
219 if (!*l)
220 return 0;
221
222 if (strchr(COMMENTS "\n", *l))
223 return 0;
224
225 if (startswith(l, ".include ")) {
226 _cleanup_free_ char *fn = NULL;
227
228 /* .includes are a bad idea, we only support them here
229 * for historical reasons. They create cyclic include
230 * problems and make it difficult to detect
231 * configuration file changes with an easy
232 * stat(). Better approaches, such as .d/ drop-in
233 * snippets exist.
234 *
235 * Support for them should be eventually removed. */
236
237 if (!allow_include) {
238 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
239 ".include not allowed here. Ignoring.");
240 return 0;
241 }
242
243 fn = file_in_same_dir(filename, strstrip(l+9));
244 if (!fn)
245 return -ENOMEM;
246
247 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, userdata);
248 }
249
250 if (*l == '[') {
251 size_t k;
252 char *n;
253
254 k = strlen(l);
255 assert(k > 0);
256
257 if (l[k-1] != ']') {
258 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
259 "Invalid section header '%s'", l);
260 return -EBADMSG;
261 }
262
263 n = strndup(l+1, k-2);
264 if (!n)
265 return -ENOMEM;
266
267 if (sections && !nulstr_contains(sections, n)) {
268
269 if (!relaxed)
270 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
271 "Unknown section '%s'. Ignoring.", n);
272
273 free(n);
274 free(*section);
275 *section = NULL;
276 *section_line = 0;
277 } else {
278 free(*section);
279 *section = n;
280 *section_line = line;
281 }
282
283 return 0;
284 }
285
286 if (sections && !*section) {
287
288 if (!relaxed)
289 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
290 "Assignment outside of section. Ignoring.");
291
292 return 0;
293 }
294
295 e = strchr(l, '=');
296 if (!e) {
297 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
298 return -EBADMSG;
299 }
300
301 *e = 0;
302 e++;
303
304 return next_assignment(unit,
305 filename,
306 line,
307 lookup,
308 table,
309 *section,
310 *section_line,
311 strstrip(l),
312 strstrip(e),
313 relaxed,
314 userdata);
315 }
316
317 /* Go through the file and parse each line */
318 int config_parse(const char *unit,
319 const char *filename,
320 FILE *f,
321 const char *sections,
322 ConfigItemLookup lookup,
323 void *table,
324 bool relaxed,
325 bool allow_include,
326 void *userdata) {
327
328 _cleanup_free_ char *section = NULL, *continuation = NULL;
329 _cleanup_fclose_ FILE *ours = NULL;
330 unsigned line = 0, section_line = 0;
331 int r;
332
333 assert(filename);
334 assert(lookup);
335
336 if (!f) {
337 f = ours = fopen(filename, "re");
338 if (!f) {
339 log_error("Failed to open configuration file '%s': %m", filename);
340 return -errno;
341 }
342 }
343
344 fd_warn_permissions(filename, fileno(f));
345
346 while (!feof(f)) {
347 char l[LINE_MAX], *p, *c = NULL, *e;
348 bool escaped = false;
349
350 if (!fgets(l, sizeof(l), f)) {
351 if (feof(f))
352 break;
353
354 log_error("Failed to read configuration file '%s': %m", filename);
355 return -errno;
356 }
357
358 truncate_nl(l);
359
360 if (continuation) {
361 c = strappend(continuation, l);
362 if (!c)
363 return -ENOMEM;
364
365 free(continuation);
366 continuation = NULL;
367 p = c;
368 } else
369 p = l;
370
371 for (e = p; *e; e++) {
372 if (escaped)
373 escaped = false;
374 else if (*e == '\\')
375 escaped = true;
376 }
377
378 if (escaped) {
379 *(e-1) = ' ';
380
381 if (c)
382 continuation = c;
383 else {
384 continuation = strdup(l);
385 if (!continuation)
386 return -ENOMEM;
387 }
388
389 continue;
390 }
391
392 r = parse_line(unit,
393 filename,
394 ++line,
395 sections,
396 lookup,
397 table,
398 relaxed,
399 allow_include,
400 &section,
401 &section_line,
402 p,
403 userdata);
404 free(c);
405
406 if (r < 0)
407 return r;
408 }
409
410 return 0;
411 }
412
413 #define DEFINE_PARSER(type, vartype, conv_func) \
414 int config_parse_##type(const char *unit, \
415 const char *filename, \
416 unsigned line, \
417 const char *section, \
418 unsigned section_line, \
419 const char *lvalue, \
420 int ltype, \
421 const char *rvalue, \
422 void *data, \
423 void *userdata) { \
424 \
425 vartype *i = data; \
426 int r; \
427 \
428 assert(filename); \
429 assert(lvalue); \
430 assert(rvalue); \
431 assert(data); \
432 \
433 r = conv_func(rvalue, i); \
434 if (r < 0) \
435 log_syntax(unit, LOG_ERR, filename, line, -r, \
436 "Failed to parse %s value, ignoring: %s", \
437 #vartype, rvalue); \
438 \
439 return 0; \
440 }
441
442 DEFINE_PARSER(int, int, safe_atoi)
443 DEFINE_PARSER(long, long, safe_atoli)
444 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
445 DEFINE_PARSER(unsigned, unsigned, safe_atou)
446 DEFINE_PARSER(double, double, safe_atod)
447 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
448 DEFINE_PARSER(sec, usec_t, parse_sec)
449
450 int config_parse_iec_size(const char* unit,
451 const char *filename,
452 unsigned line,
453 const char *section,
454 unsigned section_line,
455 const char *lvalue,
456 int ltype,
457 const char *rvalue,
458 void *data,
459 void *userdata) {
460
461 size_t *sz = data;
462 off_t o;
463 int r;
464
465 assert(filename);
466 assert(lvalue);
467 assert(rvalue);
468 assert(data);
469
470 r = parse_size(rvalue, 1024, &o);
471 if (r < 0 || (off_t) (size_t) o != o) {
472 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
473 return 0;
474 }
475
476 *sz = (size_t) o;
477 return 0;
478 }
479
480 int config_parse_si_size(const char* unit,
481 const char *filename,
482 unsigned line,
483 const char *section,
484 unsigned section_line,
485 const char *lvalue,
486 int ltype,
487 const char *rvalue,
488 void *data,
489 void *userdata) {
490
491 size_t *sz = data;
492 off_t o;
493 int r;
494
495 assert(filename);
496 assert(lvalue);
497 assert(rvalue);
498 assert(data);
499
500 r = parse_size(rvalue, 1000, &o);
501 if (r < 0 || (off_t) (size_t) o != o) {
502 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
503 return 0;
504 }
505
506 *sz = (size_t) o;
507 return 0;
508 }
509
510 int config_parse_iec_off(const char* unit,
511 const char *filename,
512 unsigned line,
513 const char *section,
514 unsigned section_line,
515 const char *lvalue,
516 int ltype,
517 const char *rvalue,
518 void *data,
519 void *userdata) {
520
521 off_t *bytes = data;
522 int r;
523
524 assert(filename);
525 assert(lvalue);
526 assert(rvalue);
527 assert(data);
528
529 assert_cc(sizeof(off_t) == sizeof(uint64_t));
530
531 r = parse_size(rvalue, 1024, bytes);
532 if (r < 0)
533 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
534
535 return 0;
536 }
537
538 int config_parse_bool(const char* unit,
539 const char *filename,
540 unsigned line,
541 const char *section,
542 unsigned section_line,
543 const char *lvalue,
544 int ltype,
545 const char *rvalue,
546 void *data,
547 void *userdata) {
548
549 int k;
550 bool *b = data;
551
552 assert(filename);
553 assert(lvalue);
554 assert(rvalue);
555 assert(data);
556
557 k = parse_boolean(rvalue);
558 if (k < 0) {
559 log_syntax(unit, LOG_ERR, filename, line, -k,
560 "Failed to parse boolean value, ignoring: %s", rvalue);
561 return 0;
562 }
563
564 *b = !!k;
565 return 0;
566 }
567
568 int config_parse_show_status(const char* unit,
569 const char *filename,
570 unsigned line,
571 const char *section,
572 unsigned section_line,
573 const char *lvalue,
574 int ltype,
575 const char *rvalue,
576 void *data,
577 void *userdata) {
578
579 int k;
580 ShowStatus *b = data;
581
582 assert(filename);
583 assert(lvalue);
584 assert(rvalue);
585 assert(data);
586
587 k = parse_show_status(rvalue, b);
588 if (k < 0) {
589 log_syntax(unit, LOG_ERR, filename, line, -k,
590 "Failed to parse show status setting, ignoring: %s", rvalue);
591 return 0;
592 }
593
594 return 0;
595 }
596
597 int config_parse_string(const char *unit,
598 const char *filename,
599 unsigned line,
600 const char *section,
601 unsigned section_line,
602 const char *lvalue,
603 int ltype,
604 const char *rvalue,
605 void *data,
606 void *userdata) {
607
608 char **s = data;
609 char *n;
610
611 assert(filename);
612 assert(lvalue);
613 assert(rvalue);
614 assert(data);
615
616 n = strdup(rvalue);
617 if (!n)
618 return log_oom();
619
620 if (!utf8_is_valid(n)) {
621 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
622 "String is not UTF-8 clean, ignoring assignment: %s", rvalue);
623 free(n);
624 return 0;
625 }
626
627 free(*s);
628 if (*n)
629 *s = n;
630 else {
631 free(n);
632 *s = NULL;
633 }
634
635 return 0;
636 }
637
638 int config_parse_path(const char *unit,
639 const char *filename,
640 unsigned line,
641 const char *section,
642 unsigned section_line,
643 const char *lvalue,
644 int ltype,
645 const char *rvalue,
646 void *data,
647 void *userdata) {
648
649 char **s = data;
650 char *n;
651 int offset;
652
653 assert(filename);
654 assert(lvalue);
655 assert(rvalue);
656 assert(data);
657
658 if (!utf8_is_valid(rvalue)) {
659 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
660 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
661 return 0;
662 }
663
664 offset = rvalue[0] == '-' && (streq(lvalue, "InaccessibleDirectories") ||
665 streq(lvalue, "ReadOnlyDirectories"));
666 if (!path_is_absolute(rvalue + offset)) {
667 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
668 "Not an absolute path, ignoring: %s", rvalue);
669 return 0;
670 }
671
672 n = strdup(rvalue);
673 if (!n)
674 return log_oom();
675
676 path_kill_slashes(n);
677
678 free(*s);
679 *s = n;
680
681 return 0;
682 }
683
684 int config_parse_strv(const char *unit,
685 const char *filename,
686 unsigned line,
687 const char *section,
688 unsigned section_line,
689 const char *lvalue,
690 int ltype,
691 const char *rvalue,
692 void *data,
693 void *userdata) {
694
695 char *** sv = data, *w, *state;
696 size_t l;
697 int r;
698
699 assert(filename);
700 assert(lvalue);
701 assert(rvalue);
702 assert(data);
703
704 if (isempty(rvalue)) {
705 char **empty;
706
707 /* Empty assignment resets the list. As a special rule
708 * we actually fill in a real empty array here rather
709 * than NULL, since some code wants to know if
710 * something was set at all... */
711 empty = strv_new(NULL, NULL);
712 if (!empty)
713 return log_oom();
714
715 strv_free(*sv);
716 *sv = empty;
717 return 0;
718 }
719
720 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
721 _cleanup_free_ char *n;
722
723 n = cunescape_length(w, l);
724 if (!n)
725 return log_oom();
726
727 if (!utf8_is_valid(n)) {
728 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
729 "String is not UTF-8 clean, ignoring: %s", rvalue);
730 continue;
731 }
732
733 r = strv_extend(sv, n);
734 if (r < 0)
735 return log_oom();
736 }
737
738 return 0;
739 }
740
741 int config_parse_path_strv(const char *unit,
742 const char *filename,
743 unsigned line,
744 const char *section,
745 unsigned section_line,
746 const char *lvalue,
747 int ltype,
748 const char *rvalue,
749 void *data,
750 void *userdata) {
751
752 char*** sv = data, *w, *state;
753 size_t l;
754 int r;
755
756 assert(filename);
757 assert(lvalue);
758 assert(rvalue);
759 assert(data);
760
761 if (isempty(rvalue)) {
762 /* Empty assignment resets the list */
763 strv_free(*sv);
764 *sv = NULL;
765 return 0;
766 }
767
768 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
769 _cleanup_free_ char *n;
770 int offset;
771
772 n = strndup(w, l);
773 if (!n)
774 return log_oom();
775
776 if (!utf8_is_valid(n)) {
777 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
778 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
779 continue;
780 }
781
782 offset = n[0] == '-' && (streq(lvalue, "InaccessibleDirectories") ||
783 streq(lvalue, "ReadOnlyDirectories"));
784 if (!path_is_absolute(n + offset)) {
785 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
786 "Not an absolute path, ignoring: %s", rvalue);
787 continue;
788 }
789
790 path_kill_slashes(n);
791 r = strv_extend(sv, n);
792 if (r < 0)
793 return log_oom();
794 }
795
796 return 0;
797 }
798
799 int config_parse_mode(const char *unit,
800 const char *filename,
801 unsigned line,
802 const char *section,
803 unsigned section_line,
804 const char *lvalue,
805 int ltype,
806 const char *rvalue,
807 void *data,
808 void *userdata) {
809
810 mode_t *m = data;
811 long l;
812 char *x = NULL;
813
814 assert(filename);
815 assert(lvalue);
816 assert(rvalue);
817 assert(data);
818
819 errno = 0;
820 l = strtol(rvalue, &x, 8);
821 if (!x || x == rvalue || *x || errno) {
822 log_syntax(unit, LOG_ERR, filename, line, errno,
823 "Failed to parse mode value, ignoring: %s", rvalue);
824 return 0;
825 }
826
827 if (l < 0000 || l > 07777) {
828 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
829 "Mode value out of range, ignoring: %s", rvalue);
830 return 0;
831 }
832
833 *m = (mode_t) l;
834 return 0;
835 }
836
837 int config_parse_log_facility(
838 const char *unit,
839 const char *filename,
840 unsigned line,
841 const char *section,
842 unsigned section_line,
843 const char *lvalue,
844 int ltype,
845 const char *rvalue,
846 void *data,
847 void *userdata) {
848
849
850 int *o = data, x;
851
852 assert(filename);
853 assert(lvalue);
854 assert(rvalue);
855 assert(data);
856
857 x = log_facility_unshifted_from_string(rvalue);
858 if (x < 0) {
859 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
860 "Failed to parse log facility, ignoring: %s", rvalue);
861 return 0;
862 }
863
864 *o = (x << 3) | LOG_PRI(*o);
865
866 return 0;
867 }
868
869 int config_parse_log_level(
870 const char *unit,
871 const char *filename,
872 unsigned line,
873 const char *section,
874 unsigned section_line,
875 const char *lvalue,
876 int ltype,
877 const char *rvalue,
878 void *data,
879 void *userdata) {
880
881
882 int *o = data, x;
883
884 assert(filename);
885 assert(lvalue);
886 assert(rvalue);
887 assert(data);
888
889 x = log_level_from_string(rvalue);
890 if (x < 0) {
891 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
892 "Failed to parse log level, ignoring: %s", rvalue);
893 return 0;
894 }
895
896 *o = (*o & LOG_FACMASK) | x;
897 return 0;
898 }
899
900 int config_parse_set_status(const char *unit,
901 const char *filename,
902 unsigned line,
903 const char *section,
904 unsigned section_line,
905 const char *lvalue,
906 int ltype,
907 const char *rvalue,
908 void *data,
909 void *userdata) {
910
911 char *w;
912 size_t l;
913 char *state;
914 int r;
915 ExitStatusSet *status_set = data;
916
917 assert(filename);
918 assert(lvalue);
919 assert(rvalue);
920 assert(data);
921
922 if (isempty(rvalue)) {
923 /* Empty assignment resets the list */
924
925 set_free(status_set->signal);
926 set_free(status_set->code);
927
928 status_set->signal = status_set->code = NULL;
929 return 0;
930 }
931
932 FOREACH_WORD(w, l, rvalue, state) {
933 int val;
934 char *temp;
935
936 temp = strndup(w, l);
937 if (!temp)
938 return log_oom();
939
940 r = safe_atoi(temp, &val);
941 if (r < 0) {
942 val = signal_from_string_try_harder(temp);
943 free(temp);
944
945 if (val > 0) {
946 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
947 if (r < 0)
948 return log_oom();
949
950 r = set_put(status_set->signal, INT_TO_PTR(val));
951 if (r < 0) {
952 log_syntax(unit, LOG_ERR, filename, line, -r,
953 "Unable to store: %s", w);
954 return r;
955 }
956 } else {
957 log_syntax(unit, LOG_ERR, filename, line, -val,
958 "Failed to parse value, ignoring: %s", w);
959 return 0;
960 }
961 } else {
962 free(temp);
963
964 if (val < 0 || val > 255)
965 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
966 "Value %d is outside range 0-255, ignoring", val);
967 else {
968 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
969 if (r < 0)
970 return log_oom();
971
972 r = set_put(status_set->code, INT_TO_PTR(val));
973 if (r < 0) {
974 log_syntax(unit, LOG_ERR, filename, line, -r,
975 "Unable to store: %s", w);
976 return r;
977 }
978 }
979 }
980 }
981
982 return 0;
983 }