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