]>
Commit | Line | Data |
---|---|---|
d6c9574f | 1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
ed5bcfbe | 2 | |
a7334b09 LP |
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 | |
5430f7f2 LP |
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 | |
a7334b09 LP |
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 | |
5430f7f2 | 16 | Lesser General Public License for more details. |
a7334b09 | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
a7334b09 LP |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
ed5bcfbe LP |
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" | |
57d42a5f | 31 | #include "strv.h" |
16354eff | 32 | #include "log.h" |
7f110ff9 | 33 | #include "utf8.h" |
9eb977db | 34 | #include "path-util.h" |
96342de6 LN |
35 | #include "set.h" |
36 | #include "exit-status.h" | |
e8e581bf ZJS |
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 | log_info("logged here: '%s': %d", msg, r); | |
74 | return r; | |
75 | } | |
ed5bcfbe | 76 | |
f975e971 LP |
77 | int config_item_table_lookup( |
78 | void *table, | |
ed5bcfbe | 79 | const char *section, |
ed5bcfbe | 80 | const char *lvalue, |
f975e971 LP |
81 | ConfigParserCallback *func, |
82 | int *ltype, | |
83 | void **data, | |
ed5bcfbe LP |
84 | void *userdata) { |
85 | ||
f975e971 LP |
86 | ConfigTableItem *t; |
87 | ||
88 | assert(table); | |
ed5bcfbe | 89 | assert(lvalue); |
f975e971 LP |
90 | assert(func); |
91 | assert(ltype); | |
92 | assert(data); | |
ed5bcfbe | 93 | |
f975e971 | 94 | for (t = table; t->lvalue; t++) { |
ed5bcfbe | 95 | |
f975e971 | 96 | if (!streq(lvalue, t->lvalue)) |
ed5bcfbe LP |
97 | continue; |
98 | ||
f975e971 | 99 | if (!streq_ptr(section, t->section)) |
ed5bcfbe LP |
100 | continue; |
101 | ||
f975e971 LP |
102 | *func = t->parse; |
103 | *ltype = t->ltype; | |
104 | *data = t->data; | |
105 | return 1; | |
106 | } | |
ed5bcfbe | 107 | |
f975e971 LP |
108 | return 0; |
109 | } | |
10e87ee7 | 110 | |
f975e971 LP |
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 | ||
b7def684 | 134 | key = strjoin(section, ".", lvalue, NULL); |
44d91056 | 135 | if (!key) |
f975e971 LP |
136 | return -ENOMEM; |
137 | ||
138 | p = lookup(key, strlen(key)); | |
139 | free(key); | |
ed5bcfbe LP |
140 | } |
141 | ||
f975e971 LP |
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 */ | |
e8e581bf ZJS |
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) { | |
f975e971 LP |
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, <ype, &data, userdata); | |
175 | if (r < 0) | |
176 | return r; | |
177 | ||
d937fbbd LP |
178 | if (r > 0) { |
179 | if (func) | |
e8e581bf ZJS |
180 | return func(unit, filename, line, section, lvalue, ltype, |
181 | rvalue, data, userdata); | |
d937fbbd LP |
182 | |
183 | return 0; | |
184 | } | |
f975e971 | 185 | |
46205bb6 | 186 | /* Warn about unknown non-extension fields. */ |
10e87ee7 | 187 | if (!relaxed && !startswith(lvalue, "X-")) |
e8e581bf ZJS |
188 | log_syntax(unit, LOG_WARNING, filename, line, EINVAL, |
189 | "Unknown lvalue '%s' in section '%s'", lvalue, section); | |
46205bb6 | 190 | |
f1857be0 | 191 | return 0; |
ed5bcfbe LP |
192 | } |
193 | ||
ed5bcfbe | 194 | /* Parse a variable assignment line */ |
e8e581bf ZJS |
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 | char **section, | |
203 | char *l, | |
204 | void *userdata) { | |
f975e971 | 205 | |
b2aa81ef | 206 | char *e; |
ed5bcfbe | 207 | |
f975e971 LP |
208 | assert(filename); |
209 | assert(line > 0); | |
210 | assert(lookup); | |
211 | assert(l); | |
212 | ||
b2aa81ef | 213 | l = strstrip(l); |
ed5bcfbe | 214 | |
b2aa81ef | 215 | if (!*l) |
ed5bcfbe | 216 | return 0; |
1ea86b18 | 217 | |
d3b6d0c2 | 218 | if (strchr(COMMENTS "\n", *l)) |
1ea86b18 | 219 | return 0; |
ed5bcfbe | 220 | |
b2aa81ef | 221 | if (startswith(l, ".include ")) { |
7fd1b19b | 222 | _cleanup_free_ char *fn; |
ed5bcfbe | 223 | |
f975e971 LP |
224 | fn = file_in_same_dir(filename, strstrip(l+9)); |
225 | if (!fn) | |
b2aa81ef | 226 | return -ENOMEM; |
ed5bcfbe | 227 | |
e8e581bf | 228 | return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, userdata); |
ed5bcfbe LP |
229 | } |
230 | ||
b2aa81ef | 231 | if (*l == '[') { |
ed5bcfbe LP |
232 | size_t k; |
233 | char *n; | |
234 | ||
b2aa81ef | 235 | k = strlen(l); |
ed5bcfbe LP |
236 | assert(k > 0); |
237 | ||
b2aa81ef | 238 | if (l[k-1] != ']') { |
e8e581bf ZJS |
239 | log_syntax(unit, LOG_ERR, filename, line, EBADMSG, |
240 | "Invalid section header '%s'", l); | |
ed5bcfbe LP |
241 | return -EBADMSG; |
242 | } | |
243 | ||
f975e971 LP |
244 | n = strndup(l+1, k-2); |
245 | if (!n) | |
ed5bcfbe LP |
246 | return -ENOMEM; |
247 | ||
f975e971 | 248 | if (sections && !nulstr_contains(sections, n)) { |
42f4e3c4 | 249 | |
f975e971 | 250 | if (!relaxed) |
e8e581bf ZJS |
251 | log_syntax(unit, LOG_WARNING, filename, line, EINVAL, |
252 | "Unknown section '%s'. Ignoring.", n); | |
f975e971 LP |
253 | |
254 | free(n); | |
255 | *section = NULL; | |
256 | } else { | |
257 | free(*section); | |
258 | *section = n; | |
259 | } | |
ed5bcfbe LP |
260 | |
261 | return 0; | |
262 | } | |
263 | ||
62f168a0 LP |
264 | if (sections && !*section) { |
265 | ||
266 | if (!relaxed) | |
e8e581bf ZJS |
267 | log_syntax(unit, LOG_WARNING, filename, line, EINVAL, |
268 | "Assignment outside of section. Ignoring."); | |
62f168a0 | 269 | |
10e87ee7 | 270 | return 0; |
62f168a0 | 271 | } |
10e87ee7 | 272 | |
f975e971 LP |
273 | e = strchr(l, '='); |
274 | if (!e) { | |
e8e581bf | 275 | log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='."); |
ed5bcfbe LP |
276 | return -EBADMSG; |
277 | } | |
278 | ||
279 | *e = 0; | |
280 | e++; | |
281 | ||
e8e581bf ZJS |
282 | return next_assignment(unit, |
283 | filename, | |
284 | line, | |
285 | lookup, | |
286 | table, | |
287 | *section, | |
288 | strstrip(l), | |
289 | strstrip(e), | |
290 | relaxed, | |
291 | userdata); | |
ed5bcfbe LP |
292 | } |
293 | ||
294 | /* Go through the file and parse each line */ | |
e8e581bf ZJS |
295 | int config_parse(const char *unit, |
296 | const char *filename, | |
297 | FILE *f, | |
298 | const char *sections, | |
299 | ConfigItemLookup lookup, | |
300 | void *table, | |
301 | bool relaxed, | |
302 | void *userdata) { | |
f975e971 | 303 | |
ed5bcfbe | 304 | unsigned line = 0; |
7fd1b19b HH |
305 | _cleanup_free_ char *section = NULL, *continuation = NULL; |
306 | _cleanup_fclose_ FILE *ours = NULL; | |
ed5bcfbe LP |
307 | int r; |
308 | ||
309 | assert(filename); | |
f975e971 | 310 | assert(lookup); |
ed5bcfbe | 311 | |
87f0e418 | 312 | if (!f) { |
245802dd | 313 | f = ours = fopen(filename, "re"); |
f975e971 | 314 | if (!f) { |
245802dd ZJS |
315 | log_error("Failed to open configuration file '%s': %m", filename); |
316 | return -errno; | |
87f0e418 | 317 | } |
ed5bcfbe LP |
318 | } |
319 | ||
320 | while (!feof(f)) { | |
3dab2943 LP |
321 | char l[LINE_MAX], *p, *c = NULL, *e; |
322 | bool escaped = false; | |
ed5bcfbe LP |
323 | |
324 | if (!fgets(l, sizeof(l), f)) { | |
325 | if (feof(f)) | |
326 | break; | |
327 | ||
245802dd ZJS |
328 | log_error("Failed to read configuration file '%s': %m", filename); |
329 | return -errno; | |
ed5bcfbe LP |
330 | } |
331 | ||
3dab2943 LP |
332 | truncate_nl(l); |
333 | ||
334 | if (continuation) { | |
f975e971 | 335 | c = strappend(continuation, l); |
245802dd ZJS |
336 | if (!c) |
337 | return -ENOMEM; | |
3dab2943 LP |
338 | |
339 | free(continuation); | |
340 | continuation = NULL; | |
341 | p = c; | |
342 | } else | |
343 | p = l; | |
344 | ||
345 | for (e = p; *e; e++) { | |
346 | if (escaped) | |
347 | escaped = false; | |
348 | else if (*e == '\\') | |
349 | escaped = true; | |
350 | } | |
351 | ||
352 | if (escaped) { | |
353 | *(e-1) = ' '; | |
354 | ||
355 | if (c) | |
356 | continuation = c; | |
f975e971 LP |
357 | else { |
358 | continuation = strdup(l); | |
245802dd ZJS |
359 | if (!continuation) |
360 | return -ENOMEM; | |
3dab2943 LP |
361 | } |
362 | ||
363 | continue; | |
364 | } | |
365 | ||
e8e581bf ZJS |
366 | r = parse_line(unit, |
367 | filename, | |
368 | ++line, | |
369 | sections, | |
370 | lookup, | |
371 | table, | |
372 | relaxed, | |
373 | §ion, | |
374 | p, | |
375 | userdata); | |
3dab2943 LP |
376 | free(c); |
377 | ||
378 | if (r < 0) | |
245802dd | 379 | return r; |
ed5bcfbe LP |
380 | } |
381 | ||
245802dd | 382 | return 0; |
ed5bcfbe LP |
383 | } |
384 | ||
eb3491d9 | 385 | #define DEFINE_PARSER(type, vartype, conv_func) \ |
e8e581bf ZJS |
386 | int config_parse_##type(const char *unit, \ |
387 | const char *filename, \ | |
eb3491d9 ZJS |
388 | unsigned line, \ |
389 | const char *section, \ | |
390 | const char *lvalue, \ | |
391 | int ltype, \ | |
392 | const char *rvalue, \ | |
393 | void *data, \ | |
394 | void *userdata) { \ | |
395 | \ | |
396 | vartype *i = data; \ | |
397 | int r; \ | |
398 | \ | |
399 | assert(filename); \ | |
400 | assert(lvalue); \ | |
401 | assert(rvalue); \ | |
402 | assert(data); \ | |
403 | \ | |
404 | r = conv_func(rvalue, i); \ | |
405 | if (r < 0) \ | |
e8e581bf ZJS |
406 | log_syntax(unit, LOG_ERR, filename, line, -r, \ |
407 | "Failed to parse %s value, ignoring: %s", \ | |
408 | #vartype, rvalue); \ | |
eb3491d9 ZJS |
409 | \ |
410 | return 0; \ | |
ed5bcfbe LP |
411 | } |
412 | ||
eb3491d9 ZJS |
413 | DEFINE_PARSER(int, int, safe_atoi) |
414 | DEFINE_PARSER(long, long, safe_atoli) | |
415 | DEFINE_PARSER(uint64, uint64_t, safe_atou64) | |
416 | DEFINE_PARSER(unsigned, unsigned, safe_atou) | |
417 | DEFINE_PARSER(double, double, safe_atod) | |
418 | DEFINE_PARSER(nsec, nsec_t, parse_nsec) | |
419 | DEFINE_PARSER(sec, usec_t, parse_sec) | |
ed5bcfbe | 420 | |
f7900e25 | 421 | |
e8e581bf ZJS |
422 | int config_parse_bytes_size(const char* unit, |
423 | const char *filename, | |
424 | unsigned line, | |
425 | const char *section, | |
426 | const char *lvalue, | |
427 | int ltype, | |
428 | const char *rvalue, | |
429 | void *data, | |
430 | void *userdata) { | |
ed5bcfbe LP |
431 | |
432 | size_t *sz = data; | |
9ba1a159 | 433 | off_t o; |
e8e581bf | 434 | int r; |
ed5bcfbe LP |
435 | |
436 | assert(filename); | |
437 | assert(lvalue); | |
438 | assert(rvalue); | |
439 | assert(data); | |
440 | ||
e8e581bf ZJS |
441 | r = parse_bytes(rvalue, &o); |
442 | if (r < 0 || (off_t) (size_t) o != o) { | |
443 | log_syntax(unit, LOG_ERR, filename, line, -r, | |
444 | "Failed to parse byte value, ignoring: %s", rvalue); | |
9ba1a159 LP |
445 | return 0; |
446 | } | |
447 | ||
448 | *sz = (size_t) o; | |
449 | return 0; | |
450 | } | |
451 | ||
452 | ||
e8e581bf ZJS |
453 | int config_parse_bytes_off(const char* unit, |
454 | const char *filename, | |
455 | unsigned line, | |
456 | const char *section, | |
457 | const char *lvalue, | |
458 | int ltype, | |
459 | const char *rvalue, | |
460 | void *data, | |
461 | void *userdata) { | |
9ba1a159 LP |
462 | |
463 | off_t *bytes = data; | |
e8e581bf | 464 | int r; |
9ba1a159 LP |
465 | |
466 | assert(filename); | |
467 | assert(lvalue); | |
468 | assert(rvalue); | |
469 | assert(data); | |
470 | ||
471 | assert_cc(sizeof(off_t) == sizeof(uint64_t)); | |
472 | ||
e8e581bf ZJS |
473 | r = parse_bytes(rvalue, bytes); |
474 | if (r < 0) | |
475 | log_syntax(unit, LOG_ERR, filename, line, -r, | |
476 | "Failed to parse bytes value, ignoring: %s", rvalue); | |
ed5bcfbe | 477 | |
ed5bcfbe LP |
478 | return 0; |
479 | } | |
480 | ||
e8e581bf ZJS |
481 | int config_parse_bool(const char* unit, |
482 | const char *filename, | |
483 | unsigned line, | |
484 | const char *section, | |
485 | const char *lvalue, | |
486 | int ltype, | |
487 | const char *rvalue, | |
488 | void *data, | |
489 | void *userdata) { | |
ed5bcfbe LP |
490 | |
491 | int k; | |
492 | bool *b = data; | |
493 | ||
494 | assert(filename); | |
495 | assert(lvalue); | |
496 | assert(rvalue); | |
497 | assert(data); | |
498 | ||
e8e581bf ZJS |
499 | k = parse_boolean(rvalue); |
500 | if (k < 0) { | |
501 | log_syntax(unit, LOG_ERR, filename, line, -k, | |
502 | "Failed to parse boolean value, ignoring: %s", rvalue); | |
f975e971 | 503 | return 0; |
ed5bcfbe LP |
504 | } |
505 | ||
506 | *b = !!k; | |
507 | return 0; | |
508 | } | |
509 | ||
e8e581bf ZJS |
510 | int config_parse_tristate(const char *unit, |
511 | const char *filename, | |
512 | unsigned line, | |
513 | const char *section, | |
514 | const char *lvalue, | |
515 | int ltype, | |
516 | const char *rvalue, | |
517 | void *data, | |
518 | void *userdata) { | |
8d53b453 LP |
519 | |
520 | int k; | |
521 | int *b = data; | |
522 | ||
523 | assert(filename); | |
524 | assert(lvalue); | |
525 | assert(rvalue); | |
526 | assert(data); | |
527 | ||
528 | /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */ | |
529 | ||
530 | k = parse_boolean(rvalue); | |
531 | if (k < 0) { | |
e8e581bf ZJS |
532 | log_syntax(unit, LOG_ERR, filename, line, -k, |
533 | "Failed to parse boolean value, ignoring: %s", rvalue); | |
8d53b453 LP |
534 | return 0; |
535 | } | |
536 | ||
537 | *b = !!k; | |
538 | return 0; | |
539 | } | |
540 | ||
e8e581bf ZJS |
541 | int config_parse_string(const char *unit, |
542 | const char *filename, | |
543 | unsigned line, | |
544 | const char *section, | |
545 | const char *lvalue, | |
546 | int ltype, | |
547 | const char *rvalue, | |
548 | void *data, | |
549 | void *userdata) { | |
ed5bcfbe LP |
550 | |
551 | char **s = data; | |
552 | char *n; | |
553 | ||
554 | assert(filename); | |
555 | assert(lvalue); | |
556 | assert(rvalue); | |
557 | assert(data); | |
558 | ||
faa368e3 | 559 | n = strdup(rvalue); |
7f110ff9 | 560 | if (!n) |
74051b9b | 561 | return log_oom(); |
7f110ff9 LP |
562 | |
563 | if (!utf8_is_valid(n)) { | |
e8e581bf ZJS |
564 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
565 | "String is not UTF-8 clean, ignoring assignment: %s", rvalue); | |
7f110ff9 LP |
566 | free(n); |
567 | return 0; | |
568 | } | |
ed5bcfbe LP |
569 | |
570 | free(*s); | |
7f110ff9 LP |
571 | if (*n) |
572 | *s = n; | |
573 | else { | |
574 | free(n); | |
575 | *s = NULL; | |
576 | } | |
ed5bcfbe LP |
577 | |
578 | return 0; | |
579 | } | |
57d42a5f | 580 | |
e8e581bf ZJS |
581 | int config_parse_path(const char *unit, |
582 | const char *filename, | |
583 | unsigned line, | |
584 | const char *section, | |
585 | const char *lvalue, | |
586 | int ltype, | |
587 | const char *rvalue, | |
588 | void *data, | |
589 | void *userdata) { | |
034c6ed7 LP |
590 | |
591 | char **s = data; | |
592 | char *n; | |
593 | ||
594 | assert(filename); | |
595 | assert(lvalue); | |
596 | assert(rvalue); | |
597 | assert(data); | |
598 | ||
7f110ff9 | 599 | if (!utf8_is_valid(rvalue)) { |
e8e581bf ZJS |
600 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
601 | "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); | |
7f110ff9 LP |
602 | return 0; |
603 | } | |
604 | ||
15ae422b | 605 | if (!path_is_absolute(rvalue)) { |
e8e581bf ZJS |
606 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
607 | "Not an absolute path, ignoring: %s", rvalue); | |
f975e971 | 608 | return 0; |
034c6ed7 LP |
609 | } |
610 | ||
7f110ff9 LP |
611 | n = strdup(rvalue); |
612 | if (!n) | |
74051b9b | 613 | return log_oom(); |
034c6ed7 | 614 | |
01f78473 LP |
615 | path_kill_slashes(n); |
616 | ||
034c6ed7 LP |
617 | free(*s); |
618 | *s = n; | |
619 | ||
620 | return 0; | |
621 | } | |
57d42a5f | 622 | |
e8e581bf ZJS |
623 | int config_parse_strv(const char *unit, |
624 | const char *filename, | |
625 | unsigned line, | |
626 | const char *section, | |
627 | const char *lvalue, | |
628 | int ltype, | |
629 | const char *rvalue, | |
630 | void *data, | |
631 | void *userdata) { | |
57d42a5f | 632 | |
853b8397 | 633 | char *** sv = data, *w, *state; |
57d42a5f | 634 | size_t l; |
7f110ff9 | 635 | int r; |
57d42a5f LP |
636 | |
637 | assert(filename); | |
638 | assert(lvalue); | |
639 | assert(rvalue); | |
640 | assert(data); | |
641 | ||
74051b9b | 642 | if (isempty(rvalue)) { |
4589f5bb LP |
643 | char **empty; |
644 | ||
645 | /* Empty assignment resets the list. As a special rule | |
646 | * we actually fill in a real empty array here rather | |
647 | * than NULL, since some code wants to know if | |
648 | * something was set at all... */ | |
649 | empty = strv_new(NULL, NULL); | |
650 | if (!empty) | |
651 | return log_oom(); | |
652 | ||
74051b9b | 653 | strv_free(*sv); |
4589f5bb | 654 | *sv = empty; |
853b8397 | 655 | return 0; |
74051b9b LP |
656 | } |
657 | ||
7f110ff9 | 658 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
853b8397 | 659 | _cleanup_free_ char *n; |
7f110ff9 | 660 | |
853b8397 LP |
661 | n = cunescape_length(w, l); |
662 | if (!n) | |
663 | return log_oom(); | |
664 | ||
665 | if (!utf8_is_valid(n)) { | |
e8e581bf ZJS |
666 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
667 | "String is not UTF-8 clean, ignoring: %s", rvalue); | |
7f110ff9 LP |
668 | continue; |
669 | } | |
670 | ||
853b8397 LP |
671 | r = strv_extend(sv, n); |
672 | if (r < 0) | |
673 | return log_oom(); | |
7f110ff9 | 674 | } |
57d42a5f | 675 | |
57d42a5f | 676 | return 0; |
57d42a5f | 677 | } |
15ae422b | 678 | |
e8e581bf ZJS |
679 | int config_parse_path_strv(const char *unit, |
680 | const char *filename, | |
681 | unsigned line, | |
682 | const char *section, | |
683 | const char *lvalue, | |
684 | int ltype, | |
685 | const char *rvalue, | |
686 | void *data, | |
687 | void *userdata) { | |
15ae422b | 688 | |
853b8397 | 689 | char*** sv = data, *w, *state; |
15ae422b | 690 | size_t l; |
15ae422b LP |
691 | int r; |
692 | ||
693 | assert(filename); | |
694 | assert(lvalue); | |
695 | assert(rvalue); | |
696 | assert(data); | |
697 | ||
74051b9b LP |
698 | if (isempty(rvalue)) { |
699 | /* Empty assignment resets the list */ | |
700 | strv_free(*sv); | |
701 | *sv = NULL; | |
853b8397 | 702 | return 0; |
74051b9b LP |
703 | } |
704 | ||
15ae422b | 705 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
853b8397 | 706 | _cleanup_free_ char *n; |
15ae422b | 707 | |
853b8397 LP |
708 | n = strndup(w, l); |
709 | if (!n) | |
710 | return log_oom(); | |
711 | ||
712 | if (!utf8_is_valid(n)) { | |
e8e581bf ZJS |
713 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
714 | "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); | |
7f110ff9 LP |
715 | continue; |
716 | } | |
717 | ||
853b8397 | 718 | if (!path_is_absolute(n)) { |
e8e581bf ZJS |
719 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
720 | "Not an absolute path, ignoring: %s", rvalue); | |
f975e971 | 721 | continue; |
15ae422b LP |
722 | } |
723 | ||
853b8397 LP |
724 | path_kill_slashes(n); |
725 | r = strv_extend(sv, n); | |
726 | if (r < 0) | |
727 | return log_oom(); | |
15ae422b LP |
728 | } |
729 | ||
f975e971 LP |
730 | return 0; |
731 | } | |
732 | ||
e8e581bf ZJS |
733 | int config_parse_mode(const char *unit, |
734 | const char *filename, | |
735 | unsigned line, | |
736 | const char *section, | |
737 | const char *lvalue, | |
738 | int ltype, | |
739 | const char *rvalue, | |
740 | void *data, | |
741 | void *userdata) { | |
f975e971 LP |
742 | |
743 | mode_t *m = data; | |
744 | long l; | |
745 | char *x = NULL; | |
746 | ||
747 | assert(filename); | |
748 | assert(lvalue); | |
749 | assert(rvalue); | |
750 | assert(data); | |
751 | ||
752 | errno = 0; | |
753 | l = strtol(rvalue, &x, 8); | |
f3910003 | 754 | if (!x || x == rvalue || *x || errno) { |
e8e581bf ZJS |
755 | log_syntax(unit, LOG_ERR, filename, line, errno, |
756 | "Failed to parse mode value, ignoring: %s", rvalue); | |
f975e971 LP |
757 | return 0; |
758 | } | |
759 | ||
760 | if (l < 0000 || l > 07777) { | |
e8e581bf ZJS |
761 | log_syntax(unit, LOG_ERR, filename, line, ERANGE, |
762 | "Mode value out of range, ignoring: %s", rvalue); | |
f975e971 LP |
763 | return 0; |
764 | } | |
765 | ||
766 | *m = (mode_t) l; | |
767 | return 0; | |
768 | } | |
213ba152 | 769 | |
e8e581bf ZJS |
770 | int config_parse_facility(const char *unit, |
771 | const char *filename, | |
772 | unsigned line, | |
773 | const char *section, | |
774 | const char *lvalue, | |
775 | int ltype, | |
776 | const char *rvalue, | |
777 | void *data, | |
778 | void *userdata) { | |
213ba152 LP |
779 | |
780 | ||
781 | int *o = data, x; | |
782 | ||
783 | assert(filename); | |
784 | assert(lvalue); | |
785 | assert(rvalue); | |
786 | assert(data); | |
787 | ||
788 | x = log_facility_unshifted_from_string(rvalue); | |
789 | if (x < 0) { | |
e8e581bf ZJS |
790 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
791 | "Failed to parse log facility, ignoring: %s", rvalue); | |
213ba152 LP |
792 | return 0; |
793 | } | |
794 | ||
795 | *o = (x << 3) | LOG_PRI(*o); | |
796 | ||
797 | return 0; | |
798 | } | |
799 | ||
e8e581bf ZJS |
800 | int config_parse_level(const char *unit, |
801 | const char *filename, | |
802 | unsigned line, | |
803 | const char *section, | |
804 | const char *lvalue, | |
805 | int ltype, | |
806 | const char *rvalue, | |
807 | void *data, | |
808 | void *userdata) { | |
213ba152 LP |
809 | |
810 | ||
811 | int *o = data, x; | |
812 | ||
813 | assert(filename); | |
814 | assert(lvalue); | |
815 | assert(rvalue); | |
816 | assert(data); | |
817 | ||
818 | x = log_level_from_string(rvalue); | |
819 | if (x < 0) { | |
e8e581bf ZJS |
820 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
821 | "Failed to parse log level, ignoring: %s", rvalue); | |
213ba152 LP |
822 | return 0; |
823 | } | |
824 | ||
825 | *o = (*o & LOG_FACMASK) | x; | |
826 | return 0; | |
827 | } | |
96342de6 | 828 | |
e8e581bf ZJS |
829 | int config_parse_set_status(const char *unit, |
830 | const char *filename, | |
831 | unsigned line, | |
832 | const char *section, | |
833 | const char *lvalue, | |
834 | int ltype, | |
835 | const char *rvalue, | |
836 | void *data, | |
837 | void *userdata) { | |
96342de6 LN |
838 | |
839 | char *w; | |
840 | size_t l; | |
841 | char *state; | |
842 | int r; | |
843 | ExitStatusSet *status_set = data; | |
844 | ||
845 | assert(filename); | |
846 | assert(lvalue); | |
847 | assert(rvalue); | |
848 | assert(data); | |
849 | ||
74051b9b LP |
850 | if (isempty(rvalue)) { |
851 | /* Empty assignment resets the list */ | |
852 | ||
853 | set_free(status_set->signal); | |
854 | set_free(status_set->code); | |
855 | ||
856 | status_set->signal = status_set->code = NULL; | |
857 | return 0; | |
858 | } | |
859 | ||
96342de6 LN |
860 | FOREACH_WORD(w, l, rvalue, state) { |
861 | int val; | |
d046b20b LP |
862 | char *temp; |
863 | ||
864 | temp = strndup(w, l); | |
96342de6 LN |
865 | if (!temp) |
866 | return log_oom(); | |
867 | ||
868 | r = safe_atoi(temp, &val); | |
869 | if (r < 0) { | |
870 | val = signal_from_string_try_harder(temp); | |
871 | free(temp); | |
d046b20b | 872 | |
96342de6 | 873 | if (val > 0) { |
d046b20b LP |
874 | r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func); |
875 | if (r < 0) | |
876 | return log_oom(); | |
877 | ||
96342de6 LN |
878 | r = set_put(status_set->signal, INT_TO_PTR(val)); |
879 | if (r < 0) { | |
e8e581bf ZJS |
880 | log_syntax(unit, LOG_ERR, filename, line, -r, |
881 | "Unable to store: %s", w); | |
96342de6 LN |
882 | return r; |
883 | } | |
884 | } else { | |
e8e581bf ZJS |
885 | log_syntax(unit, LOG_ERR, filename, line, -val, |
886 | "Failed to parse value, ignoring: %s", w); | |
07cacf5f | 887 | return 0; |
96342de6 LN |
888 | } |
889 | } else { | |
890 | free(temp); | |
d046b20b LP |
891 | |
892 | if (val < 0 || val > 255) | |
e8e581bf ZJS |
893 | log_syntax(unit, LOG_ERR, filename, line, ERANGE, |
894 | "Value %d is outside range 0-255, ignoring", val); | |
96342de6 | 895 | else { |
d046b20b LP |
896 | r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func); |
897 | if (r < 0) | |
898 | return log_oom(); | |
899 | ||
96342de6 LN |
900 | r = set_put(status_set->code, INT_TO_PTR(val)); |
901 | if (r < 0) { | |
e8e581bf ZJS |
902 | log_syntax(unit, LOG_ERR, filename, line, -r, |
903 | "Unable to store: %s", w); | |
96342de6 LN |
904 | return r; |
905 | } | |
906 | } | |
907 | } | |
96342de6 | 908 | } |
07cacf5f | 909 | |
96342de6 LN |
910 | return 0; |
911 | } |