chiark / gitweb /
conf-parse: rename config_parse_level() to config_parse_log_level()
[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                 /* .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 }