]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
mkosi: Add explicit --bootable=no openSUSE dependencies
[thirdparty/systemd.git] / src / nspawn / nspawn-settings.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "alloc-util.h"
4 #include "cap-list.h"
5 #include "conf-parser.h"
6 #include "cpu-set-util.h"
7 #include "hostname-util.h"
8 #include "nspawn-network.h"
9 #include "nspawn-settings.h"
10 #include "parse-util.h"
11 #include "process-util.h"
12 #include "rlimit-util.h"
13 #include "socket-util.h"
14 #include "string-table.h"
15 #include "string-util.h"
16 #include "strv.h"
17 #include "user-util.h"
18 #include "util.h"
19
20 Settings *settings_new(void) {
21 Settings *s;
22
23 s = new(Settings, 1);
24 if (!s)
25 return NULL;
26
27 *s = (Settings) {
28 .start_mode = _START_MODE_INVALID,
29 .personality = PERSONALITY_INVALID,
30
31 .resolv_conf = _RESOLV_CONF_MODE_INVALID,
32 .link_journal = _LINK_JOURNAL_INVALID,
33 .timezone = _TIMEZONE_MODE_INVALID,
34
35 .userns_mode = _USER_NAMESPACE_MODE_INVALID,
36 .userns_chown = -1,
37 .uid_shift = UID_INVALID,
38 .uid_range = UID_INVALID,
39
40 .no_new_privileges = -1,
41
42 .read_only = -1,
43 .volatile_mode = _VOLATILE_MODE_INVALID,
44
45 .private_network = -1,
46 .network_veth = -1,
47
48 .full_capabilities = CAPABILITY_QUINTET_NULL,
49
50 .uid = UID_INVALID,
51 .gid = GID_INVALID,
52
53 .console_mode = _CONSOLE_MODE_INVALID,
54 .console_width = (unsigned) -1,
55 .console_height = (unsigned) -1,
56
57 .clone_ns_flags = (unsigned long) -1,
58 .use_cgns = -1,
59 };
60
61 return s;
62 }
63
64 int settings_load(FILE *f, const char *path, Settings **ret) {
65 _cleanup_(settings_freep) Settings *s = NULL;
66 int r;
67
68 assert(path);
69 assert(ret);
70
71 s = settings_new();
72 if (!s)
73 return -ENOMEM;
74
75 r = config_parse(NULL, path, f,
76 "Exec\0"
77 "Network\0"
78 "Files\0",
79 config_item_perf_lookup, nspawn_gperf_lookup,
80 CONFIG_PARSE_WARN,
81 s);
82 if (r < 0)
83 return r;
84
85 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
86 * both fields shall be initialized or neither. */
87 if (s->userns_mode == USER_NAMESPACE_PICK)
88 s->userns_chown = true;
89 else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
90 s->userns_chown = false;
91
92 if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
93 s->userns_mode = USER_NAMESPACE_NO;
94
95 *ret = TAKE_PTR(s);
96 return 0;
97 }
98
99 static void free_oci_hooks(OciHook *h, size_t n) {
100 size_t i;
101
102 assert(h || n == 0);
103
104 for (i = 0; i < n; i++) {
105 free(h[i].path);
106 strv_free(h[i].args);
107 strv_free(h[i].env);
108 }
109
110 free(h);
111 }
112
113 void device_node_array_free(DeviceNode *node, size_t n) {
114 size_t i;
115
116 for (i = 0; i < n; i++)
117 free(node[i].path);
118
119 free(node);
120 }
121
122 Settings* settings_free(Settings *s) {
123 if (!s)
124 return NULL;
125
126 strv_free(s->parameters);
127 strv_free(s->environment);
128 free(s->user);
129 free(s->pivot_root_new);
130 free(s->pivot_root_old);
131 free(s->working_directory);
132 strv_free(s->syscall_whitelist);
133 strv_free(s->syscall_blacklist);
134 rlimit_free_all(s->rlimit);
135 free(s->hostname);
136 cpu_set_reset(&s->cpu_set);
137
138 strv_free(s->network_interfaces);
139 strv_free(s->network_macvlan);
140 strv_free(s->network_ipvlan);
141 strv_free(s->network_veth_extra);
142 free(s->network_bridge);
143 free(s->network_zone);
144 expose_port_free_all(s->expose_ports);
145
146 custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
147
148 free(s->bundle);
149 free(s->root);
150
151 free_oci_hooks(s->oci_hooks_prestart, s->n_oci_hooks_prestart);
152 free_oci_hooks(s->oci_hooks_poststart, s->n_oci_hooks_poststart);
153 free_oci_hooks(s->oci_hooks_poststop, s->n_oci_hooks_poststop);
154
155 free(s->slice);
156 sd_bus_message_unref(s->properties);
157
158 free(s->supplementary_gids);
159 device_node_array_free(s->extra_nodes, s->n_extra_nodes);
160 free(s->network_namespace_path);
161
162 strv_free(s->sysctl);
163
164 #if HAVE_SECCOMP
165 seccomp_release(s->seccomp);
166 #endif
167
168 return mfree(s);
169 }
170
171 bool settings_private_network(Settings *s) {
172 assert(s);
173
174 return
175 s->private_network > 0 ||
176 s->network_veth > 0 ||
177 s->network_bridge ||
178 s->network_zone ||
179 s->network_interfaces ||
180 s->network_macvlan ||
181 s->network_ipvlan ||
182 s->network_veth_extra;
183 }
184
185 bool settings_network_veth(Settings *s) {
186 assert(s);
187
188 return
189 s->network_veth > 0 ||
190 s->network_bridge ||
191 s->network_zone;
192 }
193
194 int settings_allocate_properties(Settings *s) {
195 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
196 int r;
197
198 assert(s);
199
200 if (s->properties)
201 return 0;
202
203 r = sd_bus_default_system(&bus);
204 if (r < 0)
205 return r;
206
207 r = sd_bus_message_new(bus, &s->properties, SD_BUS_MESSAGE_METHOD_CALL);
208 if (r < 0)
209 return r;
210
211 return 0;
212 }
213
214 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
215
216 int config_parse_expose_port(
217 const char *unit,
218 const char *filename,
219 unsigned line,
220 const char *section,
221 unsigned section_line,
222 const char *lvalue,
223 int ltype,
224 const char *rvalue,
225 void *data,
226 void *userdata) {
227
228 Settings *s = data;
229 int r;
230
231 assert(filename);
232 assert(lvalue);
233 assert(rvalue);
234
235 r = expose_port_parse(&s->expose_ports, rvalue);
236 if (r == -EEXIST) {
237 log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
238 return 0;
239 }
240 if (r < 0) {
241 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
242 return 0;
243 }
244
245 return 0;
246 }
247
248 int config_parse_capability(
249 const char *unit,
250 const char *filename,
251 unsigned line,
252 const char *section,
253 unsigned section_line,
254 const char *lvalue,
255 int ltype,
256 const char *rvalue,
257 void *data,
258 void *userdata) {
259
260 uint64_t u = 0, *result = data;
261 int r;
262
263 assert(filename);
264 assert(lvalue);
265 assert(rvalue);
266
267 for (;;) {
268 _cleanup_free_ char *word = NULL;
269
270 r = extract_first_word(&rvalue, &word, NULL, 0);
271 if (r < 0) {
272 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
273 return 0;
274 }
275 if (r == 0)
276 break;
277
278 if (streq(word, "all"))
279 u = (uint64_t) -1;
280 else {
281 r = capability_from_name(word);
282 if (r < 0) {
283 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse capability, ignoring: %s", word);
284 continue;
285 }
286
287 u |= UINT64_C(1) << r;
288 }
289 }
290
291 if (u == 0)
292 return 0;
293
294 *result |= u;
295 return 0;
296 }
297
298 int config_parse_id128(
299 const char *unit,
300 const char *filename,
301 unsigned line,
302 const char *section,
303 unsigned section_line,
304 const char *lvalue,
305 int ltype,
306 const char *rvalue,
307 void *data,
308 void *userdata) {
309
310 sd_id128_t t, *result = data;
311 int r;
312
313 assert(filename);
314 assert(lvalue);
315 assert(rvalue);
316
317 r = sd_id128_from_string(rvalue, &t);
318 if (r < 0) {
319 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
320 return 0;
321 }
322
323 *result = t;
324 return 0;
325 }
326
327 int config_parse_pivot_root(
328 const char *unit,
329 const char *filename,
330 unsigned line,
331 const char *section,
332 unsigned section_line,
333 const char *lvalue,
334 int ltype,
335 const char *rvalue,
336 void *data,
337 void *userdata) {
338
339 Settings *settings = data;
340 int r;
341
342 assert(filename);
343 assert(lvalue);
344 assert(rvalue);
345
346 r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
347 if (r < 0) {
348 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
349 return 0;
350 }
351
352 return 0;
353 }
354
355 int config_parse_bind(
356 const char *unit,
357 const char *filename,
358 unsigned line,
359 const char *section,
360 unsigned section_line,
361 const char *lvalue,
362 int ltype,
363 const char *rvalue,
364 void *data,
365 void *userdata) {
366
367 Settings *settings = data;
368 int r;
369
370 assert(filename);
371 assert(lvalue);
372 assert(rvalue);
373
374 r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
375 if (r < 0) {
376 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
377 return 0;
378 }
379
380 return 0;
381 }
382
383 int config_parse_tmpfs(
384 const char *unit,
385 const char *filename,
386 unsigned line,
387 const char *section,
388 unsigned section_line,
389 const char *lvalue,
390 int ltype,
391 const char *rvalue,
392 void *data,
393 void *userdata) {
394
395 Settings *settings = data;
396 int r;
397
398 assert(filename);
399 assert(lvalue);
400 assert(rvalue);
401
402 r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
403 if (r < 0) {
404 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
405 return 0;
406 }
407
408 return 0;
409 }
410
411 int config_parse_inaccessible(
412 const char *unit,
413 const char *filename,
414 unsigned line,
415 const char *section,
416 unsigned section_line,
417 const char *lvalue,
418 int ltype,
419 const char *rvalue,
420 void *data,
421 void *userdata) {
422
423 Settings *settings = data;
424 int r;
425
426 assert(filename);
427 assert(lvalue);
428 assert(rvalue);
429
430 r = inaccessible_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
431 if (r < 0) {
432 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid inaccessible file system specification %s: %m", rvalue);
433 return 0;
434 }
435
436 return 0;
437 }
438
439 int config_parse_overlay(
440 const char *unit,
441 const char *filename,
442 unsigned line,
443 const char *section,
444 unsigned section_line,
445 const char *lvalue,
446 int ltype,
447 const char *rvalue,
448 void *data,
449 void *userdata) {
450
451 Settings *settings = data;
452 int r;
453
454 assert(filename);
455 assert(lvalue);
456 assert(rvalue);
457
458 r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
459 if (r < 0)
460 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
461
462 return 0;
463 }
464
465 int config_parse_veth_extra(
466 const char *unit,
467 const char *filename,
468 unsigned line,
469 const char *section,
470 unsigned section_line,
471 const char *lvalue,
472 int ltype,
473 const char *rvalue,
474 void *data,
475 void *userdata) {
476
477 Settings *settings = data;
478 int r;
479
480 assert(filename);
481 assert(lvalue);
482 assert(rvalue);
483
484 r = veth_extra_parse(&settings->network_veth_extra, rvalue);
485 if (r < 0) {
486 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
487 return 0;
488 }
489
490 return 0;
491 }
492
493 int config_parse_network_zone(
494 const char *unit,
495 const char *filename,
496 unsigned line,
497 const char *section,
498 unsigned section_line,
499 const char *lvalue,
500 int ltype,
501 const char *rvalue,
502 void *data,
503 void *userdata) {
504
505 Settings *settings = data;
506 _cleanup_free_ char *j = NULL;
507
508 assert(filename);
509 assert(lvalue);
510 assert(rvalue);
511
512 j = strjoin("vz-", rvalue);
513 if (!ifname_valid(j)) {
514 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name, ignoring: %s", rvalue);
515 return 0;
516 }
517
518 free_and_replace(settings->network_zone, j);
519
520 return 0;
521 }
522
523 int config_parse_boot(
524 const char *unit,
525 const char *filename,
526 unsigned line,
527 const char *section,
528 unsigned section_line,
529 const char *lvalue,
530 int ltype,
531 const char *rvalue,
532 void *data,
533 void *userdata) {
534
535 Settings *settings = data;
536 int r;
537
538 assert(filename);
539 assert(lvalue);
540 assert(rvalue);
541
542 r = parse_boolean(rvalue);
543 if (r < 0) {
544 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
545 return 0;
546 }
547
548 if (r > 0) {
549 if (settings->start_mode == START_PID2)
550 goto conflict;
551
552 settings->start_mode = START_BOOT;
553 } else {
554 if (settings->start_mode == START_BOOT)
555 goto conflict;
556
557 if (settings->start_mode < 0)
558 settings->start_mode = START_PID1;
559 }
560
561 return 0;
562
563 conflict:
564 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
565 return 0;
566 }
567
568 int config_parse_pid2(
569 const char *unit,
570 const char *filename,
571 unsigned line,
572 const char *section,
573 unsigned section_line,
574 const char *lvalue,
575 int ltype,
576 const char *rvalue,
577 void *data,
578 void *userdata) {
579
580 Settings *settings = data;
581 int r;
582
583 assert(filename);
584 assert(lvalue);
585 assert(rvalue);
586
587 r = parse_boolean(rvalue);
588 if (r < 0) {
589 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
590 return 0;
591 }
592
593 if (r > 0) {
594 if (settings->start_mode == START_BOOT)
595 goto conflict;
596
597 settings->start_mode = START_PID2;
598 } else {
599 if (settings->start_mode == START_PID2)
600 goto conflict;
601
602 if (settings->start_mode < 0)
603 settings->start_mode = START_PID1;
604 }
605
606 return 0;
607
608 conflict:
609 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
610 return 0;
611 }
612
613 int config_parse_private_users(
614 const char *unit,
615 const char *filename,
616 unsigned line,
617 const char *section,
618 unsigned section_line,
619 const char *lvalue,
620 int ltype,
621 const char *rvalue,
622 void *data,
623 void *userdata) {
624
625 Settings *settings = data;
626 int r;
627
628 assert(filename);
629 assert(lvalue);
630 assert(rvalue);
631
632 r = parse_boolean(rvalue);
633 if (r == 0) {
634 /* no: User namespacing off */
635 settings->userns_mode = USER_NAMESPACE_NO;
636 settings->uid_shift = UID_INVALID;
637 settings->uid_range = UINT32_C(0x10000);
638 } else if (r > 0) {
639 /* yes: User namespacing on, UID range is read from root dir */
640 settings->userns_mode = USER_NAMESPACE_FIXED;
641 settings->uid_shift = UID_INVALID;
642 settings->uid_range = UINT32_C(0x10000);
643 } else if (streq(rvalue, "pick")) {
644 /* pick: User namespacing on, UID range is picked randomly */
645 settings->userns_mode = USER_NAMESPACE_PICK;
646 settings->uid_shift = UID_INVALID;
647 settings->uid_range = UINT32_C(0x10000);
648 } else {
649 const char *range, *shift;
650 uid_t sh, rn;
651
652 /* anything else: User namespacing on, UID range is explicitly configured */
653
654 range = strchr(rvalue, ':');
655 if (range) {
656 shift = strndupa(rvalue, range - rvalue);
657 range++;
658
659 r = safe_atou32(range, &rn);
660 if (r < 0 || rn <= 0) {
661 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
662 return 0;
663 }
664 } else {
665 shift = rvalue;
666 rn = UINT32_C(0x10000);
667 }
668
669 r = parse_uid(shift, &sh);
670 if (r < 0) {
671 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
672 return 0;
673 }
674
675 settings->userns_mode = USER_NAMESPACE_FIXED;
676 settings->uid_shift = sh;
677 settings->uid_range = rn;
678 }
679
680 return 0;
681 }
682
683 int config_parse_syscall_filter(
684 const char *unit,
685 const char *filename,
686 unsigned line,
687 const char *section,
688 unsigned section_line,
689 const char *lvalue,
690 int ltype,
691 const char *rvalue,
692 void *data,
693 void *userdata) {
694
695 Settings *settings = data;
696 bool negative;
697 const char *items;
698 int r;
699
700 assert(filename);
701 assert(lvalue);
702 assert(rvalue);
703
704 negative = rvalue[0] == '~';
705 items = negative ? rvalue + 1 : rvalue;
706
707 for (;;) {
708 _cleanup_free_ char *word = NULL;
709
710 r = extract_first_word(&items, &word, NULL, 0);
711 if (r == 0)
712 break;
713 if (r == -ENOMEM)
714 return log_oom();
715 if (r < 0) {
716 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
717 return 0;
718 }
719
720 if (negative)
721 r = strv_extend(&settings->syscall_blacklist, word);
722 else
723 r = strv_extend(&settings->syscall_whitelist, word);
724 if (r < 0)
725 return log_oom();
726 }
727
728 return 0;
729 }
730
731 int config_parse_hostname(
732 const char *unit,
733 const char *filename,
734 unsigned line,
735 const char *section,
736 unsigned section_line,
737 const char *lvalue,
738 int ltype,
739 const char *rvalue,
740 void *data,
741 void *userdata) {
742
743 char **s = data;
744
745 assert(rvalue);
746 assert(s);
747
748 if (!hostname_is_valid(rvalue, false)) {
749 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue);
750 return 0;
751 }
752
753 if (free_and_strdup(s, empty_to_null(rvalue)) < 0)
754 return log_oom();
755
756 return 0;
757 }
758
759 int config_parse_oom_score_adjust(
760 const char *unit,
761 const char *filename,
762 unsigned line,
763 const char *section,
764 unsigned section_line,
765 const char *lvalue,
766 int ltype,
767 const char *rvalue,
768 void *data,
769 void *userdata) {
770
771 Settings *settings = data;
772 int oa, r;
773
774 assert(rvalue);
775 assert(settings);
776
777 if (isempty(rvalue)) {
778 settings->oom_score_adjust_set = false;
779 return 0;
780 }
781
782 r = parse_oom_score_adjust(rvalue, &oa);
783 if (r == -ERANGE) {
784 log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
785 return 0;
786 }
787 if (r < 0) {
788 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
789 return 0;
790 }
791
792 settings->oom_score_adjust = oa;
793 settings->oom_score_adjust_set = true;
794
795 return 0;
796 }
797
798 int config_parse_cpu_affinity(
799 const char *unit,
800 const char *filename,
801 unsigned line,
802 const char *section,
803 unsigned section_line,
804 const char *lvalue,
805 int ltype,
806 const char *rvalue,
807 void *data,
808 void *userdata) {
809
810 Settings *settings = data;
811
812 assert(rvalue);
813 assert(settings);
814
815 return parse_cpu_set_extend(rvalue, &settings->cpu_set, true, unit, filename, line, lvalue);
816 }
817
818 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
819
820 static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = {
821 [RESOLV_CONF_OFF] = "off",
822 [RESOLV_CONF_COPY_HOST] = "copy-host",
823 [RESOLV_CONF_COPY_STATIC] = "copy-static",
824 [RESOLV_CONF_COPY_UPLINK] = "copy-uplink",
825 [RESOLV_CONF_COPY_STUB] = "copy-stub",
826 [RESOLV_CONF_REPLACE_HOST] = "replace-host",
827 [RESOLV_CONF_REPLACE_STATIC] = "replace-static",
828 [RESOLV_CONF_REPLACE_UPLINK] = "replace-uplink",
829 [RESOLV_CONF_REPLACE_STUB] = "replace-stub",
830 [RESOLV_CONF_BIND_HOST] = "bind-host",
831 [RESOLV_CONF_BIND_STATIC] = "bind-static",
832 [RESOLV_CONF_BIND_UPLINK] = "bind-uplink",
833 [RESOLV_CONF_BIND_STUB] = "bind-stub",
834 [RESOLV_CONF_DELETE] = "delete",
835 [RESOLV_CONF_AUTO] = "auto",
836 };
837
838 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode, ResolvConfMode, RESOLV_CONF_AUTO);
839
840 int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try) {
841 assert(s);
842 assert(ret_mode);
843 assert(ret_try);
844
845 if (streq(s, "auto")) {
846 *ret_mode = LINK_AUTO;
847 *ret_try = false;
848 } else if (streq(s, "no")) {
849 *ret_mode = LINK_NO;
850 *ret_try = false;
851 } else if (streq(s, "guest")) {
852 *ret_mode = LINK_GUEST;
853 *ret_try = false;
854 } else if (streq(s, "host")) {
855 *ret_mode = LINK_HOST;
856 *ret_try = false;
857 } else if (streq(s, "try-guest")) {
858 *ret_mode = LINK_GUEST;
859 *ret_try = true;
860 } else if (streq(s, "try-host")) {
861 *ret_mode = LINK_HOST;
862 *ret_try = true;
863 } else
864 return -EINVAL;
865
866 return 0;
867 }
868
869 int config_parse_link_journal(
870 const char *unit,
871 const char *filename,
872 unsigned line,
873 const char *section,
874 unsigned section_line,
875 const char *lvalue,
876 int ltype,
877 const char *rvalue,
878 void *data,
879 void *userdata) {
880
881 Settings *settings = data;
882 int r;
883
884 assert(rvalue);
885 assert(settings);
886
887 r = parse_link_journal(rvalue, &settings->link_journal, &settings->link_journal_try);
888 if (r < 0) {
889 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse link journal mode, ignoring: %s", rvalue);
890 return 0;
891 }
892
893 return 0;
894 }
895
896 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone, timezone_mode, TimezoneMode, "Failed to parse timezone mode");
897
898 static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = {
899 [TIMEZONE_OFF] = "off",
900 [TIMEZONE_COPY] = "copy",
901 [TIMEZONE_BIND] = "bind",
902 [TIMEZONE_SYMLINK] = "symlink",
903 [TIMEZONE_DELETE] = "delete",
904 [TIMEZONE_AUTO] = "auto",
905 };
906
907 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);