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