]>
| Commit | Line | Data |
|---|---|---|
| db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| a7334b09 | 2 | |
| 47a71f98 | 3 | #include <linux/ipv6.h> |
| 07630cea | 4 | #include <stdio.h> |
| 07630cea | 5 | |
| 1cf40697 DDM |
6 | #include "sd-id128.h" |
| 7 | ||
| b5efdb8a | 8 | #include "alloc-util.h" |
| 0e10c3d8 | 9 | #include "calendarspec.h" |
| 1cf40697 | 10 | #include "chase.h" |
| e8461023 | 11 | #include "conf-files.h" |
| 6bedfcbb | 12 | #include "conf-parser.h" |
| 28db6fbf | 13 | #include "constants.h" |
| fa787a13 | 14 | #include "dns-domain.h" |
| 3f87eaa5 | 15 | #include "escape.h" |
| 0ec2a7a1 | 16 | #include "ether-addr-util.h" |
| a8fbdf54 | 17 | #include "extract-word.h" |
| 6bedfcbb | 18 | #include "fd-util.h" |
| e6dde451 | 19 | #include "fileio.h" |
| f4f15635 | 20 | #include "fs-util.h" |
| 9ac2f3c4 | 21 | #include "hash-funcs.h" |
| fa787a13 | 22 | #include "hostname-util.h" |
| aea3f594 | 23 | #include "id128-util.h" |
| 8cde9f6c | 24 | #include "in-addr-prefix-util.h" |
| f7a1e57e | 25 | #include "ip-protocol-list.h" |
| 16354eff | 26 | #include "log.h" |
| 47f761fd | 27 | #include "missing-network.h" |
| d8b4d14d | 28 | #include "nulstr-util.h" |
| c3eaba2d | 29 | #include "parse-helpers.h" |
| 6bedfcbb | 30 | #include "parse-util.h" |
| 9eb977db | 31 | #include "path-util.h" |
| ed5033fd | 32 | #include "percent-util.h" |
| 7b3e062c | 33 | #include "process-util.h" |
| ef118d00 | 34 | #include "rlimit-util.h" |
| 0ec2a7a1 | 35 | #include "set.h" |
| f757855e | 36 | #include "signal-util.h" |
| 69a283c5 | 37 | #include "siphash24.h" |
| d31645ad | 38 | #include "socket-util.h" |
| bdb2d3c6 | 39 | #include "stat-util.h" |
| 07630cea LP |
40 | #include "string-util.h" |
| 41 | #include "strv.h" | |
| 7b3e062c | 42 | #include "syslog-util.h" |
| a8fbdf54 | 43 | #include "time-util.h" |
| 07630cea | 44 | #include "utf8.h" |
| e8e581bf | 45 | |
| 9ac2f3c4 MY |
46 | DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(config_file_hash_ops_fclose, |
| 47 | char, path_hash_func, path_compare, | |
| 48 | FILE, safe_fclose); | |
| 49 | ||
| f975e971 | 50 | int config_item_table_lookup( |
| e9f3d2d5 | 51 | const void *table, |
| ed5bcfbe | 52 | const char *section, |
| ed5bcfbe | 53 | const char *lvalue, |
| 0b954099 LP |
54 | ConfigParserCallback *ret_func, |
| 55 | int *ret_ltype, | |
| 56 | void **ret_data, | |
| ed5bcfbe LP |
57 | void *userdata) { |
| 58 | ||
| f975e971 | 59 | assert(table); |
| ed5bcfbe | 60 | assert(lvalue); |
| 0b954099 LP |
61 | assert(ret_func); |
| 62 | assert(ret_ltype); | |
| 63 | assert(ret_data); | |
| ed5bcfbe | 64 | |
| 62c9beaa | 65 | for (const ConfigTableItem *t = table; t->lvalue; t++) { |
| ed5bcfbe | 66 | |
| f975e971 | 67 | if (!streq(lvalue, t->lvalue)) |
| ed5bcfbe LP |
68 | continue; |
| 69 | ||
| f975e971 | 70 | if (!streq_ptr(section, t->section)) |
| ed5bcfbe LP |
71 | continue; |
| 72 | ||
| 0b954099 LP |
73 | *ret_func = t->parse; |
| 74 | *ret_ltype = t->ltype; | |
| 75 | *ret_data = t->data; | |
| f975e971 LP |
76 | return 1; |
| 77 | } | |
| ed5bcfbe | 78 | |
| 0b954099 LP |
79 | *ret_func = NULL; |
| 80 | *ret_ltype = 0; | |
| 81 | *ret_data = NULL; | |
| f975e971 LP |
82 | return 0; |
| 83 | } | |
| 10e87ee7 | 84 | |
| f975e971 | 85 | int config_item_perf_lookup( |
| e9f3d2d5 | 86 | const void *table, |
| f975e971 LP |
87 | const char *section, |
| 88 | const char *lvalue, | |
| 0b954099 LP |
89 | ConfigParserCallback *ret_func, |
| 90 | int *ret_ltype, | |
| 91 | void **ret_data, | |
| f975e971 LP |
92 | void *userdata) { |
| 93 | ||
| 94 | ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; | |
| 95 | const ConfigPerfItem *p; | |
| 96 | ||
| 97 | assert(table); | |
| 98 | assert(lvalue); | |
| 0b954099 LP |
99 | assert(ret_func); |
| 100 | assert(ret_ltype); | |
| 101 | assert(ret_data); | |
| f975e971 | 102 | |
| 4472fa6d LP |
103 | if (section) { |
| 104 | const char *key; | |
| f975e971 | 105 | |
| 4472fa6d | 106 | key = strjoina(section, ".", lvalue); |
| f975e971 | 107 | p = lookup(key, strlen(key)); |
| 4472fa6d LP |
108 | } else |
| 109 | p = lookup(lvalue, strlen(lvalue)); | |
| 0b954099 LP |
110 | if (!p) { |
| 111 | *ret_func = NULL; | |
| 112 | *ret_ltype = 0; | |
| 113 | *ret_data = NULL; | |
| f975e971 | 114 | return 0; |
| 0b954099 | 115 | } |
| f975e971 | 116 | |
| 0b954099 LP |
117 | *ret_func = p->parse; |
| 118 | *ret_ltype = p->ltype; | |
| 119 | *ret_data = (uint8_t*) userdata + p->offset; | |
| f975e971 LP |
120 | return 1; |
| 121 | } | |
| 122 | ||
| 123 | /* Run the user supplied parser for an assignment */ | |
| bcde742e LP |
124 | static int next_assignment( |
| 125 | const char *unit, | |
| 126 | const char *filename, | |
| 127 | unsigned line, | |
| 128 | ConfigItemLookup lookup, | |
| 129 | const void *table, | |
| 130 | const char *section, | |
| 131 | unsigned section_line, | |
| 132 | const char *lvalue, | |
| 133 | const char *rvalue, | |
| 134 | ConfigParseFlags flags, | |
| 135 | void *userdata) { | |
| f975e971 LP |
136 | |
| 137 | ConfigParserCallback func = NULL; | |
| 138 | int ltype = 0; | |
| 139 | void *data = NULL; | |
| 140 | int r; | |
| 141 | ||
| 142 | assert(filename); | |
| 143 | assert(line > 0); | |
| 144 | assert(lookup); | |
| 145 | assert(lvalue); | |
| 146 | assert(rvalue); | |
| 147 | ||
| 148 | r = lookup(table, section, lvalue, &func, <ype, &data, userdata); | |
| 149 | if (r < 0) | |
| 150 | return r; | |
| d937fbbd | 151 | if (r > 0) { |
| 0b954099 LP |
152 | if (!func) |
| 153 | return 0; | |
| d937fbbd | 154 | |
| 0b954099 LP |
155 | return func(unit, filename, line, section, section_line, |
| 156 | lvalue, ltype, rvalue, data, userdata); | |
| d937fbbd | 157 | } |
| f975e971 | 158 | |
| 46205bb6 | 159 | /* Warn about unknown non-extension fields. */ |
| bcde742e | 160 | if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-")) |
| 28f30f40 | 161 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
| 600a7405 ZJS |
162 | "Unknown key '%s'%s%s%s, ignoring.", |
| 163 | lvalue, | |
| 164 | section ? " in section [" : "", | |
| 165 | strempty(section), | |
| 166 | section ? "]" : ""); | |
| 46205bb6 | 167 | |
| f1857be0 | 168 | return 0; |
| ed5bcfbe LP |
169 | } |
| 170 | ||
| 8a37ce65 | 171 | /* Parse a single logical line */ |
| bcde742e | 172 | static int parse_line( |
| 152f3493 | 173 | const char *unit, |
| bcde742e LP |
174 | const char *filename, |
| 175 | unsigned line, | |
| 176 | const char *sections, | |
| 177 | ConfigItemLookup lookup, | |
| 178 | const void *table, | |
| 179 | ConfigParseFlags flags, | |
| 180 | char **section, | |
| 181 | unsigned *section_line, | |
| 182 | bool *section_ignored, | |
| 73a4ac8a | 183 | char *l, /* is modified */ |
| bcde742e | 184 | void *userdata) { |
| f975e971 | 185 | |
| 7ade8982 | 186 | char *e; |
| ed5bcfbe | 187 | |
| f975e971 LP |
188 | assert(filename); |
| 189 | assert(line > 0); | |
| 190 | assert(lookup); | |
| 191 | assert(l); | |
| bd141bd8 MV |
192 | assert(section); |
| 193 | assert(section_line); | |
| 194 | assert(section_ignored); | |
| f975e971 | 195 | |
| b2aa81ef | 196 | l = strstrip(l); |
| 73a4ac8a | 197 | if (isempty(l)) |
| ed5bcfbe | 198 | return 0; |
| 1ea86b18 | 199 | |
| 73a4ac8a | 200 | if (l[0] == '\n') |
| 1ea86b18 | 201 | return 0; |
| ed5bcfbe | 202 | |
| 78d17fa0 YW |
203 | if (!utf8_is_valid(l)) |
| 204 | return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l); | |
| 205 | ||
| deec0b6d LP |
206 | if (l[0] == '[') { |
| 207 | _cleanup_free_ char *n = NULL; | |
| ed5bcfbe | 208 | size_t k; |
| ed5bcfbe | 209 | |
| b2aa81ef | 210 | k = strlen(l); |
| ed5bcfbe LP |
211 | assert(k > 0); |
| 212 | ||
| 3de39a1a YW |
213 | if (l[k-1] != ']') |
| 214 | return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EBADMSG), "Invalid section header '%s'", l); | |
| ed5bcfbe | 215 | |
| f975e971 LP |
216 | n = strndup(l+1, k-2); |
| 217 | if (!n) | |
| 7a602af0 | 218 | return log_oom(); |
| ed5bcfbe | 219 | |
| 812aa57d LP |
220 | if (!string_is_safe(n, /* flags= */ 0)) |
| 221 | return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EBADMSG), "Section header invalid '%s'", l); | |
| cec7f09d | 222 | |
| f975e971 | 223 | if (sections && !nulstr_contains(sections, n)) { |
| 73a4ac8a | 224 | bool ignore; |
| 42f4e3c4 | 225 | |
| 73a4ac8a | 226 | ignore = (flags & CONFIG_PARSE_RELAXED) || startswith(n, "X-"); |
| ddeb3f5d ZJS |
227 | |
| 228 | if (!ignore) | |
| 229 | NULSTR_FOREACH(t, sections) | |
| 73a4ac8a | 230 | if (streq_ptr(n, startswith(t, "-"))) { /* Ignore sections prefixed with "-" in valid section list */ |
| ddeb3f5d ZJS |
231 | ignore = true; |
| 232 | break; | |
| 233 | } | |
| 234 | ||
| 235 | if (!ignore) | |
| 12ca818f | 236 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); |
| f975e971 | 237 | |
| a1e58e8e | 238 | *section = mfree(*section); |
| 71a61510 | 239 | *section_line = 0; |
| 342aea19 | 240 | *section_ignored = true; |
| f975e971 | 241 | } else { |
| 97b9c506 | 242 | free_and_replace(*section, n); |
| 71a61510 | 243 | *section_line = line; |
| 342aea19 | 244 | *section_ignored = false; |
| f975e971 | 245 | } |
| ed5bcfbe LP |
246 | |
| 247 | return 0; | |
| 248 | } | |
| 249 | ||
| 62f168a0 | 250 | if (sections && !*section) { |
| bcde742e | 251 | if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored) |
| 12ca818f | 252 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); |
| 62f168a0 | 253 | |
| 10e87ee7 | 254 | return 0; |
| 62f168a0 | 255 | } |
| 10e87ee7 | 256 | |
| f975e971 | 257 | e = strchr(l, '='); |
| 2d4fffb0 ZJS |
258 | if (!e) |
| 259 | return log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 260 | "Missing '=', ignoring line."); | |
| 8be8ed8c ZJS |
261 | if (e == l) |
| 262 | return log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 263 | "Missing key name before '=', ignoring line."); | |
| ed5bcfbe LP |
264 | |
| 265 | *e = 0; | |
| 266 | e++; | |
| 267 | ||
| e8e581bf ZJS |
268 | return next_assignment(unit, |
| 269 | filename, | |
| 270 | line, | |
| 271 | lookup, | |
| 272 | table, | |
| 273 | *section, | |
| 71a61510 | 274 | *section_line, |
| e8e581bf ZJS |
275 | strstrip(l), |
| 276 | strstrip(e), | |
| bcde742e | 277 | flags, |
| e8e581bf | 278 | userdata); |
| ed5bcfbe LP |
279 | } |
| 280 | ||
| 281 | /* Go through the file and parse each line */ | |
| c2f781bc YW |
282 | int config_parse( |
| 283 | const char *unit, | |
| 284 | const char *filename, | |
| 285 | FILE *f, | |
| 286 | const char *sections, | |
| 287 | ConfigItemLookup lookup, | |
| 288 | const void *table, | |
| 289 | ConfigParseFlags flags, | |
| 290 | void *userdata, | |
| 8524db50 | 291 | struct stat *ret_stat) { |
| f975e971 | 292 | |
| 7fd1b19b HH |
293 | _cleanup_free_ char *section = NULL, *continuation = NULL; |
| 294 | _cleanup_fclose_ FILE *ours = NULL; | |
| 71a61510 | 295 | unsigned line = 0, section_line = 0; |
| f9761a89 | 296 | bool section_ignored = false, bom_seen = false; |
| 8524db50 | 297 | struct stat st; |
| 14f594b9 | 298 | int r, fd; |
| ed5bcfbe LP |
299 | |
| 300 | assert(filename); | |
| f975e971 | 301 | assert(lookup); |
| ed5bcfbe | 302 | |
| 87f0e418 | 303 | if (!f) { |
| 245802dd | 304 | f = ours = fopen(filename, "re"); |
| f975e971 | 305 | if (!f) { |
| 36f822c4 ZJS |
306 | /* Only log on request, except for ENOENT, |
| 307 | * since we return 0 to the caller. */ | |
| bcde742e | 308 | if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT) |
| b8b846d7 LP |
309 | log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, |
| 310 | "Failed to open configuration file '%s': %m", filename); | |
| 8524db50 YW |
311 | |
| 312 | if (errno == ENOENT) { | |
| 313 | if (ret_stat) | |
| 314 | *ret_stat = (struct stat) {}; | |
| 315 | ||
| 316 | return 0; | |
| 317 | } | |
| 318 | ||
| 319 | return -errno; | |
| 87f0e418 | 320 | } |
| ed5bcfbe LP |
321 | } |
| 322 | ||
| 14f594b9 | 323 | fd = fileno(f); |
| 4f9ff96a | 324 | if (fd >= 0) { /* stream might not have an fd, let's be careful hence */ |
| 4f9ff96a LP |
325 | |
| 326 | if (fstat(fd, &st) < 0) | |
| 327 | return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno, | |
| 328 | "Failed to fstat(%s): %m", filename); | |
| 329 | ||
| 330 | (void) stat_warn_permissions(filename, &st); | |
| 9fd8d678 | 331 | } else |
| 8524db50 | 332 | st = (struct stat) {}; |
| fdb9161c | 333 | |
| 9dd7ea9a | 334 | for (;;) { |
| e6dde451 | 335 | _cleanup_free_ char *buf = NULL; |
| 3dab2943 | 336 | bool escaped = false; |
| 92b5e605 | 337 | char *l, *p, *e; |
| ed5bcfbe | 338 | |
| e6dde451 LP |
339 | r = read_line(f, LONG_LINE_MAX, &buf); |
| 340 | if (r == 0) | |
| 341 | break; | |
| 342 | if (r == -ENOBUFS) { | |
| bcde742e | 343 | if (flags & CONFIG_PARSE_WARN) |
| e6dde451 LP |
344 | log_error_errno(r, "%s:%u: Line too long", filename, line); |
| 345 | ||
| 346 | return r; | |
| 347 | } | |
| 348 | if (r < 0) { | |
| 5aca2e67 | 349 | if (FLAGS_SET(flags, CONFIG_PARSE_WARN)) |
| e6dde451 | 350 | log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line); |
| ed5bcfbe | 351 | |
| e6dde451 | 352 | return r; |
| ed5bcfbe LP |
353 | } |
| 354 | ||
| 68c1ac15 YW |
355 | line++; |
| 356 | ||
| 0ef69585 YW |
357 | l = skip_leading_chars(buf, WHITESPACE); |
| 358 | if (*l != '\0' && strchr(COMMENTS, *l)) | |
| 9adbfeb3 YW |
359 | continue; |
| 360 | ||
| 9dd7ea9a | 361 | l = buf; |
| f9761a89 | 362 | if (!bom_seen) { |
| e6dde451 | 363 | char *q; |
| 9dd7ea9a | 364 | |
| e6dde451 LP |
365 | q = startswith(buf, UTF8_BYTE_ORDER_MARK); |
| 366 | if (q) { | |
| 367 | l = q; | |
| f9761a89 | 368 | bom_seen = true; |
| e6dde451 LP |
369 | } |
| 370 | } | |
| 3dab2943 LP |
371 | |
| 372 | if (continuation) { | |
| e6dde451 | 373 | if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) { |
| bcde742e | 374 | if (flags & CONFIG_PARSE_WARN) |
| e6dde451 LP |
375 | log_error("%s:%u: Continuation line too long", filename, line); |
| 376 | return -ENOBUFS; | |
| 377 | } | |
| 378 | ||
| c2bc710b | 379 | if (!strextend(&continuation, l)) { |
| bcde742e | 380 | if (flags & CONFIG_PARSE_WARN) |
| 36f822c4 | 381 | log_oom(); |
| 245802dd | 382 | return -ENOMEM; |
| 36f822c4 | 383 | } |
| 3dab2943 | 384 | |
| 92b5e605 | 385 | p = continuation; |
| 3dab2943 LP |
386 | } else |
| 387 | p = l; | |
| 388 | ||
| 389 | for (e = p; *e; e++) { | |
| 390 | if (escaped) | |
| 391 | escaped = false; | |
| 392 | else if (*e == '\\') | |
| 393 | escaped = true; | |
| 394 | } | |
| 395 | ||
| 396 | if (escaped) { | |
| 397 | *(e-1) = ' '; | |
| 398 | ||
| 92b5e605 | 399 | if (!continuation) { |
| f975e971 | 400 | continuation = strdup(l); |
| 36f822c4 | 401 | if (!continuation) { |
| bcde742e | 402 | if (flags & CONFIG_PARSE_WARN) |
| 36f822c4 | 403 | log_oom(); |
| 245802dd | 404 | return -ENOMEM; |
| 36f822c4 | 405 | } |
| 3dab2943 LP |
406 | } |
| 407 | ||
| 408 | continue; | |
| 409 | } | |
| 410 | ||
| e8e581bf ZJS |
411 | r = parse_line(unit, |
| 412 | filename, | |
| 68c1ac15 | 413 | line, |
| e8e581bf ZJS |
414 | sections, |
| 415 | lookup, | |
| 416 | table, | |
| bcde742e | 417 | flags, |
| e8e581bf | 418 | §ion, |
| 71a61510 | 419 | §ion_line, |
| 342aea19 | 420 | §ion_ignored, |
| e8e581bf ZJS |
421 | p, |
| 422 | userdata); | |
| 36f822c4 | 423 | if (r < 0) { |
| bcde742e | 424 | if (flags & CONFIG_PARSE_WARN) |
| e6dde451 | 425 | log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line); |
| 245802dd | 426 | return r; |
| 36f822c4 | 427 | } |
| 92b5e605 LP |
428 | |
| 429 | continuation = mfree(continuation); | |
| ed5bcfbe LP |
430 | } |
| 431 | ||
| 4f29e0db FB |
432 | if (continuation) { |
| 433 | r = parse_line(unit, | |
| 434 | filename, | |
| 435 | ++line, | |
| 436 | sections, | |
| 437 | lookup, | |
| 438 | table, | |
| 439 | flags, | |
| 440 | §ion, | |
| 441 | §ion_line, | |
| 442 | §ion_ignored, | |
| 443 | continuation, | |
| 444 | userdata); | |
| 445 | if (r < 0) { | |
| 446 | if (flags & CONFIG_PARSE_WARN) | |
| 447 | log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line); | |
| 448 | return r; | |
| 4f29e0db FB |
449 | } |
| 450 | } | |
| 451 | ||
| 8524db50 YW |
452 | if (ret_stat) |
| 453 | *ret_stat = st; | |
| 4f9ff96a | 454 | |
| 8b8024f1 | 455 | return 1; |
| ed5bcfbe LP |
456 | } |
| 457 | ||
| acfbd71c | 458 | int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) { |
| 8524db50 YW |
459 | _cleanup_free_ struct stat *st_copy = NULL; |
| 460 | _cleanup_free_ char *path_copy = NULL; | |
| 461 | int r; | |
| 462 | ||
| 463 | assert(stats_by_path); | |
| 464 | assert(path); | |
| 465 | assert(st); | |
| 466 | ||
| 8524db50 YW |
467 | st_copy = newdup(struct stat, st, 1); |
| 468 | if (!st_copy) | |
| 469 | return -ENOMEM; | |
| 470 | ||
| 471 | path_copy = strdup(path); | |
| 0fa25bd5 | 472 | if (!path_copy) |
| 8524db50 YW |
473 | return -ENOMEM; |
| 474 | ||
| a95ae2d3 | 475 | r = hashmap_ensure_put(stats_by_path, &path_hash_ops_free_free, path_copy, st_copy); |
| 8524db50 YW |
476 | if (r < 0) |
| 477 | return r; | |
| 478 | ||
| 479 | assert(r > 0); | |
| 480 | TAKE_PTR(path_copy); | |
| 481 | TAKE_PTR(st_copy); | |
| 482 | return 0; | |
| 483 | } | |
| 484 | ||
| 23bb31aa | 485 | static int config_parse_many_files( |
| d8a91c6b | 486 | const char *root, |
| 6ecfa12a | 487 | int root_fd, |
| 8b8024f1 | 488 | const char* const* conf_files, |
| 23bb31aa | 489 | char **files, |
| 43688c49 ZJS |
490 | const char *sections, |
| 491 | ConfigItemLookup lookup, | |
| 492 | const void *table, | |
| bcde742e | 493 | ConfigParseFlags flags, |
| 4f9ff96a | 494 | void *userdata, |
| 8524db50 | 495 | Hashmap **ret_stats_by_path) { |
| 43688c49 | 496 | |
| 8524db50 | 497 | _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; |
| 9ac2f3c4 | 498 | _cleanup_ordered_hashmap_free_ OrderedHashmap *dropins = NULL; |
| 93f1da45 | 499 | _cleanup_set_free_ Set *inodes = NULL; |
| 8524db50 | 500 | struct stat st; |
| be8e4b1a | 501 | int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG; |
| e8461023 | 502 | |
| 8524db50 YW |
503 | if (ret_stats_by_path) { |
| 504 | stats_by_path = hashmap_new(&path_hash_ops_free_free); | |
| 505 | if (!stats_by_path) | |
| be8e4b1a | 506 | return log_oom_full(level); |
| 8524db50 YW |
507 | } |
| 508 | ||
| 6ecfa12a | 509 | /* Pin and stat() all dropins */ |
| 93f1da45 | 510 | STRV_FOREACH(fn, files) { |
| 9ac2f3c4 | 511 | _cleanup_fclose_ FILE *f = NULL; |
| 79862d33 | 512 | r = chase_and_fopenat_unlocked(root_fd, root_fd, *fn, CHASE_MUST_BE_REGULAR, "re", /* ret_path= */ NULL, &f); |
| d8a91c6b ZJS |
513 | if (r == -ENOENT) |
| 514 | continue; | |
| 515 | if (r < 0) | |
| be8e4b1a | 516 | return log_full_errno(level, r, "Failed to open %s: %m", *fn); |
| 9ac2f3c4 | 517 | |
| d8a91c6b | 518 | int fd = fileno(f); |
| 6ecfa12a | 519 | assert(fd >= 0); |
| 9ac2f3c4 MY |
520 | |
| 521 | r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f); | |
| 522 | if (r < 0) { | |
| be8e4b1a YW |
523 | assert(r == -ENOMEM); |
| 524 | return log_oom_full(level); | |
| 9ac2f3c4 MY |
525 | } |
| 526 | assert(r > 0); | |
| 527 | TAKE_PTR(f); | |
| 528 | ||
| 529 | /* Get inodes for all drop-ins. Later we'll verify if main config is a symlink to or is | |
| 530 | * symlinked as one of them. If so, we skip reading main config file directly. */ | |
| 93f1da45 | 531 | |
| d8a91c6b | 532 | _cleanup_free_ struct stat *st_dropin = new(struct stat, 1); |
| 93f1da45 | 533 | if (!st_dropin) |
| be8e4b1a | 534 | return log_oom_full(level); |
| 93f1da45 | 535 | |
| 9ac2f3c4 | 536 | if (fstat(fd, st_dropin) < 0) |
| be8e4b1a | 537 | return log_full_errno(level, errno, "Failed to stat %s: %m", *fn); |
| 93f1da45 MY |
538 | |
| 539 | r = set_ensure_consume(&inodes, &inode_hash_ops, TAKE_PTR(st_dropin)); | |
| 540 | if (r < 0) | |
| be8e4b1a | 541 | return log_oom_full(level); |
| 93f1da45 MY |
542 | } |
| 543 | ||
| 6ecfa12a | 544 | /* First process the first found main config file. */ |
| 2034c8b8 | 545 | STRV_FOREACH(fn, conf_files) { |
| 9ac2f3c4 | 546 | _cleanup_fclose_ FILE *f = NULL; |
| 79862d33 | 547 | r = chase_and_fopenat_unlocked(root_fd, root_fd, *fn, CHASE_MUST_BE_REGULAR, "re", /* ret_path= */ NULL, &f); |
| d8a91c6b ZJS |
548 | if (r == -ENOENT) |
| 549 | continue; | |
| 550 | if (r < 0) | |
| be8e4b1a | 551 | return log_full_errno(level, r, "Failed to open %s: %m", *fn); |
| 9ac2f3c4 | 552 | |
| 93f1da45 | 553 | if (inodes) { |
| 9ac2f3c4 | 554 | if (fstat(fileno(f), &st) < 0) |
| be8e4b1a | 555 | return log_full_errno(level, errno, "Failed to stat %s: %m", *fn); |
| 93f1da45 MY |
556 | |
| 557 | if (set_contains(inodes, &st)) { | |
| 9ac2f3c4 | 558 | log_debug("%s: symlink to/symlinked as drop-in, will be read later.", *fn); |
| 0ca66adf | 559 | break; |
| 93f1da45 MY |
560 | } |
| 561 | } | |
| 562 | ||
| b7d62bdb | 563 | r = config_parse(/* unit= */ NULL, *fn, f, sections, lookup, table, flags, userdata, &st); |
| e8461023 | 564 | if (r < 0) |
| be8e4b1a | 565 | return r; /* config_parse() logs internally. */ |
| 8ea288db | 566 | assert(r > 0); |
| 8524db50 | 567 | |
| 8ea288db MY |
568 | if (ret_stats_by_path) { |
| 569 | r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st); | |
| 570 | if (r < 0) | |
| be8e4b1a | 571 | return log_full_errno(level, r, "Failed to save stats of %s: %m", *fn); |
| 8524db50 | 572 | } |
| 0ca66adf MY |
573 | |
| 574 | break; | |
| e8461023 JT |
575 | } |
| 576 | ||
| 8b8024f1 | 577 | /* Then read all the drop-ins. */ |
| 9ac2f3c4 MY |
578 | const char *path_dropin; |
| 579 | FILE *f_dropin; | |
| 580 | ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) { | |
| b7d62bdb | 581 | r = config_parse(/* unit= */ NULL, path_dropin, f_dropin, sections, lookup, table, flags, userdata, &st); |
| e8461023 | 582 | if (r < 0) |
| be8e4b1a | 583 | return r; /* config_parse() logs internally. */ |
| 8ea288db | 584 | assert(r > 0); |
| 8524db50 YW |
585 | |
| 586 | if (ret_stats_by_path) { | |
| 9ac2f3c4 | 587 | r = hashmap_put_stats_by_path(&stats_by_path, path_dropin, &st); |
| 8524db50 | 588 | if (r < 0) |
| be8e4b1a | 589 | return log_full_errno(level, r, "Failed to save stats of %s: %m", path_dropin); |
| 8524db50 | 590 | } |
| e8461023 JT |
591 | } |
| 592 | ||
| 8524db50 YW |
593 | if (ret_stats_by_path) |
| 594 | *ret_stats_by_path = TAKE_PTR(stats_by_path); | |
| 4f9ff96a | 595 | |
| e8461023 JT |
596 | return 0; |
| 597 | } | |
| 598 | ||
| 6ecfa12a LP |
599 | static int normalize_root_fd(const char *root, int *root_fd, int *ret_opened_fd) { |
| 600 | assert(root_fd); | |
| 601 | assert(ret_opened_fd); | |
| 602 | ||
| 603 | /* Normalizes a root dir specification: if root_fd is already valid, keep it. Otherwise, we open the | |
| 604 | * specified dir */ | |
| 605 | ||
| eb3799a3 | 606 | if (wildcard_fd_is_valid(*root_fd)) { |
| 6ecfa12a LP |
607 | *ret_opened_fd = -EBADF; |
| 608 | return 0; | |
| 609 | } | |
| 610 | ||
| 611 | if (empty_or_root(root)) { | |
| 612 | *root_fd = XAT_FDROOT; | |
| 613 | *ret_opened_fd = -EBADF; | |
| 614 | return 0; | |
| 615 | } | |
| 616 | ||
| 617 | int fd = open(root, O_CLOEXEC|O_PATH|O_DIRECTORY); | |
| 618 | if (fd < 0) | |
| 619 | return log_error_errno(errno, "Failed to open root directory '%s': %m", root); | |
| 620 | ||
| 621 | *ret_opened_fd = *root_fd = fd; | |
| 622 | return 0; | |
| 623 | } | |
| 624 | ||
| 23bb31aa | 625 | /* Parse each config file in the directories specified as strv. */ |
| 6ecfa12a | 626 | int config_parse_many_full( |
| 8b8024f1 | 627 | const char* const* conf_files, |
| 23bb31aa ZJS |
628 | const char* const* conf_file_dirs, |
| 629 | const char *dropin_dirname, | |
| 947f59ba | 630 | const char *root, |
| 6ecfa12a | 631 | int root_fd, |
| 23bb31aa ZJS |
632 | const char *sections, |
| 633 | ConfigItemLookup lookup, | |
| 634 | const void *table, | |
| bcde742e | 635 | ConfigParseFlags flags, |
| 9f83091e | 636 | void *userdata, |
| ead3a3fc RP |
637 | Hashmap **ret_stats_by_path, |
| 638 | char ***ret_dropin_files) { | |
| 23bb31aa | 639 | |
| 23bb31aa | 640 | _cleanup_strv_free_ char **files = NULL; |
| 23bb31aa ZJS |
641 | int r; |
| 642 | ||
| bdb2d3c6 YW |
643 | assert(conf_file_dirs); |
| 644 | assert(dropin_dirname); | |
| bdb2d3c6 YW |
645 | assert(table); |
| 646 | ||
| 6ecfa12a LP |
647 | _cleanup_close_ int opened_root_fd = -EBADF; |
| 648 | r = normalize_root_fd(root, &root_fd, &opened_root_fd); | |
| 649 | if (r < 0) | |
| 650 | return r; | |
| 651 | ||
| 652 | r = conf_files_list_dropins( | |
| 653 | &files, | |
| 654 | dropin_dirname, | |
| 655 | root, | |
| 656 | root_fd, | |
| 657 | FLAGS_SET(flags, CONFIG_PARSE_WARN) ? CONF_FILES_WARN : 0, | |
| 658 | conf_file_dirs); | |
| bdb2d3c6 | 659 | if (r < 0) |
| be8e4b1a | 660 | return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG, r, |
| f1f1ade0 | 661 | "Failed to list drop-ins in %s: %m", dropin_dirname); |
| bdb2d3c6 | 662 | |
| 6ecfa12a LP |
663 | r = config_parse_many_files( |
| 664 | root, | |
| 665 | root_fd, | |
| 666 | conf_files, | |
| 667 | files, | |
| 668 | sections, | |
| 669 | lookup, | |
| 670 | table, | |
| 671 | flags, | |
| 672 | userdata, | |
| 673 | ret_stats_by_path); | |
| ead3a3fc | 674 | if (r < 0) |
| be8e4b1a | 675 | return r; /* config_parse_many_files() logs internally. */ |
| ead3a3fc RP |
676 | |
| 677 | if (ret_dropin_files) | |
| 678 | *ret_dropin_files = TAKE_PTR(files); | |
| 679 | ||
| 680 | return 0; | |
| bdb2d3c6 YW |
681 | } |
| 682 | ||
| e7e52ff9 ZJS |
683 | int config_parse_standard_file_with_dropins_full( |
| 684 | const char *root, | |
| 6ecfa12a | 685 | int root_fd, |
| e7e52ff9 ZJS |
686 | const char *main_file, /* A path like "systemd/frobnicator.conf" */ |
| 687 | const char *sections, | |
| 688 | ConfigItemLookup lookup, | |
| 689 | const void *table, | |
| 690 | ConfigParseFlags flags, | |
| 691 | void *userdata, | |
| 692 | Hashmap **ret_stats_by_path, | |
| 693 | char ***ret_dropin_files) { | |
| 694 | ||
| 6ecfa12a LP |
695 | const char* const *conf_file_dirs = (const char* const*) CONF_PATHS_STRV(""); |
| 696 | _cleanup_strv_free_ char **conf_files = NULL; | |
| be8e4b1a | 697 | int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG; |
| e7e52ff9 ZJS |
698 | |
| 699 | /* Build the list of main config files */ | |
| 6ecfa12a LP |
700 | r = strv_extend_strv_concat( |
| 701 | &conf_files, | |
| 702 | conf_file_dirs, | |
| 703 | main_file); | |
| be8e4b1a YW |
704 | if (r < 0) |
| 705 | return log_oom_full(level); | |
| e7e52ff9 ZJS |
706 | |
| 707 | _cleanup_free_ char *dropin_dirname = strjoin(main_file, ".d"); | |
| be8e4b1a YW |
708 | if (!dropin_dirname) |
| 709 | return log_oom_full(level); | |
| e7e52ff9 | 710 | |
| 6ecfa12a LP |
711 | return config_parse_many_full( |
| 712 | (const char* const*) conf_files, | |
| 713 | conf_file_dirs, | |
| e7e52ff9 ZJS |
714 | dropin_dirname, |
| 715 | root, | |
| 6ecfa12a | 716 | root_fd, |
| e7e52ff9 ZJS |
717 | sections, |
| 718 | lookup, | |
| 719 | table, | |
| 720 | flags, | |
| 721 | userdata, | |
| 722 | ret_stats_by_path, | |
| 723 | ret_dropin_files); | |
| 724 | } | |
| 725 | ||
| 3f4dfd9d | 726 | static int dropins_get_stats_by_path( |
| bdb2d3c6 YW |
727 | const char* conf_file, |
| 728 | const char* const* conf_file_dirs, | |
| 3f4dfd9d | 729 | Hashmap **stats_by_path) { |
| bdb2d3c6 YW |
730 | |
| 731 | _cleanup_strv_free_ char **files = NULL; | |
| 732 | _cleanup_free_ char *dropin_dirname = NULL; | |
| bdb2d3c6 YW |
733 | int r; |
| 734 | ||
| 735 | assert(conf_file); | |
| 736 | assert(conf_file_dirs); | |
| 737 | assert(stats_by_path); | |
| 738 | ||
| bdb2d3c6 | 739 | r = path_extract_filename(conf_file, &dropin_dirname); |
| 23bb31aa ZJS |
740 | if (r < 0) |
| 741 | return r; | |
| bdb2d3c6 YW |
742 | if (r == O_DIRECTORY) |
| 743 | return -EINVAL; | |
| 744 | ||
| 745 | if (!strextend(&dropin_dirname, ".d")) | |
| 746 | return -ENOMEM; | |
| 23bb31aa | 747 | |
| 6ecfa12a LP |
748 | r = conf_files_list_dropins( |
| 749 | &files, | |
| 750 | dropin_dirname, | |
| 751 | /* root= */ NULL, | |
| 752 | /* root_fd= */ XAT_FDROOT, | |
| 753 | /* flags= */ 0, | |
| 754 | conf_file_dirs); | |
| 23bb31aa ZJS |
755 | if (r < 0) |
| 756 | return r; | |
| 757 | ||
| bdb2d3c6 | 758 | STRV_FOREACH(fn, files) { |
| 3f4dfd9d YW |
759 | struct stat st; |
| 760 | ||
| bdb2d3c6 YW |
761 | if (stat(*fn, &st) < 0) { |
| 762 | if (errno == ENOENT) | |
| 763 | continue; | |
| 764 | ||
| 765 | return -errno; | |
| 766 | } | |
| 767 | ||
| 3f4dfd9d | 768 | r = hashmap_put_stats_by_path(stats_by_path, *fn, &st); |
| bdb2d3c6 YW |
769 | if (r < 0) |
| 770 | return r; | |
| 771 | } | |
| 772 | ||
| 773 | return 0; | |
| 774 | } | |
| 775 | ||
| 776 | int config_get_stats_by_path( | |
| 777 | const char *suffix, | |
| 778 | const char *root, | |
| 2aa49631 | 779 | ConfFilesFlags flags, |
| bdb2d3c6 | 780 | const char* const* dirs, |
| 3f4dfd9d | 781 | bool check_dropins, |
| bdb2d3c6 YW |
782 | Hashmap **ret) { |
| 783 | ||
| 784 | _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; | |
| 785 | _cleanup_strv_free_ char **files = NULL; | |
| 786 | int r; | |
| 787 | ||
| 788 | assert(suffix); | |
| 789 | assert(dirs); | |
| 790 | assert(ret); | |
| 791 | ||
| 3f4dfd9d YW |
792 | /* Unlike config_parse(), this does not support stream. */ |
| 793 | ||
| bdb2d3c6 YW |
794 | r = conf_files_list_strv(&files, suffix, root, flags, dirs); |
| 795 | if (r < 0) | |
| 796 | return r; | |
| 797 | ||
| bdb2d3c6 | 798 | STRV_FOREACH(f, files) { |
| 3f4dfd9d YW |
799 | struct stat st; |
| 800 | ||
| 801 | /* First read the main config file. */ | |
| 802 | if (stat(*f, &st) < 0) { | |
| 803 | if (errno == ENOENT) | |
| 804 | continue; | |
| 805 | ||
| 806 | return -errno; | |
| 807 | } | |
| 808 | ||
| 2ae79a31 LZ |
809 | /* Skipping an empty file. */ |
| 810 | if (null_or_empty(&st)) | |
| 811 | continue; | |
| 812 | ||
| 3f4dfd9d YW |
813 | r = hashmap_put_stats_by_path(&stats_by_path, *f, &st); |
| 814 | if (r < 0) | |
| 815 | return r; | |
| 816 | ||
| 817 | if (!check_dropins) | |
| 818 | continue; | |
| 819 | ||
| 820 | /* Then read all the drop-ins if requested. */ | |
| 821 | r = dropins_get_stats_by_path(*f, dirs, &stats_by_path); | |
| bdb2d3c6 YW |
822 | if (r < 0) |
| 823 | return r; | |
| 824 | } | |
| 825 | ||
| 826 | *ret = TAKE_PTR(stats_by_path); | |
| 827 | return 0; | |
| 828 | } | |
| 829 | ||
| 830 | bool stats_by_path_equal(Hashmap *a, Hashmap *b) { | |
| 831 | struct stat *st_a, *st_b; | |
| 832 | const char *path; | |
| 833 | ||
| 834 | if (hashmap_size(a) != hashmap_size(b)) | |
| 835 | return false; | |
| 836 | ||
| 837 | HASHMAP_FOREACH_KEY(st_a, path, a) { | |
| 838 | st_b = hashmap_get(b, path); | |
| 839 | if (!st_b) | |
| 840 | return false; | |
| 841 | ||
| 842 | if (!stat_inode_unmodified(st_a, st_b)) | |
| 843 | return false; | |
| 844 | } | |
| 845 | ||
| 846 | return true; | |
| 23bb31aa ZJS |
847 | } |
| 848 | ||
| 152b8a4e YW |
849 | int config_section_parse( |
| 850 | const ConfigSectionParser *parsers, | |
| 851 | size_t n_parsers, | |
| 852 | const char *unit, | |
| 853 | const char *filename, | |
| 854 | unsigned line, | |
| 855 | const char *section, | |
| 856 | unsigned section_line, | |
| 857 | const char *lvalue, | |
| 858 | int ltype, | |
| 859 | const char *rvalue, | |
| 860 | void *userdata) { | |
| 861 | ||
| 862 | assert(parsers); | |
| 863 | assert(n_parsers > 0); | |
| 864 | assert(ltype >= 0); | |
| 865 | assert((size_t) ltype < n_parsers); | |
| 866 | assert(userdata); | |
| 867 | ||
| 868 | const ConfigSectionParser *e = parsers + ltype; | |
| 869 | assert(e->parser); | |
| 870 | ||
| 871 | /* This is used when a object is dynamically allocated per [SECTION] in a config parser, e.g. | |
| 872 | * [Address] for systemd.network. Takes the allocated object as 'userdata', then it is passed to | |
| 873 | * config parsers in the table. The 'data' field points to an element of the passed object, where | |
| 874 | * its offset is given by the table. */ | |
| 875 | ||
| 876 | return e->parser(unit, filename, line, section, section_line, lvalue, e->ltype, rvalue, | |
| 877 | (uint8_t*) userdata + e->offset, userdata); | |
| 878 | } | |
| 879 | ||
| b46d1694 | 880 | void config_section_hash_func(const ConfigSection *c, struct siphash *state) { |
| 307fe3cd | 881 | siphash24_compress_string(c->filename, state); |
| c01a5c05 | 882 | siphash24_compress_typesafe(c->line, state); |
| 307fe3cd YW |
883 | } |
| 884 | ||
| b46d1694 | 885 | int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) { |
| 307fe3cd YW |
886 | int r; |
| 887 | ||
| 888 | r = strcmp(x->filename, y->filename); | |
| 889 | if (r != 0) | |
| 890 | return r; | |
| 891 | ||
| 892 | return CMP(x->line, y->line); | |
| 893 | } | |
| 894 | ||
| 895 | DEFINE_HASH_OPS(config_section_hash_ops, ConfigSection, config_section_hash_func, config_section_compare_func); | |
| 896 | ||
| 08ca764d | 897 | int config_section_new(const char *filename, unsigned line, ConfigSection **ret) { |
| 307fe3cd YW |
898 | ConfigSection *cs; |
| 899 | ||
| 08ca764d YW |
900 | assert(filename); |
| 901 | assert(line > 0); | |
| 902 | assert(ret); | |
| 903 | ||
| 307fe3cd YW |
904 | cs = malloc0(offsetof(ConfigSection, filename) + strlen(filename) + 1); |
| 905 | if (!cs) | |
| 906 | return -ENOMEM; | |
| 907 | ||
| 908 | strcpy(cs->filename, filename); | |
| 909 | cs->line = line; | |
| 910 | ||
| 08ca764d | 911 | *ret = TAKE_PTR(cs); |
| 307fe3cd YW |
912 | return 0; |
| 913 | } | |
| 914 | ||
| dbef7bcf | 915 | static int _hashmap_by_section_find_unused_line( |
| e63c6e9f | 916 | HashmapBase *entries_by_section, |
| d9171a23 YW |
917 | const char *filename, |
| 918 | unsigned *ret) { | |
| 919 | ||
| 307fe3cd YW |
920 | ConfigSection *cs; |
| 921 | unsigned n = 0; | |
| 922 | void *entry; | |
| 923 | ||
| 029335ea MV |
924 | assert(ret); |
| 925 | ||
| e63c6e9f | 926 | HASHMAP_BASE_FOREACH_KEY(entry, cs, entries_by_section) { |
| d9171a23 YW |
927 | if (filename && !streq(cs->filename, filename)) |
| 928 | continue; | |
| 929 | n = MAX(n, cs->line); | |
| 930 | } | |
| 931 | ||
| 932 | /* overflow? */ | |
| 933 | if (n >= UINT_MAX) | |
| 934 | return -EFBIG; | |
| 307fe3cd | 935 | |
| d9171a23 YW |
936 | *ret = n + 1; |
| 937 | return 0; | |
| 307fe3cd YW |
938 | } |
| 939 | ||
| 69a283c5 DDM |
940 | int hashmap_by_section_find_unused_line( |
| 941 | Hashmap *entries_by_section, | |
| 942 | const char *filename, | |
| 943 | unsigned *ret) { | |
| 944 | ||
| 945 | return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section), filename, ret); | |
| 946 | } | |
| 947 | ||
| 948 | int ordered_hashmap_by_section_find_unused_line( | |
| 949 | OrderedHashmap *entries_by_section, | |
| 950 | const char *filename, | |
| 951 | unsigned *ret) { | |
| 952 | ||
| 953 | return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section), filename, ret); | |
| 954 | } | |
| 955 | ||
| eb3491d9 | 956 | #define DEFINE_PARSER(type, vartype, conv_func) \ |
| 42efe5be | 957 | DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype) |
| 94d75d64 LP |
958 | |
| 959 | DEFINE_PARSER(int, int, safe_atoi); | |
| 960 | DEFINE_PARSER(long, long, safe_atoli); | |
| 134e24e1 | 961 | DEFINE_PARSER(uint8, uint8_t, safe_atou8); |
| c7440e74 | 962 | DEFINE_PARSER(uint16, uint16_t, safe_atou16); |
| 94d75d64 | 963 | DEFINE_PARSER(uint32, uint32_t, safe_atou32); |
| b57ebc60 | 964 | DEFINE_PARSER(int32, int32_t, safe_atoi32); |
| 94d75d64 LP |
965 | DEFINE_PARSER(uint64, uint64_t, safe_atou64); |
| 966 | DEFINE_PARSER(unsigned, unsigned, safe_atou); | |
| 967 | DEFINE_PARSER(double, double, safe_atod); | |
| 968 | DEFINE_PARSER(nsec, nsec_t, parse_nsec); | |
| 969 | DEFINE_PARSER(sec, usec_t, parse_sec); | |
| 7b61ce3c | 970 | DEFINE_PARSER(sec_def_infinity, usec_t, parse_sec_def_infinity); |
| 94d75d64 | 971 | DEFINE_PARSER(mode, mode_t, parse_mode); |
| 65a0ede2 | 972 | DEFINE_PARSER(pid, pid_t, parse_pid); |
| ed5bcfbe | 973 | |
| c2f781bc | 974 | int config_parse_iec_size( |
| 152f3493 | 975 | const char *unit, |
| c2f781bc YW |
976 | const char *filename, |
| 977 | unsigned line, | |
| 978 | const char *section, | |
| 979 | unsigned section_line, | |
| 980 | const char *lvalue, | |
| 981 | int ltype, | |
| 982 | const char *rvalue, | |
| 983 | void *data, | |
| 984 | void *userdata) { | |
| ed5bcfbe | 985 | |
| 99534007 | 986 | size_t *sz = ASSERT_PTR(data); |
| 59f448cf | 987 | uint64_t v; |
| e8e581bf | 988 | int r; |
| ed5bcfbe LP |
989 | |
| 990 | assert(filename); | |
| 991 | assert(lvalue); | |
| 992 | assert(rvalue); | |
| ed5bcfbe | 993 | |
| 59f448cf | 994 | r = parse_size(rvalue, 1024, &v); |
| 1e5f4e8b YW |
995 | if (r >= 0 && (uint64_t) (size_t) v != v) |
| 996 | r = -ERANGE; | |
| 6f12cb91 YW |
997 | if (r < 0) |
| 998 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 9ba1a159 | 999 | |
| 59f448cf | 1000 | *sz = (size_t) v; |
| 6f12cb91 | 1001 | return 1; |
| 9ba1a159 LP |
1002 | } |
| 1003 | ||
| 78238fd7 | 1004 | int config_parse_iec_size_long( |
| 1005 | const char *unit, | |
| 1006 | const char *filename, | |
| 1007 | unsigned line, | |
| 1008 | const char *section, | |
| 1009 | unsigned section_line, | |
| 1010 | const char *lvalue, | |
| 1011 | int ltype, | |
| 1012 | const char *rvalue, | |
| 1013 | void *data, | |
| 1014 | void *userdata) { | |
| 1015 | ||
| 1016 | long *sz = ASSERT_PTR(data); | |
| 1017 | uint64_t v; | |
| 1018 | int r; | |
| 1019 | ||
| 1020 | assert(filename); | |
| 1021 | assert(lvalue); | |
| 1022 | assert(rvalue); | |
| 1023 | ||
| 1024 | r = parse_size(rvalue, 1024, &v); | |
| 1025 | if (r >= 0 && v > LONG_MAX) | |
| 1026 | r = -ERANGE; | |
| 1027 | if (r < 0) | |
| 1028 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 1029 | ||
| 1030 | *sz = (long) v; | |
| 1031 | return 1; | |
| 1032 | } | |
| 1033 | ||
| 50299121 | 1034 | int config_parse_si_uint64( |
| 152f3493 | 1035 | const char *unit, |
| 78f3c4bc LP |
1036 | const char *filename, |
| 1037 | unsigned line, | |
| 1038 | const char *section, | |
| 1039 | unsigned section_line, | |
| 1040 | const char *lvalue, | |
| 1041 | int ltype, | |
| 1042 | const char *rvalue, | |
| 1043 | void *data, | |
| 1044 | void *userdata) { | |
| 5556b5fe | 1045 | |
| 99534007 | 1046 | uint64_t *sz = ASSERT_PTR(data); |
| 5556b5fe LP |
1047 | int r; |
| 1048 | ||
| 1049 | assert(filename); | |
| 1050 | assert(lvalue); | |
| 1051 | assert(rvalue); | |
| 5556b5fe | 1052 | |
| 50299121 | 1053 | r = parse_size(rvalue, 1000, sz); |
| d96edb2c | 1054 | if (r < 0) |
| 6f12cb91 | 1055 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
| 5556b5fe | 1056 | |
| 6f12cb91 | 1057 | return 1; |
| 5556b5fe | 1058 | } |
| 9ba1a159 | 1059 | |
| 78f3c4bc | 1060 | int config_parse_iec_uint64( |
| 152f3493 | 1061 | const char *unit, |
| 78f3c4bc LP |
1062 | const char *filename, |
| 1063 | unsigned line, | |
| 1064 | const char *section, | |
| 1065 | unsigned section_line, | |
| 1066 | const char *lvalue, | |
| 1067 | int ltype, | |
| 1068 | const char *rvalue, | |
| 1069 | void *data, | |
| 1070 | void *userdata) { | |
| 9ba1a159 | 1071 | |
| 99534007 | 1072 | uint64_t *bytes = ASSERT_PTR(data); |
| e8e581bf | 1073 | int r; |
| 9ba1a159 LP |
1074 | |
| 1075 | assert(filename); | |
| 1076 | assert(lvalue); | |
| 1077 | assert(rvalue); | |
| 9ba1a159 | 1078 | |
| 5556b5fe | 1079 | r = parse_size(rvalue, 1024, bytes); |
| e8e581bf | 1080 | if (r < 0) |
| 6f12cb91 | 1081 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
| ed5bcfbe | 1082 | |
| 6f12cb91 | 1083 | return 1; |
| ed5bcfbe LP |
1084 | } |
| 1085 | ||
| 6e8791a0 | 1086 | int config_parse_iec_uint64_infinity( |
| 152f3493 | 1087 | const char *unit, |
| 6e8791a0 TB |
1088 | const char *filename, |
| 1089 | unsigned line, | |
| 1090 | const char *section, | |
| 1091 | unsigned section_line, | |
| 1092 | const char *lvalue, | |
| 1093 | int ltype, | |
| 1094 | const char *rvalue, | |
| 1095 | void *data, | |
| 1096 | void *userdata) { | |
| 1097 | ||
| 99534007 | 1098 | uint64_t *bytes = ASSERT_PTR(data); |
| 6e8791a0 TB |
1099 | |
| 1100 | assert(rvalue); | |
| 6e8791a0 TB |
1101 | |
| 1102 | if (streq(rvalue, "infinity")) { | |
| 1103 | *bytes = UINT64_MAX; | |
| 6f12cb91 | 1104 | return 1; |
| 6e8791a0 TB |
1105 | } |
| 1106 | ||
| 1107 | return config_parse_iec_uint64(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata); | |
| 1108 | } | |
| 1109 | ||
| c2f781bc | 1110 | int config_parse_bool( |
| 152f3493 | 1111 | const char *unit, |
| c2f781bc YW |
1112 | const char *filename, |
| 1113 | unsigned line, | |
| 1114 | const char *section, | |
| 1115 | unsigned section_line, | |
| 1116 | const char *lvalue, | |
| 1117 | int ltype, | |
| 1118 | const char *rvalue, | |
| 1119 | void *data, | |
| 1120 | void *userdata) { | |
| ed5bcfbe | 1121 | |
| 99534007 | 1122 | bool *b = ASSERT_PTR(data); |
| 2c75fb73 | 1123 | bool fatal = ltype; |
| 6f12cb91 | 1124 | int r; |
| ed5bcfbe LP |
1125 | |
| 1126 | assert(filename); | |
| 1127 | assert(lvalue); | |
| 1128 | assert(rvalue); | |
| ed5bcfbe | 1129 | |
| 6f12cb91 YW |
1130 | r = parse_boolean(rvalue); |
| 1131 | if (r < 0) { | |
| 1132 | log_syntax_parse_error_full(unit, filename, line, r, fatal, lvalue, rvalue); | |
| 2c75fb73 | 1133 | return fatal ? -ENOEXEC : 0; |
| ed5bcfbe LP |
1134 | } |
| 1135 | ||
| 6f12cb91 | 1136 | *b = r; |
| f4810fe2 | 1137 | return 1; /* set */ |
| ed5bcfbe LP |
1138 | } |
| 1139 | ||
| 6db311fd YW |
1140 | int config_parse_uint32_flag( |
| 1141 | const char *unit, | |
| 1142 | const char *filename, | |
| 1143 | unsigned line, | |
| 1144 | const char *section, | |
| 1145 | unsigned section_line, | |
| 1146 | const char *lvalue, | |
| 1147 | int ltype, | |
| 1148 | const char *rvalue, | |
| 1149 | void *data, | |
| 1150 | void *userdata) { | |
| 1151 | ||
| 1152 | uint32_t *flags = ASSERT_PTR(data); | |
| 1153 | int r; | |
| 1154 | ||
| 1155 | assert(ltype != 0); | |
| 1156 | ||
| 1157 | r = isempty(rvalue) ? 0 : parse_boolean(rvalue); | |
| 6f12cb91 YW |
1158 | if (r < 0) |
| 1159 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 6db311fd YW |
1160 | |
| 1161 | SET_FLAG(*flags, ltype, r); | |
| 1162 | return 1; | |
| 1163 | } | |
| 1164 | ||
| ff616da4 YW |
1165 | int config_parse_uint32_invert_flag( |
| 1166 | const char *unit, | |
| 1167 | const char *filename, | |
| 1168 | unsigned line, | |
| 1169 | const char *section, | |
| 1170 | unsigned section_line, | |
| 1171 | const char *lvalue, | |
| 1172 | int ltype, | |
| 1173 | const char *rvalue, | |
| 1174 | void *data, | |
| 1175 | void *userdata) { | |
| 1176 | ||
| 1177 | uint32_t *flags = ASSERT_PTR(data); | |
| 1178 | int r; | |
| 1179 | ||
| 1180 | assert(ltype != 0); | |
| 1181 | ||
| 1182 | r = isempty(rvalue) ? 0 : parse_boolean(rvalue); | |
| 1183 | if (r < 0) | |
| 1184 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 1185 | ||
| 1186 | SET_FLAG(*flags, ltype, !r); | |
| 1187 | return 1; | |
| 1188 | } | |
| 1189 | ||
| 12963533 TH |
1190 | int config_parse_id128( |
| 1191 | const char *unit, | |
| 1192 | const char *filename, | |
| 1193 | unsigned line, | |
| 1194 | const char *section, | |
| 1195 | unsigned section_line, | |
| 1196 | const char *lvalue, | |
| 1197 | int ltype, | |
| 1198 | const char *rvalue, | |
| 1199 | void *data, | |
| 1200 | void *userdata) { | |
| 1201 | ||
| aea3f594 | 1202 | sd_id128_t *result = data; |
| 12963533 TH |
1203 | int r; |
| 1204 | ||
| 1205 | assert(filename); | |
| 1206 | assert(lvalue); | |
| 1207 | assert(rvalue); | |
| 1208 | ||
| aea3f594 | 1209 | r = id128_from_string_nonzero(rvalue, result); |
| 6f12cb91 | 1210 | if (r == -ENXIO) { |
| aea3f594 | 1211 | log_syntax(unit, LOG_WARNING, filename, line, r, "128-bit ID/UUID is all 0, ignoring: %s", rvalue); |
| 6f12cb91 YW |
1212 | return 0; |
| 1213 | } | |
| 1214 | if (r < 0) | |
| 1215 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 12963533 | 1216 | |
| 6f12cb91 | 1217 | return 1; |
| 12963533 TH |
1218 | } |
| 1219 | ||
| f757855e | 1220 | int config_parse_tristate( |
| 152f3493 | 1221 | const char *unit, |
| f757855e LP |
1222 | const char *filename, |
| 1223 | unsigned line, | |
| 1224 | const char *section, | |
| 1225 | unsigned section_line, | |
| 1226 | const char *lvalue, | |
| 1227 | int ltype, | |
| 1228 | const char *rvalue, | |
| 1229 | void *data, | |
| 1230 | void *userdata) { | |
| 1231 | ||
| b71a721f | 1232 | int r, *t = ASSERT_PTR(data); |
| f757855e LP |
1233 | |
| 1234 | assert(filename); | |
| 1235 | assert(lvalue); | |
| 1236 | assert(rvalue); | |
| f757855e | 1237 | |
| 33f2de7b YW |
1238 | /* A tristate is pretty much a boolean, except that it can also take an empty string, |
| 1239 | * indicating "uninitialized", much like NULL is for a pointer type. */ | |
| 1240 | ||
| 1241 | if (isempty(rvalue)) { | |
| 1242 | *t = -1; | |
| 4207f6c0 | 1243 | return 1; |
| 33f2de7b | 1244 | } |
| f757855e | 1245 | |
| b71a721f | 1246 | r = parse_tristate(rvalue, t); |
| 6f12cb91 YW |
1247 | if (r < 0) |
| 1248 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| f757855e | 1249 | |
| 4207f6c0 | 1250 | return 1; |
| f757855e LP |
1251 | } |
| 1252 | ||
| 8f2665a4 LP |
1253 | int config_parse_string( |
| 1254 | const char *unit, | |
| 1255 | const char *filename, | |
| 1256 | unsigned line, | |
| 1257 | const char *section, | |
| 1258 | unsigned section_line, | |
| 1259 | const char *lvalue, | |
| 1260 | int ltype, | |
| 1261 | const char *rvalue, | |
| 1262 | void *data, | |
| 1263 | void *userdata) { | |
| 1264 | ||
| 3f87eaa5 | 1265 | char **s = ASSERT_PTR(data); |
| 9aa3c079 | 1266 | int r; |
| ed5bcfbe LP |
1267 | |
| 1268 | assert(filename); | |
| 1269 | assert(lvalue); | |
| 1270 | assert(rvalue); | |
| ed5bcfbe | 1271 | |
| 3f87eaa5 YW |
1272 | if (isempty(rvalue)) { |
| 1273 | *s = mfree(*s); | |
| 9aa3c079 | 1274 | return 1; |
| 3f87eaa5 | 1275 | } |
| 2d17d699 | 1276 | |
| 812aa57d | 1277 | if (FLAGS_SET(ltype, CONFIG_PARSE_STRING_SAFE) && !string_is_safe(rvalue, STRING_ALLOW_GLOBS)) { |
| 3f87eaa5 | 1278 | _cleanup_free_ char *escaped = NULL; |
| 2d17d699 | 1279 | |
| 3f87eaa5 YW |
1280 | escaped = cescape(rvalue); |
| 1281 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 1282 | "Specified string contains unsafe characters, ignoring: %s", strna(escaped)); | |
| 2d17d699 LP |
1283 | return 0; |
| 1284 | } | |
| 1285 | ||
| e289ce7f YW |
1286 | if (FLAGS_SET(ltype, CONFIG_PARSE_STRING_ASCII) && !ascii_is_valid(rvalue)) { |
| 1287 | _cleanup_free_ char *escaped = NULL; | |
| 1288 | ||
| 1289 | escaped = cescape(rvalue); | |
| 1290 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 1291 | "Specified string contains invalid ASCII characters, ignoring: %s", strna(escaped)); | |
| 1292 | return 0; | |
| 1293 | } | |
| 1294 | ||
| 6f12cb91 | 1295 | r = free_and_strdup_warn(s, rvalue); |
| 9aa3c079 YW |
1296 | if (r < 0) |
| 1297 | return r; | |
| 1298 | ||
| 1299 | return 1; | |
| 2d17d699 LP |
1300 | } |
| 1301 | ||
| fa787a13 YW |
1302 | int config_parse_dns_name( |
| 1303 | const char *unit, | |
| 1304 | const char *filename, | |
| 1305 | unsigned line, | |
| 1306 | const char *section, | |
| 1307 | unsigned section_line, | |
| 1308 | const char *lvalue, | |
| 1309 | int ltype, | |
| 1310 | const char *rvalue, | |
| 1311 | void *data, | |
| 1312 | void *userdata) { | |
| 1313 | ||
| 1314 | char **hostname = ASSERT_PTR(data); | |
| 1315 | int r; | |
| 1316 | ||
| 1317 | assert(filename); | |
| 1318 | assert(lvalue); | |
| 1319 | assert(rvalue); | |
| 1320 | ||
| 1321 | if (isempty(rvalue)) { | |
| 1322 | *hostname = mfree(*hostname); | |
| 6f12cb91 | 1323 | return 1; |
| fa787a13 YW |
1324 | } |
| 1325 | ||
| 1326 | r = dns_name_is_valid(rvalue); | |
| 1327 | if (r < 0) { | |
| 1328 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
| 1329 | "Failed to check validity of DNS domain name '%s', ignoring assignment: %m", rvalue); | |
| 1330 | return 0; | |
| 1331 | } | |
| 1332 | if (r == 0) { | |
| 1333 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 1334 | "Specified invalid DNS domain name, ignoring assignment: %s", rvalue); | |
| 1335 | return 0; | |
| 1336 | } | |
| 1337 | ||
| 6f12cb91 YW |
1338 | r = free_and_strdup_warn(hostname, rvalue); |
| 1339 | if (r < 0) | |
| 1340 | return r; | |
| 1341 | ||
| 1342 | return 1; | |
| fa787a13 YW |
1343 | } |
| 1344 | ||
| 1345 | int config_parse_hostname( | |
| 1346 | const char *unit, | |
| 1347 | const char *filename, | |
| 1348 | unsigned line, | |
| 1349 | const char *section, | |
| 1350 | unsigned section_line, | |
| 1351 | const char *lvalue, | |
| 1352 | int ltype, | |
| 1353 | const char *rvalue, | |
| 1354 | void *data, | |
| 1355 | void *userdata) { | |
| 1356 | ||
| 1357 | char **hostname = ASSERT_PTR(data); | |
| 1358 | ||
| 1359 | assert(filename); | |
| 1360 | assert(lvalue); | |
| 1361 | assert(rvalue); | |
| 1362 | ||
| 1363 | if (isempty(rvalue)) { | |
| 1364 | *hostname = mfree(*hostname); | |
| 6f12cb91 | 1365 | return 1; |
| fa787a13 YW |
1366 | } |
| 1367 | ||
| 1368 | if (!hostname_is_valid(rvalue, 0)) { | |
| 1369 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 1370 | "Specified invalid hostname, ignoring assignment: %s", rvalue); | |
| 1371 | return 0; | |
| 1372 | } | |
| 1373 | ||
| 1374 | return config_parse_dns_name(unit, filename, line, section, section_line, | |
| 1375 | lvalue, ltype, rvalue, data, userdata); | |
| 1376 | } | |
| 1377 | ||
| 8f2665a4 LP |
1378 | int config_parse_path( |
| 1379 | const char *unit, | |
| 1380 | const char *filename, | |
| 1381 | unsigned line, | |
| 1382 | const char *section, | |
| 1383 | unsigned section_line, | |
| 1384 | const char *lvalue, | |
| 1385 | int ltype, | |
| 1386 | const char *rvalue, | |
| 1387 | void *data, | |
| 1388 | void *userdata) { | |
| 034c6ed7 | 1389 | |
| c0d72c43 | 1390 | _cleanup_free_ char *n = NULL; |
| 2c75fb73 | 1391 | bool fatal = ltype; |
| 99534007 | 1392 | char **s = ASSERT_PTR(data); |
| cd4f53c5 | 1393 | int r; |
| 034c6ed7 LP |
1394 | |
| 1395 | assert(filename); | |
| 1396 | assert(lvalue); | |
| 1397 | assert(rvalue); | |
| 034c6ed7 | 1398 | |
| 6f12cb91 YW |
1399 | if (isempty(rvalue)) { |
| 1400 | *s = mfree(*s); | |
| 1401 | return 1; | |
| 1402 | } | |
| 0fe50629 | 1403 | |
| 7f110ff9 LP |
1404 | n = strdup(rvalue); |
| 1405 | if (!n) | |
| 74051b9b | 1406 | return log_oom(); |
| 034c6ed7 | 1407 | |
| cd4f53c5 YW |
1408 | r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue); |
| 1409 | if (r < 0) | |
| 1410 | return fatal ? -ENOEXEC : 0; | |
| 01f78473 | 1411 | |
| 6f12cb91 YW |
1412 | free_and_replace(*s, n); |
| 1413 | return 1; | |
| 034c6ed7 | 1414 | } |
| 57d42a5f | 1415 | |
| 8249bb72 LP |
1416 | int config_parse_strv( |
| 1417 | const char *unit, | |
| 1418 | const char *filename, | |
| 1419 | unsigned line, | |
| 1420 | const char *section, | |
| 1421 | unsigned section_line, | |
| 1422 | const char *lvalue, | |
| c33aacdc | 1423 | int ltype, /* When true, duplicated entries will be filtered. */ |
| 8249bb72 LP |
1424 | const char *rvalue, |
| 1425 | void *data, | |
| 1426 | void *userdata) { | |
| 57d42a5f | 1427 | |
| 99534007 | 1428 | char ***sv = ASSERT_PTR(data); |
| 00d0fd06 | 1429 | int r; |
| 57d42a5f LP |
1430 | |
| 1431 | assert(filename); | |
| 1432 | assert(lvalue); | |
| 1433 | assert(rvalue); | |
| 57d42a5f | 1434 | |
| 74051b9b | 1435 | if (isempty(rvalue)) { |
| 8249bb72 | 1436 | *sv = strv_free(*sv); |
| 6f12cb91 | 1437 | return 1; |
| 74051b9b LP |
1438 | } |
| 1439 | ||
| c33aacdc | 1440 | _cleanup_strv_free_ char **strv = NULL; |
| d96edb2c | 1441 | for (const char *p = rvalue;;) { |
| c33aacdc | 1442 | char *word; |
| 00d0fd06 | 1443 | |
| d96edb2c | 1444 | r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); |
| 6f12cb91 YW |
1445 | if (r < 0) |
| 1446 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 34f253f0 | 1447 | if (r == 0) |
| c33aacdc | 1448 | break; |
| 853b8397 | 1449 | |
| c33aacdc | 1450 | r = strv_consume(&strv, word); |
| 853b8397 LP |
1451 | if (r < 0) |
| 1452 | return log_oom(); | |
| 7f110ff9 | 1453 | } |
| c33aacdc | 1454 | |
| 93378148 | 1455 | r = strv_extend_strv_consume(sv, TAKE_PTR(strv), /* filter_duplicates= */ ltype); |
| c33aacdc YW |
1456 | if (r < 0) |
| 1457 | return log_oom(); | |
| 1458 | ||
| 6f12cb91 | 1459 | return 1; |
| 57d42a5f | 1460 | } |
| 15ae422b | 1461 | |
| 1e35c5ab RP |
1462 | int config_parse_warn_compat( |
| 1463 | const char *unit, | |
| 1464 | const char *filename, | |
| 1465 | unsigned line, | |
| 1466 | const char *section, | |
| 1467 | unsigned section_line, | |
| 1468 | const char *lvalue, | |
| 1469 | int ltype, | |
| 1470 | const char *rvalue, | |
| 1471 | void *data, | |
| 1472 | void *userdata) { | |
| 7ef7e15b | 1473 | |
| 1e35c5ab RP |
1474 | Disabled reason = ltype; |
| 1475 | ||
| 79893116 | 1476 | switch (reason) { |
| 7ef7e15b | 1477 | |
| 1e35c5ab RP |
1478 | case DISABLED_CONFIGURATION: |
| 1479 | log_syntax(unit, LOG_DEBUG, filename, line, 0, | |
| 1480 | "Support for option %s= has been disabled at compile time and it is ignored", lvalue); | |
| 1481 | break; | |
| 7ef7e15b | 1482 | |
| 1e35c5ab RP |
1483 | case DISABLED_LEGACY: |
| 1484 | log_syntax(unit, LOG_INFO, filename, line, 0, | |
| 1485 | "Support for option %s= has been removed and it is ignored", lvalue); | |
| 1486 | break; | |
| 7ef7e15b | 1487 | |
| 1e35c5ab RP |
1488 | case DISABLED_EXPERIMENTAL: |
| 1489 | log_syntax(unit, LOG_INFO, filename, line, 0, | |
| 1490 | "Support for option %s= has not yet been enabled and it is ignored", lvalue); | |
| 1491 | break; | |
| 7ef7e15b | 1492 | } |
| 1e35c5ab RP |
1493 | |
| 1494 | return 0; | |
| 1495 | } | |
| 1496 | ||
| ca37242e LP |
1497 | int config_parse_log_facility( |
| 1498 | const char *unit, | |
| 1499 | const char *filename, | |
| 1500 | unsigned line, | |
| 1501 | const char *section, | |
| 1502 | unsigned section_line, | |
| 1503 | const char *lvalue, | |
| 1504 | int ltype, | |
| 1505 | const char *rvalue, | |
| 1506 | void *data, | |
| 1507 | void *userdata) { | |
| 213ba152 | 1508 | |
| 213ba152 LP |
1509 | int *o = data, x; |
| 1510 | ||
| 1511 | assert(filename); | |
| 1512 | assert(lvalue); | |
| 1513 | assert(rvalue); | |
| 1514 | assert(data); | |
| 1515 | ||
| 1516 | x = log_facility_unshifted_from_string(rvalue); | |
| 6f12cb91 YW |
1517 | if (x < 0) |
| 1518 | return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); | |
| 213ba152 LP |
1519 | |
| 1520 | *o = (x << 3) | LOG_PRI(*o); | |
| 1521 | ||
| 6f12cb91 | 1522 | return 1; |
| 213ba152 LP |
1523 | } |
| 1524 | ||
| ca37242e LP |
1525 | int config_parse_log_level( |
| 1526 | const char *unit, | |
| 1527 | const char *filename, | |
| 1528 | unsigned line, | |
| 1529 | const char *section, | |
| 1530 | unsigned section_line, | |
| 1531 | const char *lvalue, | |
| 1532 | int ltype, | |
| 1533 | const char *rvalue, | |
| 1534 | void *data, | |
| 1535 | void *userdata) { | |
| 213ba152 | 1536 | |
| 213ba152 LP |
1537 | int *o = data, x; |
| 1538 | ||
| 1539 | assert(filename); | |
| 1540 | assert(lvalue); | |
| 1541 | assert(rvalue); | |
| 1542 | assert(data); | |
| 1543 | ||
| 1544 | x = log_level_from_string(rvalue); | |
| 6f12cb91 YW |
1545 | if (x < 0) |
| 1546 | return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); | |
| 213ba152 | 1547 | |
| d3070fbd LP |
1548 | if (*o < 0) /* if it wasn't initialized so far, assume zero facility */ |
| 1549 | *o = x; | |
| 1550 | else | |
| 1551 | *o = (*o & LOG_FACMASK) | x; | |
| 1552 | ||
| 6f12cb91 | 1553 | return 1; |
| 213ba152 | 1554 | } |
| f757855e LP |
1555 | |
| 1556 | int config_parse_signal( | |
| 1557 | const char *unit, | |
| 1558 | const char *filename, | |
| 1559 | unsigned line, | |
| 1560 | const char *section, | |
| 1561 | unsigned section_line, | |
| 1562 | const char *lvalue, | |
| 1563 | int ltype, | |
| 1564 | const char *rvalue, | |
| 1565 | void *data, | |
| 1566 | void *userdata) { | |
| 1567 | ||
| 1568 | int *sig = data, r; | |
| 1569 | ||
| 1570 | assert(filename); | |
| 1571 | assert(lvalue); | |
| 1572 | assert(rvalue); | |
| 1573 | assert(sig); | |
| 1574 | ||
| 29a3db75 | 1575 | r = signal_from_string(rvalue); |
| 6f12cb91 YW |
1576 | if (r <= 0) |
| 1577 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| f757855e LP |
1578 | |
| 1579 | *sig = r; | |
| 6f12cb91 | 1580 | return 1; |
| f757855e LP |
1581 | } |
| 1582 | ||
| 1583 | int config_parse_personality( | |
| 1584 | const char *unit, | |
| 1585 | const char *filename, | |
| 1586 | unsigned line, | |
| 1587 | const char *section, | |
| 1588 | unsigned section_line, | |
| 1589 | const char *lvalue, | |
| 1590 | int ltype, | |
| 1591 | const char *rvalue, | |
| 1592 | void *data, | |
| 1593 | void *userdata) { | |
| 1594 | ||
| 1595 | unsigned long *personality = data, p; | |
| 1596 | ||
| 1597 | assert(filename); | |
| 1598 | assert(lvalue); | |
| 1599 | assert(rvalue); | |
| 1600 | assert(personality); | |
| 1601 | ||
| 6f12cb91 YW |
1602 | if (isempty(rvalue)) { |
| 1603 | *personality = PERSONALITY_INVALID; | |
| 1604 | return 1; | |
| f757855e LP |
1605 | } |
| 1606 | ||
| 6f12cb91 YW |
1607 | p = personality_from_string(rvalue); |
| 1608 | if (p == PERSONALITY_INVALID) | |
| 1609 | return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue); | |
| 1610 | ||
| f757855e | 1611 | *personality = p; |
| 6f12cb91 | 1612 | return 1; |
| f757855e | 1613 | } |
| d31645ad LP |
1614 | |
| 1615 | int config_parse_ifname( | |
| 1616 | const char *unit, | |
| 1617 | const char *filename, | |
| 1618 | unsigned line, | |
| 1619 | const char *section, | |
| 1620 | unsigned section_line, | |
| 1621 | const char *lvalue, | |
| 1622 | int ltype, | |
| 1623 | const char *rvalue, | |
| 1624 | void *data, | |
| 1625 | void *userdata) { | |
| 1626 | ||
| 99534007 | 1627 | char **s = ASSERT_PTR(data); |
| d31645ad LP |
1628 | int r; |
| 1629 | ||
| 1630 | assert(filename); | |
| 1631 | assert(lvalue); | |
| 1632 | assert(rvalue); | |
| d31645ad LP |
1633 | |
| 1634 | if (isempty(rvalue)) { | |
| 1635 | *s = mfree(*s); | |
| f4810fe2 | 1636 | return 1; |
| d31645ad LP |
1637 | } |
| 1638 | ||
| 1639 | if (!ifname_valid(rvalue)) { | |
| d96edb2c | 1640 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); |
| d31645ad LP |
1641 | return 0; |
| 1642 | } | |
| 1643 | ||
| 6f12cb91 | 1644 | r = free_and_strdup_warn(s, rvalue); |
| d31645ad | 1645 | if (r < 0) |
| 6f12cb91 | 1646 | return r; |
| d31645ad | 1647 | |
| f4810fe2 | 1648 | return 1; |
| d31645ad | 1649 | } |
| 177d0b20 | 1650 | |
| a5053a15 YW |
1651 | int config_parse_ifnames( |
| 1652 | const char *unit, | |
| 1653 | const char *filename, | |
| 1654 | unsigned line, | |
| 1655 | const char *section, | |
| 1656 | unsigned section_line, | |
| 1657 | const char *lvalue, | |
| 1658 | int ltype, | |
| 1659 | const char *rvalue, | |
| 1660 | void *data, | |
| 1661 | void *userdata) { | |
| 1662 | ||
| 1663 | _cleanup_strv_free_ char **names = NULL; | |
| 99534007 | 1664 | char ***s = ASSERT_PTR(data); |
| a5053a15 YW |
1665 | int r; |
| 1666 | ||
| 1667 | assert(filename); | |
| 1668 | assert(lvalue); | |
| 1669 | assert(rvalue); | |
| a5053a15 YW |
1670 | |
| 1671 | if (isempty(rvalue)) { | |
| 1672 | *s = strv_free(*s); | |
| 6f12cb91 | 1673 | return 1; |
| a5053a15 YW |
1674 | } |
| 1675 | ||
| d96edb2c | 1676 | for (const char *p = rvalue;;) { |
| a5053a15 YW |
1677 | _cleanup_free_ char *word = NULL; |
| 1678 | ||
| 1679 | r = extract_first_word(&p, &word, NULL, 0); | |
| 6f12cb91 YW |
1680 | if (r < 0) |
| 1681 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| a5053a15 YW |
1682 | if (r == 0) |
| 1683 | break; | |
| 1684 | ||
| 1685 | if (!ifname_valid_full(word, ltype)) { | |
| d96edb2c | 1686 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
| a5053a15 YW |
1687 | "Interface name is not valid or too long, ignoring assignment: %s", |
| 1688 | word); | |
| 1689 | continue; | |
| 1690 | } | |
| 1691 | ||
| 1692 | r = strv_consume(&names, TAKE_PTR(word)); | |
| 1693 | if (r < 0) | |
| 1694 | return log_oom(); | |
| 1695 | } | |
| 1696 | ||
| 1697 | r = strv_extend_strv(s, names, true); | |
| 1698 | if (r < 0) | |
| 1699 | return log_oom(); | |
| 1700 | ||
| 6f12cb91 | 1701 | return 1; |
| a5053a15 YW |
1702 | } |
| 1703 | ||
| 177d0b20 SS |
1704 | int config_parse_ip_port( |
| 1705 | const char *unit, | |
| 1706 | const char *filename, | |
| 1707 | unsigned line, | |
| 1708 | const char *section, | |
| 1709 | unsigned section_line, | |
| 1710 | const char *lvalue, | |
| 1711 | int ltype, | |
| 1712 | const char *rvalue, | |
| 1713 | void *data, | |
| 1714 | void *userdata) { | |
| 1715 | ||
| 99534007 | 1716 | uint16_t *s = ASSERT_PTR(data); |
| 177d0b20 SS |
1717 | uint16_t port; |
| 1718 | int r; | |
| 1719 | ||
| 1720 | assert(filename); | |
| 1721 | assert(lvalue); | |
| 1722 | assert(rvalue); | |
| 177d0b20 SS |
1723 | |
| 1724 | if (isempty(rvalue)) { | |
| 1725 | *s = 0; | |
| 6f12cb91 | 1726 | return 1; |
| 177d0b20 SS |
1727 | } |
| 1728 | ||
| 1729 | r = parse_ip_port(rvalue, &port); | |
| 6f12cb91 YW |
1730 | if (r < 0) |
| 1731 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 177d0b20 SS |
1732 | |
| 1733 | *s = port; | |
| 6f12cb91 | 1734 | return 1; |
| 177d0b20 | 1735 | } |
| 9ecdba8c | 1736 | |
| 79138a38 LP |
1737 | int config_parse_mtu( |
| 1738 | const char *unit, | |
| 1739 | const char *filename, | |
| 1740 | unsigned line, | |
| 1741 | const char *section, | |
| 1742 | unsigned section_line, | |
| 1743 | const char *lvalue, | |
| 1744 | int ltype, | |
| 1745 | const char *rvalue, | |
| 1746 | void *data, | |
| 1747 | void *userdata) { | |
| 1748 | ||
| 99534007 | 1749 | uint32_t *mtu = ASSERT_PTR(data); |
| 79138a38 LP |
1750 | int r; |
| 1751 | ||
| 1752 | assert(rvalue); | |
| 79138a38 LP |
1753 | |
| 1754 | r = parse_mtu(ltype, rvalue, mtu); | |
| 1755 | if (r == -ERANGE) { | |
| d96edb2c | 1756 | log_syntax(unit, LOG_WARNING, filename, line, r, |
| 79138a38 LP |
1757 | "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s", |
| 1758 | (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX, | |
| 1759 | rvalue); | |
| 1760 | return 0; | |
| 1761 | } | |
| 6f12cb91 YW |
1762 | if (r < 0) |
| 1763 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 79138a38 | 1764 | |
| 18c4c5d8 | 1765 | return 1; |
| 79138a38 | 1766 | } |
| 4f424df7 LP |
1767 | |
| 1768 | int config_parse_rlimit( | |
| 1769 | const char *unit, | |
| 1770 | const char *filename, | |
| 1771 | unsigned line, | |
| 1772 | const char *section, | |
| 1773 | unsigned section_line, | |
| 1774 | const char *lvalue, | |
| 1775 | int ltype, | |
| 1776 | const char *rvalue, | |
| 1777 | void *data, | |
| 1778 | void *userdata) { | |
| 1779 | ||
| 1780 | struct rlimit **rl = data, d = {}; | |
| 1781 | int r; | |
| 1782 | ||
| 1783 | assert(rvalue); | |
| 1784 | assert(rl); | |
| 1785 | ||
| 1786 | r = rlimit_parse(ltype, rvalue, &d); | |
| 1787 | if (r == -EILSEQ) { | |
| 1788 | log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); | |
| 1789 | return 0; | |
| 1790 | } | |
| 6f12cb91 YW |
1791 | if (r < 0) |
| 1792 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 4f424df7 LP |
1793 | |
| 1794 | if (rl[ltype]) | |
| 1795 | *rl[ltype] = d; | |
| 1796 | else { | |
| 1797 | rl[ltype] = newdup(struct rlimit, &d, 1); | |
| 1798 | if (!rl[ltype]) | |
| 1799 | return log_oom(); | |
| 1800 | } | |
| 1801 | ||
| 6f12cb91 | 1802 | return 1; |
| 4f424df7 | 1803 | } |
| c07b23ca | 1804 | |
| c2f781bc | 1805 | int config_parse_permille( |
| 152f3493 | 1806 | const char *unit, |
| c2f781bc YW |
1807 | const char *filename, |
| 1808 | unsigned line, | |
| 1809 | const char *section, | |
| 1810 | unsigned section_line, | |
| 1811 | const char *lvalue, | |
| 1812 | int ltype, | |
| 1813 | const char *rvalue, | |
| 1814 | void *data, | |
| 1815 | void *userdata) { | |
| c07b23ca | 1816 | |
| 99534007 | 1817 | unsigned *permille = ASSERT_PTR(data); |
| c07b23ca MKB |
1818 | int r; |
| 1819 | ||
| 1820 | assert(filename); | |
| 1821 | assert(lvalue); | |
| 1822 | assert(rvalue); | |
| c07b23ca MKB |
1823 | |
| 1824 | r = parse_permille(rvalue); | |
| 6f12cb91 YW |
1825 | if (r < 0) |
| 1826 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| c07b23ca MKB |
1827 | |
| 1828 | *permille = (unsigned) r; | |
| 6f12cb91 | 1829 | return 1; |
| c07b23ca | 1830 | } |
| 4df4df5b | 1831 | |
| c2f781bc | 1832 | int config_parse_vlanprotocol( |
| 152f3493 | 1833 | const char *unit, |
| c2f781bc YW |
1834 | const char *filename, |
| 1835 | unsigned line, | |
| 1836 | const char *section, | |
| 1837 | unsigned section_line, | |
| 1838 | const char *lvalue, | |
| 1839 | int ltype, | |
| 1840 | const char *rvalue, | |
| 1841 | void *data, | |
| 1842 | void *userdata) { | |
| 1843 | ||
| 4df4df5b | 1844 | int *vlan_protocol = data; |
| c2f781bc | 1845 | |
| 4df4df5b RF |
1846 | assert(filename); |
| 1847 | assert(lvalue); | |
| 1848 | ||
| 1849 | if (isempty(rvalue)) { | |
| 1850 | *vlan_protocol = -1; | |
| 6f12cb91 | 1851 | return 1; |
| 4df4df5b RF |
1852 | } |
| 1853 | ||
| 1854 | if (STR_IN_SET(rvalue, "802.1ad", "802.1AD")) | |
| 1855 | *vlan_protocol = ETH_P_8021AD; | |
| 1856 | else if (STR_IN_SET(rvalue, "802.1q", "802.1Q")) | |
| 1857 | *vlan_protocol = ETH_P_8021Q; | |
| 6f12cb91 YW |
1858 | else |
| 1859 | return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue); | |
| 4df4df5b | 1860 | |
| 6f12cb91 | 1861 | return 1; |
| 4df4df5b | 1862 | } |
| 9de5e321 | 1863 | |
| 99628f36 YW |
1864 | int config_parse_hw_addr( |
| 1865 | const char *unit, | |
| 1866 | const char *filename, | |
| 1867 | unsigned line, | |
| 1868 | const char *section, | |
| 1869 | unsigned section_line, | |
| 1870 | const char *lvalue, | |
| 1871 | int ltype, | |
| 1872 | const char *rvalue, | |
| 1873 | void *data, | |
| 1874 | void *userdata) { | |
| 1875 | ||
| 1775654e | 1876 | struct hw_addr_data *hwaddr = ASSERT_PTR(data); |
| 99628f36 YW |
1877 | int r; |
| 1878 | ||
| 1879 | assert(filename); | |
| 1880 | assert(lvalue); | |
| 1881 | assert(rvalue); | |
| 99628f36 YW |
1882 | |
| 1883 | if (isempty(rvalue)) { | |
| 1884 | *hwaddr = HW_ADDR_NULL; | |
| 6f12cb91 | 1885 | return 1; |
| 99628f36 YW |
1886 | } |
| 1887 | ||
| 1775654e | 1888 | r = parse_hw_addr_full(rvalue, ltype, hwaddr); |
| 6f12cb91 YW |
1889 | if (r < 0) |
| 1890 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 99628f36 | 1891 | |
| 6f12cb91 | 1892 | return 1; |
| 99628f36 YW |
1893 | } |
| 1894 | ||
| 1895 | int config_parse_hw_addrs( | |
| 1896 | const char *unit, | |
| 1897 | const char *filename, | |
| 1898 | unsigned line, | |
| 1899 | const char *section, | |
| 1900 | unsigned section_line, | |
| 1901 | const char *lvalue, | |
| 1902 | int ltype, | |
| 1903 | const char *rvalue, | |
| 1904 | void *data, | |
| 1905 | void *userdata) { | |
| 1906 | ||
| 99534007 | 1907 | Set **hwaddrs = ASSERT_PTR(data); |
| 99628f36 YW |
1908 | int r; |
| 1909 | ||
| 1910 | assert(filename); | |
| 1911 | assert(lvalue); | |
| 1912 | assert(rvalue); | |
| 99628f36 YW |
1913 | |
| 1914 | if (isempty(rvalue)) { | |
| 1915 | /* Empty assignment resets the list */ | |
| 1916 | *hwaddrs = set_free(*hwaddrs); | |
| 6f12cb91 | 1917 | return 1; |
| 99628f36 YW |
1918 | } |
| 1919 | ||
| 1920 | for (const char *p = rvalue;;) { | |
| 1921 | _cleanup_free_ char *word = NULL; | |
| 1922 | _cleanup_free_ struct hw_addr_data *n = NULL; | |
| 1923 | ||
| 1924 | r = extract_first_word(&p, &word, NULL, 0); | |
| 6f12cb91 YW |
1925 | if (r < 0) |
| 1926 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 99628f36 | 1927 | if (r == 0) |
| 6f12cb91 | 1928 | return 1; |
| 99628f36 YW |
1929 | |
| 1930 | n = new(struct hw_addr_data, 1); | |
| 1931 | if (!n) | |
| 1932 | return log_oom(); | |
| 1933 | ||
| 1934 | r = parse_hw_addr_full(word, ltype, n); | |
| 1935 | if (r < 0) { | |
| 1936 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
| 1937 | "Not a valid hardware address, ignoring: %s", word); | |
| 1938 | continue; | |
| 1939 | } | |
| 1940 | ||
| 1941 | r = set_ensure_consume(hwaddrs, &hw_addr_hash_ops_free, TAKE_PTR(n)); | |
| 1942 | if (r < 0) | |
| 1943 | return log_oom(); | |
| 1944 | } | |
| 1945 | } | |
| 1946 | ||
| aa4f7653 | 1947 | int config_parse_ether_addr( |
| 0ec2a7a1 YW |
1948 | const char *unit, |
| 1949 | const char *filename, | |
| 1950 | unsigned line, | |
| 1951 | const char *section, | |
| 1952 | unsigned section_line, | |
| 1953 | const char *lvalue, | |
| 1954 | int ltype, | |
| 1955 | const char *rvalue, | |
| 1956 | void *data, | |
| 1957 | void *userdata) { | |
| 1958 | ||
| 1959 | _cleanup_free_ struct ether_addr *n = NULL; | |
| 99534007 | 1960 | struct ether_addr **hwaddr = ASSERT_PTR(data); |
| 0ec2a7a1 YW |
1961 | int r; |
| 1962 | ||
| 1963 | assert(filename); | |
| 1964 | assert(lvalue); | |
| 1965 | assert(rvalue); | |
| 0ec2a7a1 YW |
1966 | |
| 1967 | if (isempty(rvalue)) { | |
| 1968 | *hwaddr = mfree(*hwaddr); | |
| 6f12cb91 | 1969 | return 1; |
| 0ec2a7a1 YW |
1970 | } |
| 1971 | ||
| 1972 | n = new0(struct ether_addr, 1); | |
| 1973 | if (!n) | |
| 1974 | return log_oom(); | |
| 1975 | ||
| 227e9ce2 | 1976 | r = parse_ether_addr(rvalue, n); |
| 6f12cb91 YW |
1977 | if (r < 0) |
| 1978 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 0ec2a7a1 YW |
1979 | |
| 1980 | free_and_replace(*hwaddr, n); | |
| 6f12cb91 | 1981 | return 1; |
| 0ec2a7a1 YW |
1982 | } |
| 1983 | ||
| aa4f7653 | 1984 | int config_parse_ether_addrs( |
| 0ec2a7a1 YW |
1985 | const char *unit, |
| 1986 | const char *filename, | |
| 1987 | unsigned line, | |
| 1988 | const char *section, | |
| 1989 | unsigned section_line, | |
| 1990 | const char *lvalue, | |
| 1991 | int ltype, | |
| 1992 | const char *rvalue, | |
| 1993 | void *data, | |
| 1994 | void *userdata) { | |
| 1995 | ||
| 99534007 | 1996 | Set **hwaddrs = ASSERT_PTR(data); |
| 0ec2a7a1 YW |
1997 | int r; |
| 1998 | ||
| 1999 | assert(filename); | |
| 2000 | assert(lvalue); | |
| 2001 | assert(rvalue); | |
| 0ec2a7a1 YW |
2002 | |
| 2003 | if (isempty(rvalue)) { | |
| 2004 | /* Empty assignment resets the list */ | |
| c6df73ca | 2005 | *hwaddrs = set_free(*hwaddrs); |
| 6f12cb91 | 2006 | return 1; |
| 0ec2a7a1 YW |
2007 | } |
| 2008 | ||
| 2009 | for (const char *p = rvalue;;) { | |
| 2010 | _cleanup_free_ char *word = NULL; | |
| 2011 | _cleanup_free_ struct ether_addr *n = NULL; | |
| 2012 | ||
| 2013 | r = extract_first_word(&p, &word, NULL, 0); | |
| 6f12cb91 YW |
2014 | if (r < 0) |
| 2015 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 0ec2a7a1 | 2016 | if (r == 0) |
| 6f12cb91 | 2017 | return 1; |
| 0ec2a7a1 YW |
2018 | |
| 2019 | n = new(struct ether_addr, 1); | |
| 2020 | if (!n) | |
| 2021 | return log_oom(); | |
| 2022 | ||
| 227e9ce2 | 2023 | r = parse_ether_addr(word, n); |
| 0ec2a7a1 YW |
2024 | if (r < 0) { |
| 2025 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
| 2026 | "Not a valid MAC address, ignoring: %s", word); | |
| 2027 | continue; | |
| 2028 | } | |
| 2029 | ||
| c6df73ca | 2030 | r = set_ensure_consume(hwaddrs, ðer_addr_hash_ops_free, TAKE_PTR(n)); |
| 0ec2a7a1 YW |
2031 | if (r < 0) |
| 2032 | return log_oom(); | |
| 0ec2a7a1 YW |
2033 | } |
| 2034 | } | |
| 2035 | ||
| cf074772 YW |
2036 | int config_parse_in_addr_non_null( |
| 2037 | const char *unit, | |
| 2038 | const char *filename, | |
| 2039 | unsigned line, | |
| 2040 | const char *section, | |
| 2041 | unsigned section_line, | |
| 2042 | const char *lvalue, | |
| 2043 | int ltype, | |
| 2044 | const char *rvalue, | |
| 2045 | void *data, | |
| 2046 | void *userdata) { | |
| 2047 | ||
| 2048 | /* data must be a pointer to struct in_addr or in6_addr, and the type is determined by ltype. */ | |
| 99534007 DT |
2049 | struct in_addr *ipv4 = ASSERT_PTR(data); |
| 2050 | struct in6_addr *ipv6 = ASSERT_PTR(data); | |
| cf074772 YW |
2051 | union in_addr_union a; |
| 2052 | int r; | |
| 2053 | ||
| 2054 | assert(filename); | |
| 2055 | assert(lvalue); | |
| 2056 | assert(rvalue); | |
| cf074772 YW |
2057 | assert(IN_SET(ltype, AF_INET, AF_INET6)); |
| 2058 | ||
| 2059 | if (isempty(rvalue)) { | |
| 2060 | if (ltype == AF_INET) | |
| 2061 | *ipv4 = (struct in_addr) {}; | |
| 2062 | else | |
| 2063 | *ipv6 = (struct in6_addr) {}; | |
| 6f12cb91 | 2064 | return 1; |
| cf074772 YW |
2065 | } |
| 2066 | ||
| 2067 | r = in_addr_from_string(ltype, rvalue, &a); | |
| 6f12cb91 YW |
2068 | if (r < 0) |
| 2069 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| cf074772 YW |
2070 | |
| 2071 | if (!in_addr_is_set(ltype, &a)) { | |
| 2072 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
| 2073 | "%s= cannot be the ANY address, ignoring: %s", lvalue, rvalue); | |
| 2074 | return 0; | |
| 2075 | } | |
| 2076 | ||
| 2077 | if (ltype == AF_INET) | |
| 2078 | *ipv4 = a.in; | |
| 2079 | else | |
| 2080 | *ipv6 = a.in6; | |
| 6f12cb91 | 2081 | return 1; |
| cf074772 YW |
2082 | } |
| 2083 | ||
| 0ea6d55a YW |
2084 | int config_parse_in_addr_data( |
| 2085 | const char *unit, | |
| 2086 | const char *filename, | |
| 2087 | unsigned line, | |
| 2088 | const char *section, | |
| 2089 | unsigned section_line, | |
| 2090 | const char *lvalue, | |
| 2091 | int ltype, | |
| 2092 | const char *rvalue, | |
| 2093 | void *data, | |
| 2094 | void *userdata) { | |
| 2095 | ||
| 2096 | struct in_addr_data *p = ASSERT_PTR(data); | |
| 2097 | int r; | |
| 2098 | ||
| 2099 | assert(filename); | |
| 2100 | assert(lvalue); | |
| 2101 | ||
| 2102 | if (isempty(rvalue)) { | |
| 2103 | *p = (struct in_addr_data) {}; | |
| 2104 | return 1; | |
| 2105 | } | |
| 2106 | ||
| 2107 | r = in_addr_from_string_auto(rvalue, &p->family, &p->address); | |
| 2108 | if (r < 0) | |
| 2109 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 2110 | ||
| 2111 | return 1; | |
| 2112 | } | |
| 2113 | ||
| 8cde9f6c YW |
2114 | int config_parse_in_addr_prefix( |
| 2115 | const char *unit, | |
| 2116 | const char *filename, | |
| 2117 | unsigned line, | |
| 2118 | const char *section, | |
| 2119 | unsigned section_line, | |
| 2120 | const char *lvalue, | |
| 2121 | int ltype, /* takes boolean, whether we warn about missing prefixlen */ | |
| 2122 | const char *rvalue, | |
| 2123 | void *data, | |
| 2124 | void *userdata) { | |
| 2125 | ||
| 2126 | struct in_addr_prefix *p = ASSERT_PTR(data); | |
| 2127 | int r; | |
| 2128 | ||
| 2129 | assert(filename); | |
| 2130 | assert(lvalue); | |
| 2131 | ||
| 2132 | if (isempty(rvalue)) { | |
| 2133 | *p = (struct in_addr_prefix) {}; | |
| 2134 | return 1; | |
| 2135 | } | |
| 2136 | ||
| 2137 | r = in_addr_prefix_from_string_auto_full(rvalue, ltype ? PREFIXLEN_REFUSE : PREFIXLEN_FULL, &p->family, &p->address, &p->prefixlen); | |
| 2138 | if (r == -ENOANO) { | |
| 2139 | r = in_addr_prefix_from_string_auto(rvalue, &p->family, &p->address, &p->prefixlen); | |
| 2140 | if (r >= 0) | |
| 2141 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
| 2142 | "%s=%s is specified without prefix length. Assuming the prefix length is %u. " | |
| 2143 | "Please specify the prefix length explicitly.", lvalue, rvalue, p->prefixlen); | |
| 2144 | } | |
| 2145 | if (r < 0) | |
| 2146 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 2147 | ||
| 2148 | return 1; | |
| 2149 | } | |
| 2150 | ||
| 851cdffd ZJS |
2151 | int config_parse_unsigned_bounded( |
| 2152 | const char *unit, | |
| 2153 | const char *filename, | |
| 2154 | unsigned line, | |
| 2155 | const char *section, | |
| 2156 | unsigned section_line, | |
| 6f12cb91 YW |
2157 | const char *lvalue, |
| 2158 | const char *rvalue, | |
| 851cdffd ZJS |
2159 | unsigned min, |
| 2160 | unsigned max, | |
| 2161 | bool ignoring, | |
| 2162 | unsigned *ret) { | |
| 2163 | ||
| 2164 | int r; | |
| 2165 | ||
| 2166 | assert(filename); | |
| 6f12cb91 YW |
2167 | assert(lvalue); |
| 2168 | assert(rvalue); | |
| 851cdffd ZJS |
2169 | assert(ret); |
| 2170 | ||
| 6f12cb91 YW |
2171 | r = safe_atou_bounded(rvalue, min, max, ret); |
| 2172 | if (r == -ERANGE) { | |
| 851cdffd ZJS |
2173 | log_syntax(unit, LOG_WARNING, filename, line, r, |
| 2174 | "Invalid '%s=%s', allowed range is %u..%u%s.", | |
| 6f12cb91 YW |
2175 | lvalue, rvalue, min, max, ignoring ? ", ignoring" : ""); |
| 2176 | return ignoring ? 0 : r; | |
| 2177 | } | |
| 2178 | if (r < 0) | |
| 93378148 | 2179 | return log_syntax_parse_error_full(unit, filename, line, r, /* critical= */ !ignoring, lvalue, rvalue); |
| 851cdffd | 2180 | |
| 6f12cb91 | 2181 | return 1; /* Return 1 if something was set */ |
| 851cdffd ZJS |
2182 | } |
| 2183 | ||
| 0e10c3d8 LN |
2184 | int config_parse_calendar( |
| 2185 | const char *unit, | |
| 2186 | const char *filename, | |
| 2187 | unsigned line, | |
| 2188 | const char *section, | |
| 2189 | unsigned section_line, | |
| 2190 | const char *lvalue, | |
| 2191 | int ltype, | |
| 2192 | const char *rvalue, | |
| 2193 | void *data, | |
| 2194 | void *userdata) { | |
| 2195 | ||
| 2196 | CalendarSpec **cr = data; | |
| 2197 | _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; | |
| 2198 | int r; | |
| 2199 | ||
| 2200 | assert(filename); | |
| 2201 | assert(lvalue); | |
| 2202 | assert(rvalue); | |
| 2203 | assert(data); | |
| 2204 | ||
| 2205 | if (isempty(rvalue)) { | |
| 2206 | *cr = calendar_spec_free(*cr); | |
| 6f12cb91 | 2207 | return 1; |
| 0e10c3d8 LN |
2208 | } |
| 2209 | ||
| 2210 | r = calendar_spec_from_string(rvalue, &c); | |
| 6f12cb91 YW |
2211 | if (r < 0) |
| 2212 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 0e10c3d8 | 2213 | |
| 8b4ef777 | 2214 | free_and_replace_full(*cr, c, calendar_spec_free); |
| 6f12cb91 | 2215 | return 1; |
| 0e10c3d8 LN |
2216 | } |
| 2217 | ||
| 42efe5be YW |
2218 | DEFINE_CONFIG_PARSE(config_parse_percent, parse_percent); |
| 2219 | DEFINE_CONFIG_PARSE(config_parse_permyriad, parse_permyriad); | |
| 2220 | DEFINE_CONFIG_PARSE_PTR(config_parse_sec_fix_0, parse_sec_fix_0, usec_t); | |
| f72e851f YW |
2221 | |
| 2222 | int config_parse_timezone( | |
| 2223 | const char *unit, | |
| 2224 | const char *filename, | |
| 2225 | unsigned line, | |
| 2226 | const char *section, | |
| 2227 | unsigned section_line, | |
| 2228 | const char *lvalue, | |
| 2229 | int ltype, | |
| 2230 | const char *rvalue, | |
| 2231 | void *data, | |
| 2232 | void *userdata) { | |
| 2233 | ||
| 2234 | char **tz = ASSERT_PTR(data); | |
| 2235 | int r; | |
| 2236 | ||
| 2237 | assert(filename); | |
| 2238 | assert(lvalue); | |
| 2239 | assert(rvalue); | |
| 2240 | ||
| 2241 | if (isempty(rvalue)) { | |
| 2242 | *tz = mfree(*tz); | |
| 6f12cb91 | 2243 | return 1; |
| f72e851f YW |
2244 | } |
| 2245 | ||
| 2246 | r = verify_timezone(rvalue, LOG_WARNING); | |
| 6f12cb91 YW |
2247 | if (r < 0) |
| 2248 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 2249 | ||
| 2250 | r = free_and_strdup_warn(tz, rvalue); | |
| 2251 | if (r < 0) | |
| 2252 | return r; | |
| f72e851f | 2253 | |
| 6f12cb91 | 2254 | return 1; |
| f72e851f | 2255 | } |
| f7a1e57e YW |
2256 | |
| 2257 | int config_parse_ip_protocol( | |
| 2258 | const char *unit, | |
| 2259 | const char *filename, | |
| 2260 | unsigned line, | |
| 2261 | const char *section, | |
| 2262 | unsigned section_line, | |
| 2263 | const char *lvalue, | |
| 2264 | int ltype, | |
| 2265 | const char *rvalue, | |
| 2266 | void *data, | |
| 2267 | void *userdata) { | |
| 2268 | ||
| 2269 | uint8_t *proto = ASSERT_PTR(data); | |
| 2270 | int r; | |
| 2271 | ||
| 2272 | r = isempty(rvalue) ? 0 : parse_ip_protocol_full(rvalue, /* relaxed= */ ltype); | |
| 6f12cb91 YW |
2273 | if (r < 0) |
| 2274 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| f7a1e57e YW |
2275 | |
| 2276 | if (r > UINT8_MAX) { | |
| 2277 | /* linux/fib_rules.h and linux/fou.h define the netlink field as one byte, so we need to | |
| 2278 | * reject protocols numbers that don't fit in one byte. */ | |
| 2279 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
| 2280 | "Invalid '%s=%s', allowed range is 0..255, ignoring.", | |
| 2281 | lvalue, rvalue); | |
| 2282 | return 0; | |
| 2283 | } | |
| 2284 | ||
| 2285 | *proto = r; | |
| 2286 | return 1; /* done. */ | |
| 2287 | } | |
| 015a3b8c SEL |
2288 | |
| 2289 | int config_parse_loadavg( | |
| 2290 | const char *unit, | |
| 2291 | const char *filename, | |
| 2292 | unsigned line, | |
| 2293 | const char *section, | |
| 2294 | unsigned section_line, | |
| 2295 | const char *lvalue, | |
| 2296 | int ltype, | |
| 2297 | const char *rvalue, | |
| 2298 | void *data, | |
| 2299 | void *userdata) { | |
| 2300 | ||
| 2301 | loadavg_t *i = ASSERT_PTR(data); | |
| 2302 | int r; | |
| 2303 | ||
| 2304 | assert(rvalue); | |
| 2305 | ||
| 2306 | r = parse_permyriad(rvalue); | |
| 2307 | if (r < 0) | |
| 2308 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 2309 | ||
| 2310 | r = store_loadavg_fixed_point(r / 100, r % 100, i); | |
| 2311 | if (r < 0) | |
| 2312 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); | |
| 2313 | ||
| 2314 | return 1; /* done. */ | |
| 2315 | } |