1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.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"
17 #include "user-util.h"
20 int settings_load(FILE *f
, const char *path
, Settings
**ret
) {
21 _cleanup_(settings_freep
) Settings
*s
= NULL
;
27 s
= new0(Settings
, 1);
31 s
->start_mode
= _START_MODE_INVALID
;
32 s
->personality
= PERSONALITY_INVALID
;
33 s
->userns_mode
= _USER_NAMESPACE_MODE_INVALID
;
34 s
->resolv_conf
= _RESOLV_CONF_MODE_INVALID
;
35 s
->link_journal
= _LINK_JOURNAL_INVALID
;
36 s
->timezone
= _TIMEZONE_MODE_INVALID
;
37 s
->uid_shift
= UID_INVALID
;
38 s
->uid_range
= UID_INVALID
;
39 s
->no_new_privileges
= -1;
42 s
->volatile_mode
= _VOLATILE_MODE_INVALID
;
45 s
->private_network
= -1;
48 r
= config_parse(NULL
, path
, f
,
52 config_item_perf_lookup
, nspawn_gperf_lookup
,
58 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
59 * both fields shall be initialized or neither. */
60 if (s
->userns_mode
== USER_NAMESPACE_PICK
)
61 s
->userns_chown
= true;
62 else if (s
->userns_mode
!= _USER_NAMESPACE_MODE_INVALID
&& s
->userns_chown
< 0)
63 s
->userns_chown
= false;
65 if (s
->userns_chown
>= 0 && s
->userns_mode
== _USER_NAMESPACE_MODE_INVALID
)
66 s
->userns_mode
= USER_NAMESPACE_NO
;
73 Settings
* settings_free(Settings
*s
) {
78 strv_free(s
->parameters
);
79 strv_free(s
->environment
);
81 free(s
->pivot_root_new
);
82 free(s
->pivot_root_old
);
83 free(s
->working_directory
);
84 strv_free(s
->syscall_whitelist
);
85 strv_free(s
->syscall_blacklist
);
86 rlimit_free_all(s
->rlimit
);
88 s
->cpuset
= cpu_set_mfree(s
->cpuset
);
90 strv_free(s
->network_interfaces
);
91 strv_free(s
->network_macvlan
);
92 strv_free(s
->network_ipvlan
);
93 strv_free(s
->network_veth_extra
);
94 free(s
->network_bridge
);
95 free(s
->network_zone
);
96 expose_port_free_all(s
->expose_ports
);
98 custom_mount_free_all(s
->custom_mounts
, s
->n_custom_mounts
);
102 bool settings_private_network(Settings
*s
) {
106 s
->private_network
> 0 ||
107 s
->network_veth
> 0 ||
110 s
->network_interfaces
||
111 s
->network_macvlan
||
113 s
->network_veth_extra
;
116 bool settings_network_veth(Settings
*s
) {
120 s
->network_veth
> 0 ||
125 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode
, volatile_mode
, VolatileMode
, "Failed to parse volatile mode");
127 int config_parse_expose_port(
129 const char *filename
,
132 unsigned section_line
,
146 r
= expose_port_parse(&s
->expose_ports
, rvalue
);
148 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Duplicate port specification, ignoring: %s", rvalue
);
152 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse host port %s: %m", rvalue
);
159 int config_parse_capability(
161 const char *filename
,
164 unsigned section_line
,
171 uint64_t u
= 0, *result
= data
;
179 _cleanup_free_
char *word
= NULL
;
182 r
= extract_first_word(&rvalue
, &word
, NULL
, 0);
184 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract capability string, ignoring: %s", rvalue
);
190 cap
= capability_from_name(word
);
192 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability, ignoring: %s", word
);
196 u
|= UINT64_C(1) << cap
;
206 int config_parse_id128(
208 const char *filename
,
211 unsigned section_line
,
218 sd_id128_t t
, *result
= data
;
225 r
= sd_id128_from_string(rvalue
, &t
);
227 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue
);
235 int config_parse_pivot_root(
237 const char *filename
,
240 unsigned section_line
,
247 Settings
*settings
= data
;
254 r
= pivot_root_parse(&settings
->pivot_root_new
, &settings
->pivot_root_old
, rvalue
);
256 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid pivot root mount specification %s: %m", rvalue
);
263 int config_parse_bind(
265 const char *filename
,
268 unsigned section_line
,
275 Settings
*settings
= data
;
282 r
= bind_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
, ltype
);
284 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bind mount specification %s: %m", rvalue
);
291 int config_parse_tmpfs(
293 const char *filename
,
296 unsigned section_line
,
303 Settings
*settings
= data
;
310 r
= tmpfs_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
);
312 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid temporary file system specification %s: %m", rvalue
);
319 int config_parse_overlay(
321 const char *filename
,
324 unsigned section_line
,
331 Settings
*settings
= data
;
338 r
= overlay_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
, ltype
);
340 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid overlay file system specification %s, ignoring: %m", rvalue
);
345 int config_parse_veth_extra(
347 const char *filename
,
350 unsigned section_line
,
357 Settings
*settings
= data
;
364 r
= veth_extra_parse(&settings
->network_veth_extra
, rvalue
);
366 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid extra virtual Ethernet link specification %s: %m", rvalue
);
373 int config_parse_network_zone(
375 const char *filename
,
378 unsigned section_line
,
385 Settings
*settings
= data
;
386 _cleanup_free_
char *j
= NULL
;
392 j
= strappend("vz-", rvalue
);
393 if (!ifname_valid(j
)) {
394 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid network zone name, ignoring: %s", rvalue
);
398 free_and_replace(settings
->network_zone
, j
);
403 int config_parse_boot(
405 const char *filename
,
408 unsigned section_line
,
415 Settings
*settings
= data
;
422 r
= parse_boolean(rvalue
);
424 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue
);
429 if (settings
->start_mode
== START_PID2
)
432 settings
->start_mode
= START_BOOT
;
434 if (settings
->start_mode
== START_BOOT
)
437 if (settings
->start_mode
< 0)
438 settings
->start_mode
= START_PID1
;
444 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
448 int config_parse_pid2(
450 const char *filename
,
453 unsigned section_line
,
460 Settings
*settings
= data
;
467 r
= parse_boolean(rvalue
);
469 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue
);
474 if (settings
->start_mode
== START_BOOT
)
477 settings
->start_mode
= START_PID2
;
479 if (settings
->start_mode
== START_PID2
)
482 if (settings
->start_mode
< 0)
483 settings
->start_mode
= START_PID1
;
489 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
493 int config_parse_private_users(
495 const char *filename
,
498 unsigned section_line
,
505 Settings
*settings
= data
;
512 r
= parse_boolean(rvalue
);
514 /* no: User namespacing off */
515 settings
->userns_mode
= USER_NAMESPACE_NO
;
516 settings
->uid_shift
= UID_INVALID
;
517 settings
->uid_range
= UINT32_C(0x10000);
519 /* yes: User namespacing on, UID range is read from root dir */
520 settings
->userns_mode
= USER_NAMESPACE_FIXED
;
521 settings
->uid_shift
= UID_INVALID
;
522 settings
->uid_range
= UINT32_C(0x10000);
523 } else if (streq(rvalue
, "pick")) {
524 /* pick: User namespacing on, UID range is picked randomly */
525 settings
->userns_mode
= USER_NAMESPACE_PICK
;
526 settings
->uid_shift
= UID_INVALID
;
527 settings
->uid_range
= UINT32_C(0x10000);
529 const char *range
, *shift
;
532 /* anything else: User namespacing on, UID range is explicitly configured */
534 range
= strchr(rvalue
, ':');
536 shift
= strndupa(rvalue
, range
- rvalue
);
539 r
= safe_atou32(range
, &rn
);
540 if (r
< 0 || rn
<= 0) {
541 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "UID/GID range invalid, ignoring: %s", range
);
546 rn
= UINT32_C(0x10000);
549 r
= parse_uid(shift
, &sh
);
551 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "UID/GID shift invalid, ignoring: %s", range
);
555 settings
->userns_mode
= USER_NAMESPACE_FIXED
;
556 settings
->uid_shift
= sh
;
557 settings
->uid_range
= rn
;
563 int config_parse_syscall_filter(
565 const char *filename
,
568 unsigned section_line
,
575 Settings
*settings
= data
;
584 negative
= rvalue
[0] == '~';
585 items
= negative
? rvalue
+ 1 : rvalue
;
588 _cleanup_free_
char *word
= NULL
;
590 r
= extract_first_word(&items
, &word
, NULL
, 0);
596 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue
);
601 r
= strv_extend(&settings
->syscall_blacklist
, word
);
603 r
= strv_extend(&settings
->syscall_whitelist
, word
);
611 int config_parse_hostname(
613 const char *filename
,
616 unsigned section_line
,
628 if (!hostname_is_valid(rvalue
, false)) {
629 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid hostname, ignoring: %s", rvalue
);
633 if (free_and_strdup(s
, empty_to_null(rvalue
)) < 0)
639 int config_parse_oom_score_adjust(
641 const char *filename
,
644 unsigned section_line
,
651 Settings
*settings
= data
;
657 if (isempty(rvalue
)) {
658 settings
->oom_score_adjust_set
= false;
662 r
= parse_oom_score_adjust(rvalue
, &oa
);
664 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "OOM score adjust value out of range, ignoring: %s", rvalue
);
668 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
672 settings
->oom_score_adjust
= oa
;
673 settings
->oom_score_adjust_set
= true;
678 int config_parse_cpu_affinity(
680 const char *filename
,
683 unsigned section_line
,
690 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
691 Settings
*settings
= data
;
697 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
702 /* An empty assignment resets the CPU list */
703 settings
->cpuset
= cpu_set_mfree(settings
->cpuset
);
704 settings
->cpuset_ncpus
= 0;
708 if (!settings
->cpuset
) {
709 settings
->cpuset
= TAKE_PTR(cpuset
);
710 settings
->cpuset_ncpus
= (unsigned) ncpus
;
714 if (settings
->cpuset_ncpus
< (unsigned) ncpus
) {
715 CPU_OR_S(CPU_ALLOC_SIZE(settings
->cpuset_ncpus
), cpuset
, settings
->cpuset
, cpuset
);
716 CPU_FREE(settings
->cpuset
);
717 settings
->cpuset
= TAKE_PTR(cpuset
);
718 settings
->cpuset_ncpus
= (unsigned) ncpus
;
722 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus
), settings
->cpuset
, settings
->cpuset
, cpuset
);
727 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf
, resolv_conf_mode
, ResolvConfMode
, "Failed to parse resolv.conf mode");
729 static const char *const resolv_conf_mode_table
[_RESOLV_CONF_MODE_MAX
] = {
730 [RESOLV_CONF_OFF
] = "off",
731 [RESOLV_CONF_COPY_HOST
] = "copy-host",
732 [RESOLV_CONF_COPY_STATIC
] = "copy-static",
733 [RESOLV_CONF_BIND_HOST
] = "bind-host",
734 [RESOLV_CONF_BIND_STATIC
] = "bind-static",
735 [RESOLV_CONF_DELETE
] = "delete",
736 [RESOLV_CONF_AUTO
] = "auto",
739 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode
, ResolvConfMode
, RESOLV_CONF_AUTO
);
741 int parse_link_journal(const char *s
, LinkJournal
*ret_mode
, bool *ret_try
) {
746 if (streq(s
, "auto")) {
747 *ret_mode
= LINK_AUTO
;
749 } else if (streq(s
, "no")) {
752 } else if (streq(s
, "guest")) {
753 *ret_mode
= LINK_GUEST
;
755 } else if (streq(s
, "host")) {
756 *ret_mode
= LINK_HOST
;
758 } else if (streq(s
, "try-guest")) {
759 *ret_mode
= LINK_GUEST
;
761 } else if (streq(s
, "try-host")) {
762 *ret_mode
= LINK_HOST
;
770 int config_parse_link_journal(
772 const char *filename
,
775 unsigned section_line
,
782 Settings
*settings
= data
;
788 r
= parse_link_journal(rvalue
, &settings
->link_journal
, &settings
->link_journal_try
);
790 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse link journal mode, ignoring: %s", rvalue
);
797 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone
, timezone_mode
, TimezoneMode
, "Failed to parse timezone mode");
799 static const char *const timezone_mode_table
[_TIMEZONE_MODE_MAX
] = {
800 [TIMEZONE_OFF
] = "off",
801 [TIMEZONE_COPY
] = "copy",
802 [TIMEZONE_BIND
] = "bind",
803 [TIMEZONE_SYMLINK
] = "symlink",
804 [TIMEZONE_DELETE
] = "delete",
805 [TIMEZONE_AUTO
] = "auto",
808 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode
, TimezoneMode
, TIMEZONE_AUTO
);