1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2015 Lennart Poettering
6 #include "alloc-util.h"
8 #include "conf-parser.h"
9 #include "cpu-set-util.h"
10 #include "hostname-util.h"
11 #include "nspawn-network.h"
12 #include "nspawn-settings.h"
13 #include "parse-util.h"
14 #include "process-util.h"
15 #include "rlimit-util.h"
16 #include "socket-util.h"
17 #include "string-table.h"
18 #include "string-util.h"
20 #include "user-util.h"
23 int settings_load(FILE *f
, const char *path
, Settings
**ret
) {
24 _cleanup_(settings_freep
) Settings
*s
= NULL
;
30 s
= new0(Settings
, 1);
34 s
->start_mode
= _START_MODE_INVALID
;
35 s
->personality
= PERSONALITY_INVALID
;
36 s
->userns_mode
= _USER_NAMESPACE_MODE_INVALID
;
37 s
->resolv_conf
= _RESOLV_CONF_MODE_INVALID
;
38 s
->link_journal
= _LINK_JOURNAL_INVALID
;
39 s
->timezone
= _TIMEZONE_MODE_INVALID
;
40 s
->uid_shift
= UID_INVALID
;
41 s
->uid_range
= UID_INVALID
;
42 s
->no_new_privileges
= -1;
45 s
->volatile_mode
= _VOLATILE_MODE_INVALID
;
48 s
->private_network
= -1;
51 r
= config_parse(NULL
, path
, f
,
55 config_item_perf_lookup
, nspawn_gperf_lookup
,
61 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
62 * both fields shall be initialized or neither. */
63 if (s
->userns_mode
== USER_NAMESPACE_PICK
)
64 s
->userns_chown
= true;
65 else if (s
->userns_mode
!= _USER_NAMESPACE_MODE_INVALID
&& s
->userns_chown
< 0)
66 s
->userns_chown
= false;
68 if (s
->userns_chown
>= 0 && s
->userns_mode
== _USER_NAMESPACE_MODE_INVALID
)
69 s
->userns_mode
= USER_NAMESPACE_NO
;
76 Settings
* settings_free(Settings
*s
) {
81 strv_free(s
->parameters
);
82 strv_free(s
->environment
);
84 free(s
->pivot_root_new
);
85 free(s
->pivot_root_old
);
86 free(s
->working_directory
);
87 strv_free(s
->syscall_whitelist
);
88 strv_free(s
->syscall_blacklist
);
89 rlimit_free_all(s
->rlimit
);
91 s
->cpuset
= cpu_set_mfree(s
->cpuset
);
93 strv_free(s
->network_interfaces
);
94 strv_free(s
->network_macvlan
);
95 strv_free(s
->network_ipvlan
);
96 strv_free(s
->network_veth_extra
);
97 free(s
->network_bridge
);
98 free(s
->network_zone
);
99 expose_port_free_all(s
->expose_ports
);
101 custom_mount_free_all(s
->custom_mounts
, s
->n_custom_mounts
);
105 bool settings_private_network(Settings
*s
) {
109 s
->private_network
> 0 ||
110 s
->network_veth
> 0 ||
113 s
->network_interfaces
||
114 s
->network_macvlan
||
116 s
->network_veth_extra
;
119 bool settings_network_veth(Settings
*s
) {
123 s
->network_veth
> 0 ||
128 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode
, volatile_mode
, VolatileMode
, "Failed to parse volatile mode");
130 int config_parse_expose_port(
132 const char *filename
,
135 unsigned section_line
,
149 r
= expose_port_parse(&s
->expose_ports
, rvalue
);
151 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Duplicate port specification, ignoring: %s", rvalue
);
155 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse host port %s: %m", rvalue
);
162 int config_parse_capability(
164 const char *filename
,
167 unsigned section_line
,
174 uint64_t u
= 0, *result
= data
;
182 _cleanup_free_
char *word
= NULL
;
185 r
= extract_first_word(&rvalue
, &word
, NULL
, 0);
187 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract capability string, ignoring: %s", rvalue
);
193 cap
= capability_from_name(word
);
195 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability, ignoring: %s", word
);
199 u
|= UINT64_C(1) << cap
;
209 int config_parse_id128(
211 const char *filename
,
214 unsigned section_line
,
221 sd_id128_t t
, *result
= data
;
228 r
= sd_id128_from_string(rvalue
, &t
);
230 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue
);
238 int config_parse_pivot_root(
240 const char *filename
,
243 unsigned section_line
,
250 Settings
*settings
= data
;
257 r
= pivot_root_parse(&settings
->pivot_root_new
, &settings
->pivot_root_old
, rvalue
);
259 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid pivot root mount specification %s: %m", rvalue
);
266 int config_parse_bind(
268 const char *filename
,
271 unsigned section_line
,
278 Settings
*settings
= data
;
285 r
= bind_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
, ltype
);
287 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bind mount specification %s: %m", rvalue
);
294 int config_parse_tmpfs(
296 const char *filename
,
299 unsigned section_line
,
306 Settings
*settings
= data
;
313 r
= tmpfs_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
);
315 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid temporary file system specification %s: %m", rvalue
);
322 int config_parse_overlay(
324 const char *filename
,
327 unsigned section_line
,
334 Settings
*settings
= data
;
341 r
= overlay_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
, ltype
);
343 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid overlay file system specification %s, ignoring: %m", rvalue
);
348 int config_parse_veth_extra(
350 const char *filename
,
353 unsigned section_line
,
360 Settings
*settings
= data
;
367 r
= veth_extra_parse(&settings
->network_veth_extra
, rvalue
);
369 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid extra virtual Ethernet link specification %s: %m", rvalue
);
376 int config_parse_network_zone(
378 const char *filename
,
381 unsigned section_line
,
388 Settings
*settings
= data
;
389 _cleanup_free_
char *j
= NULL
;
395 j
= strappend("vz-", rvalue
);
396 if (!ifname_valid(j
)) {
397 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid network zone name %s, ignoring: %m", rvalue
);
401 free_and_replace(settings
->network_zone
, j
);
406 int config_parse_boot(
408 const char *filename
,
411 unsigned section_line
,
418 Settings
*settings
= data
;
425 r
= parse_boolean(rvalue
);
427 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue
);
432 if (settings
->start_mode
== START_PID2
)
435 settings
->start_mode
= START_BOOT
;
437 if (settings
->start_mode
== START_BOOT
)
440 if (settings
->start_mode
< 0)
441 settings
->start_mode
= START_PID1
;
447 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
451 int config_parse_pid2(
453 const char *filename
,
456 unsigned section_line
,
463 Settings
*settings
= data
;
470 r
= parse_boolean(rvalue
);
472 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue
);
477 if (settings
->start_mode
== START_BOOT
)
480 settings
->start_mode
= START_PID2
;
482 if (settings
->start_mode
== START_PID2
)
485 if (settings
->start_mode
< 0)
486 settings
->start_mode
= START_PID1
;
492 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
496 int config_parse_private_users(
498 const char *filename
,
501 unsigned section_line
,
508 Settings
*settings
= data
;
515 r
= parse_boolean(rvalue
);
517 /* no: User namespacing off */
518 settings
->userns_mode
= USER_NAMESPACE_NO
;
519 settings
->uid_shift
= UID_INVALID
;
520 settings
->uid_range
= UINT32_C(0x10000);
522 /* yes: User namespacing on, UID range is read from root dir */
523 settings
->userns_mode
= USER_NAMESPACE_FIXED
;
524 settings
->uid_shift
= UID_INVALID
;
525 settings
->uid_range
= UINT32_C(0x10000);
526 } else if (streq(rvalue
, "pick")) {
527 /* pick: User namespacing on, UID range is picked randomly */
528 settings
->userns_mode
= USER_NAMESPACE_PICK
;
529 settings
->uid_shift
= UID_INVALID
;
530 settings
->uid_range
= UINT32_C(0x10000);
532 const char *range
, *shift
;
535 /* anything else: User namespacing on, UID range is explicitly configured */
537 range
= strchr(rvalue
, ':');
539 shift
= strndupa(rvalue
, range
- rvalue
);
542 r
= safe_atou32(range
, &rn
);
543 if (r
< 0 || rn
<= 0) {
544 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "UID/GID range invalid, ignoring: %s", range
);
549 rn
= UINT32_C(0x10000);
552 r
= parse_uid(shift
, &sh
);
554 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "UID/GID shift invalid, ignoring: %s", range
);
558 settings
->userns_mode
= USER_NAMESPACE_FIXED
;
559 settings
->uid_shift
= sh
;
560 settings
->uid_range
= rn
;
566 int config_parse_syscall_filter(
568 const char *filename
,
571 unsigned section_line
,
578 Settings
*settings
= data
;
587 negative
= rvalue
[0] == '~';
588 items
= negative
? rvalue
+ 1 : rvalue
;
591 _cleanup_free_
char *word
= NULL
;
593 r
= extract_first_word(&items
, &word
, NULL
, 0);
599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue
);
604 r
= strv_extend(&settings
->syscall_blacklist
, word
);
606 r
= strv_extend(&settings
->syscall_whitelist
, word
);
614 int config_parse_hostname(
616 const char *filename
,
619 unsigned section_line
,
631 if (!hostname_is_valid(rvalue
, false)) {
632 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid hostname, ignoring: %s", rvalue
);
636 if (free_and_strdup(s
, empty_to_null(rvalue
)) < 0)
642 int config_parse_oom_score_adjust(
644 const char *filename
,
647 unsigned section_line
,
654 Settings
*settings
= data
;
660 if (isempty(rvalue
)) {
661 settings
->oom_score_adjust_set
= false;
665 r
= parse_oom_score_adjust(rvalue
, &oa
);
667 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "OOM score adjust value out of range, ignoring: %s", rvalue
);
671 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
675 settings
->oom_score_adjust
= oa
;
676 settings
->oom_score_adjust_set
= true;
681 int config_parse_cpu_affinity(
683 const char *filename
,
686 unsigned section_line
,
693 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
694 Settings
*settings
= data
;
700 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
705 /* An empty assignment resets the CPU list */
706 settings
->cpuset
= cpu_set_mfree(settings
->cpuset
);
707 settings
->cpuset_ncpus
= 0;
711 if (!settings
->cpuset
) {
712 settings
->cpuset
= TAKE_PTR(cpuset
);
713 settings
->cpuset_ncpus
= (unsigned) ncpus
;
717 if (settings
->cpuset_ncpus
< (unsigned) ncpus
) {
718 CPU_OR_S(CPU_ALLOC_SIZE(settings
->cpuset_ncpus
), cpuset
, settings
->cpuset
, cpuset
);
719 CPU_FREE(settings
->cpuset
);
720 settings
->cpuset
= TAKE_PTR(cpuset
);
721 settings
->cpuset_ncpus
= (unsigned) ncpus
;
725 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus
), settings
->cpuset
, settings
->cpuset
, cpuset
);
730 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf
, resolv_conf_mode
, ResolvConfMode
, "Failed to parse resolv.conf mode");
732 static const char *const resolv_conf_mode_table
[_RESOLV_CONF_MODE_MAX
] = {
733 [RESOLV_CONF_OFF
] = "off",
734 [RESOLV_CONF_COPY_HOST
] = "copy-host",
735 [RESOLV_CONF_COPY_STATIC
] = "copy-static",
736 [RESOLV_CONF_BIND_HOST
] = "bind-host",
737 [RESOLV_CONF_BIND_STATIC
] = "bind-static",
738 [RESOLV_CONF_DELETE
] = "delete",
739 [RESOLV_CONF_AUTO
] = "auto",
742 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode
, ResolvConfMode
, RESOLV_CONF_AUTO
);
744 int parse_link_journal(const char *s
, LinkJournal
*ret_mode
, bool *ret_try
) {
749 if (streq(s
, "auto")) {
750 *ret_mode
= LINK_AUTO
;
752 } else if (streq(s
, "no")) {
755 } else if (streq(s
, "guest")) {
756 *ret_mode
= LINK_GUEST
;
758 } else if (streq(s
, "host")) {
759 *ret_mode
= LINK_HOST
;
761 } else if (streq(s
, "try-guest")) {
762 *ret_mode
= LINK_GUEST
;
764 } else if (streq(s
, "try-host")) {
765 *ret_mode
= LINK_HOST
;
773 int config_parse_link_journal(
775 const char *filename
,
778 unsigned section_line
,
785 Settings
*settings
= data
;
791 r
= parse_link_journal(rvalue
, &settings
->link_journal
, &settings
->link_journal_try
);
793 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse link journal mode, ignoring: %s", rvalue
);
800 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone
, timezone_mode
, TimezoneMode
, "Failed to parse timezone mode");
802 static const char *const timezone_mode_table
[_TIMEZONE_MODE_MAX
] = {
803 [TIMEZONE_OFF
] = "off",
804 [TIMEZONE_COPY
] = "copy",
805 [TIMEZONE_BIND
] = "bind",
806 [TIMEZONE_SYMLINK
] = "symlink",
807 [TIMEZONE_DELETE
] = "delete",
808 [TIMEZONE_AUTO
] = "auto",
811 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode
, TimezoneMode
, TIMEZONE_AUTO
);