]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
core: clean up some confusing regarding SI decimal and IEC binary suffixes for sizes
[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_facility(const char *unit,
838 const char *filename,
839 unsigned line,
840 const char *section,
841 unsigned section_line,
842 const char *lvalue,
843 int ltype,
844 const char *rvalue,
845 void *data,
846 void *userdata) {
847
848
849 int *o = data, x;
850
851 assert(filename);
852 assert(lvalue);
853 assert(rvalue);
854 assert(data);
855
856 x = log_facility_unshifted_from_string(rvalue);
857 if (x < 0) {
858 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
859 "Failed to parse log facility, ignoring: %s", rvalue);
860 return 0;
861 }
862
863 *o = (x << 3) | LOG_PRI(*o);
864
865 return 0;
866 }
867
868 int config_parse_level(const char *unit,
869 const char *filename,
870 unsigned line,
871 const char *section,
872 unsigned section_line,
873 const char *lvalue,
874 int ltype,
875 const char *rvalue,
876 void *data,
877 void *userdata) {
878
879
880 int *o = data, x;
881
882 assert(filename);
883 assert(lvalue);
884 assert(rvalue);
885 assert(data);
886
887 x = log_level_from_string(rvalue);
888 if (x < 0) {
889 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
890 "Failed to parse log level, ignoring: %s", rvalue);
891 return 0;
892 }
893
894 *o = (*o & LOG_FACMASK) | x;
895 return 0;
896 }
897
898 int config_parse_set_status(const char *unit,
899 const char *filename,
900 unsigned line,
901 const char *section,
902 unsigned section_line,
903 const char *lvalue,
904 int ltype,
905 const char *rvalue,
906 void *data,
907 void *userdata) {
908
909 char *w;
910 size_t l;
911 char *state;
912 int r;
913 ExitStatusSet *status_set = data;
914
915 assert(filename);
916 assert(lvalue);
917 assert(rvalue);
918 assert(data);
919
920 if (isempty(rvalue)) {
921 /* Empty assignment resets the list */
922
923 set_free(status_set->signal);
924 set_free(status_set->code);
925
926 status_set->signal = status_set->code = NULL;
927 return 0;
928 }
929
930 FOREACH_WORD(w, l, rvalue, state) {
931 int val;
932 char *temp;
933
934 temp = strndup(w, l);
935 if (!temp)
936 return log_oom();
937
938 r = safe_atoi(temp, &val);
939 if (r < 0) {
940 val = signal_from_string_try_harder(temp);
941 free(temp);
942
943 if (val > 0) {
944 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
945 if (r < 0)
946 return log_oom();
947
948 r = set_put(status_set->signal, INT_TO_PTR(val));
949 if (r < 0) {
950 log_syntax(unit, LOG_ERR, filename, line, -r,
951 "Unable to store: %s", w);
952 return r;
953 }
954 } else {
955 log_syntax(unit, LOG_ERR, filename, line, -val,
956 "Failed to parse value, ignoring: %s", w);
957 return 0;
958 }
959 } else {
960 free(temp);
961
962 if (val < 0 || val > 255)
963 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
964 "Value %d is outside range 0-255, ignoring", val);
965 else {
966 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
967 if (r < 0)
968 return log_oom();
969
970 r = set_put(status_set->code, INT_TO_PTR(val));
971 if (r < 0) {
972 log_syntax(unit, LOG_ERR, filename, line, -r,
973 "Unable to store: %s", w);
974 return r;
975 }
976 }
977 }
978 }
979
980 return 0;
981 }