]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[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 int settings_load(FILE *f, const char *path, Settings **ret) {
21 _cleanup_(settings_freep) Settings *s = NULL;
22 int r;
23
24 assert(path);
25 assert(ret);
26
27 s = new0(Settings, 1);
28 if (!s)
29 return -ENOMEM;
30
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;
40
41 s->read_only = -1;
42 s->volatile_mode = _VOLATILE_MODE_INVALID;
43 s->userns_chown = -1;
44
45 s->private_network = -1;
46 s->network_veth = -1;
47
48 r = config_parse(NULL, path, f,
49 "Exec\0"
50 "Network\0"
51 "Files\0",
52 config_item_perf_lookup, nspawn_gperf_lookup,
53 CONFIG_PARSE_WARN,
54 s);
55 if (r < 0)
56 return r;
57
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;
64
65 if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
66 s->userns_mode = USER_NAMESPACE_NO;
67
68 *ret = TAKE_PTR(s);
69
70 return 0;
71 }
72
73 Settings* settings_free(Settings *s) {
74
75 if (!s)
76 return NULL;
77
78 strv_free(s->parameters);
79 strv_free(s->environment);
80 free(s->user);
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);
87 free(s->hostname);
88 s->cpuset = cpu_set_mfree(s->cpuset);
89
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);
97
98 custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
99 return mfree(s);
100 }
101
102 bool settings_private_network(Settings *s) {
103 assert(s);
104
105 return
106 s->private_network > 0 ||
107 s->network_veth > 0 ||
108 s->network_bridge ||
109 s->network_zone ||
110 s->network_interfaces ||
111 s->network_macvlan ||
112 s->network_ipvlan ||
113 s->network_veth_extra;
114 }
115
116 bool settings_network_veth(Settings *s) {
117 assert(s);
118
119 return
120 s->network_veth > 0 ||
121 s->network_bridge ||
122 s->network_zone;
123 }
124
125 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
126
127 int config_parse_expose_port(
128 const char *unit,
129 const char *filename,
130 unsigned line,
131 const char *section,
132 unsigned section_line,
133 const char *lvalue,
134 int ltype,
135 const char *rvalue,
136 void *data,
137 void *userdata) {
138
139 Settings *s = data;
140 int r;
141
142 assert(filename);
143 assert(lvalue);
144 assert(rvalue);
145
146 r = expose_port_parse(&s->expose_ports, rvalue);
147 if (r == -EEXIST) {
148 log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
149 return 0;
150 }
151 if (r < 0) {
152 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
153 return 0;
154 }
155
156 return 0;
157 }
158
159 int config_parse_capability(
160 const char *unit,
161 const char *filename,
162 unsigned line,
163 const char *section,
164 unsigned section_line,
165 const char *lvalue,
166 int ltype,
167 const char *rvalue,
168 void *data,
169 void *userdata) {
170
171 uint64_t u = 0, *result = data;
172 int r;
173
174 assert(filename);
175 assert(lvalue);
176 assert(rvalue);
177
178 for (;;) {
179 _cleanup_free_ char *word = NULL;
180 int cap;
181
182 r = extract_first_word(&rvalue, &word, NULL, 0);
183 if (r < 0) {
184 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
185 return 0;
186 }
187 if (r == 0)
188 break;
189
190 cap = capability_from_name(word);
191 if (cap < 0) {
192 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
193 continue;
194 }
195
196 u |= UINT64_C(1) << cap;
197 }
198
199 if (u == 0)
200 return 0;
201
202 *result |= u;
203 return 0;
204 }
205
206 int config_parse_id128(
207 const char *unit,
208 const char *filename,
209 unsigned line,
210 const char *section,
211 unsigned section_line,
212 const char *lvalue,
213 int ltype,
214 const char *rvalue,
215 void *data,
216 void *userdata) {
217
218 sd_id128_t t, *result = data;
219 int r;
220
221 assert(filename);
222 assert(lvalue);
223 assert(rvalue);
224
225 r = sd_id128_from_string(rvalue, &t);
226 if (r < 0) {
227 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
228 return 0;
229 }
230
231 *result = t;
232 return 0;
233 }
234
235 int config_parse_pivot_root(
236 const char *unit,
237 const char *filename,
238 unsigned line,
239 const char *section,
240 unsigned section_line,
241 const char *lvalue,
242 int ltype,
243 const char *rvalue,
244 void *data,
245 void *userdata) {
246
247 Settings *settings = data;
248 int r;
249
250 assert(filename);
251 assert(lvalue);
252 assert(rvalue);
253
254 r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
255 if (r < 0) {
256 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
257 return 0;
258 }
259
260 return 0;
261 }
262
263 int config_parse_bind(
264 const char *unit,
265 const char *filename,
266 unsigned line,
267 const char *section,
268 unsigned section_line,
269 const char *lvalue,
270 int ltype,
271 const char *rvalue,
272 void *data,
273 void *userdata) {
274
275 Settings *settings = data;
276 int r;
277
278 assert(filename);
279 assert(lvalue);
280 assert(rvalue);
281
282 r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
283 if (r < 0) {
284 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
285 return 0;
286 }
287
288 return 0;
289 }
290
291 int config_parse_tmpfs(
292 const char *unit,
293 const char *filename,
294 unsigned line,
295 const char *section,
296 unsigned section_line,
297 const char *lvalue,
298 int ltype,
299 const char *rvalue,
300 void *data,
301 void *userdata) {
302
303 Settings *settings = data;
304 int r;
305
306 assert(filename);
307 assert(lvalue);
308 assert(rvalue);
309
310 r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
311 if (r < 0) {
312 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
313 return 0;
314 }
315
316 return 0;
317 }
318
319 int config_parse_overlay(
320 const char *unit,
321 const char *filename,
322 unsigned line,
323 const char *section,
324 unsigned section_line,
325 const char *lvalue,
326 int ltype,
327 const char *rvalue,
328 void *data,
329 void *userdata) {
330
331 Settings *settings = data;
332 int r;
333
334 assert(filename);
335 assert(lvalue);
336 assert(rvalue);
337
338 r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
339 if (r < 0)
340 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
341
342 return 0;
343 }
344
345 int config_parse_veth_extra(
346 const char *unit,
347 const char *filename,
348 unsigned line,
349 const char *section,
350 unsigned section_line,
351 const char *lvalue,
352 int ltype,
353 const char *rvalue,
354 void *data,
355 void *userdata) {
356
357 Settings *settings = data;
358 int r;
359
360 assert(filename);
361 assert(lvalue);
362 assert(rvalue);
363
364 r = veth_extra_parse(&settings->network_veth_extra, rvalue);
365 if (r < 0) {
366 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
367 return 0;
368 }
369
370 return 0;
371 }
372
373 int config_parse_network_zone(
374 const char *unit,
375 const char *filename,
376 unsigned line,
377 const char *section,
378 unsigned section_line,
379 const char *lvalue,
380 int ltype,
381 const char *rvalue,
382 void *data,
383 void *userdata) {
384
385 Settings *settings = data;
386 _cleanup_free_ char *j = NULL;
387
388 assert(filename);
389 assert(lvalue);
390 assert(rvalue);
391
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);
395 return 0;
396 }
397
398 free_and_replace(settings->network_zone, j);
399
400 return 0;
401 }
402
403 int config_parse_boot(
404 const char *unit,
405 const char *filename,
406 unsigned line,
407 const char *section,
408 unsigned section_line,
409 const char *lvalue,
410 int ltype,
411 const char *rvalue,
412 void *data,
413 void *userdata) {
414
415 Settings *settings = data;
416 int r;
417
418 assert(filename);
419 assert(lvalue);
420 assert(rvalue);
421
422 r = parse_boolean(rvalue);
423 if (r < 0) {
424 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
425 return 0;
426 }
427
428 if (r > 0) {
429 if (settings->start_mode == START_PID2)
430 goto conflict;
431
432 settings->start_mode = START_BOOT;
433 } else {
434 if (settings->start_mode == START_BOOT)
435 goto conflict;
436
437 if (settings->start_mode < 0)
438 settings->start_mode = START_PID1;
439 }
440
441 return 0;
442
443 conflict:
444 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
445 return 0;
446 }
447
448 int config_parse_pid2(
449 const char *unit,
450 const char *filename,
451 unsigned line,
452 const char *section,
453 unsigned section_line,
454 const char *lvalue,
455 int ltype,
456 const char *rvalue,
457 void *data,
458 void *userdata) {
459
460 Settings *settings = data;
461 int r;
462
463 assert(filename);
464 assert(lvalue);
465 assert(rvalue);
466
467 r = parse_boolean(rvalue);
468 if (r < 0) {
469 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
470 return 0;
471 }
472
473 if (r > 0) {
474 if (settings->start_mode == START_BOOT)
475 goto conflict;
476
477 settings->start_mode = START_PID2;
478 } else {
479 if (settings->start_mode == START_PID2)
480 goto conflict;
481
482 if (settings->start_mode < 0)
483 settings->start_mode = START_PID1;
484 }
485
486 return 0;
487
488 conflict:
489 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
490 return 0;
491 }
492
493 int config_parse_private_users(
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 int r;
507
508 assert(filename);
509 assert(lvalue);
510 assert(rvalue);
511
512 r = parse_boolean(rvalue);
513 if (r == 0) {
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);
518 } else if (r > 0) {
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);
528 } else {
529 const char *range, *shift;
530 uid_t sh, rn;
531
532 /* anything else: User namespacing on, UID range is explicitly configured */
533
534 range = strchr(rvalue, ':');
535 if (range) {
536 shift = strndupa(rvalue, range - rvalue);
537 range++;
538
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);
542 return 0;
543 }
544 } else {
545 shift = rvalue;
546 rn = UINT32_C(0x10000);
547 }
548
549 r = parse_uid(shift, &sh);
550 if (r < 0) {
551 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
552 return 0;
553 }
554
555 settings->userns_mode = USER_NAMESPACE_FIXED;
556 settings->uid_shift = sh;
557 settings->uid_range = rn;
558 }
559
560 return 0;
561 }
562
563 int config_parse_syscall_filter(
564 const char *unit,
565 const char *filename,
566 unsigned line,
567 const char *section,
568 unsigned section_line,
569 const char *lvalue,
570 int ltype,
571 const char *rvalue,
572 void *data,
573 void *userdata) {
574
575 Settings *settings = data;
576 bool negative;
577 const char *items;
578 int r;
579
580 assert(filename);
581 assert(lvalue);
582 assert(rvalue);
583
584 negative = rvalue[0] == '~';
585 items = negative ? rvalue + 1 : rvalue;
586
587 for (;;) {
588 _cleanup_free_ char *word = NULL;
589
590 r = extract_first_word(&items, &word, NULL, 0);
591 if (r == 0)
592 break;
593 if (r == -ENOMEM)
594 return log_oom();
595 if (r < 0) {
596 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
597 return 0;
598 }
599
600 if (negative)
601 r = strv_extend(&settings->syscall_blacklist, word);
602 else
603 r = strv_extend(&settings->syscall_whitelist, word);
604 if (r < 0)
605 return log_oom();
606 }
607
608 return 0;
609 }
610
611 int config_parse_hostname(
612 const char *unit,
613 const char *filename,
614 unsigned line,
615 const char *section,
616 unsigned section_line,
617 const char *lvalue,
618 int ltype,
619 const char *rvalue,
620 void *data,
621 void *userdata) {
622
623 char **s = data;
624
625 assert(rvalue);
626 assert(s);
627
628 if (!hostname_is_valid(rvalue, false)) {
629 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue);
630 return 0;
631 }
632
633 if (free_and_strdup(s, empty_to_null(rvalue)) < 0)
634 return log_oom();
635
636 return 0;
637 }
638
639 int config_parse_oom_score_adjust(
640 const char *unit,
641 const char *filename,
642 unsigned line,
643 const char *section,
644 unsigned section_line,
645 const char *lvalue,
646 int ltype,
647 const char *rvalue,
648 void *data,
649 void *userdata) {
650
651 Settings *settings = data;
652 int oa, r;
653
654 assert(rvalue);
655 assert(settings);
656
657 if (isempty(rvalue)) {
658 settings->oom_score_adjust_set = false;
659 return 0;
660 }
661
662 r = parse_oom_score_adjust(rvalue, &oa);
663 if (r == -ERANGE) {
664 log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
665 return 0;
666 }
667 if (r < 0) {
668 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
669 return 0;
670 }
671
672 settings->oom_score_adjust = oa;
673 settings->oom_score_adjust_set = true;
674
675 return 0;
676 }
677
678 int config_parse_cpu_affinity(
679 const char *unit,
680 const char *filename,
681 unsigned line,
682 const char *section,
683 unsigned section_line,
684 const char *lvalue,
685 int ltype,
686 const char *rvalue,
687 void *data,
688 void *userdata) {
689
690 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
691 Settings *settings = data;
692 int ncpus;
693
694 assert(rvalue);
695 assert(settings);
696
697 ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
698 if (ncpus < 0)
699 return ncpus;
700
701 if (ncpus == 0) {
702 /* An empty assignment resets the CPU list */
703 settings->cpuset = cpu_set_mfree(settings->cpuset);
704 settings->cpuset_ncpus = 0;
705 return 0;
706 }
707
708 if (!settings->cpuset) {
709 settings->cpuset = TAKE_PTR(cpuset);
710 settings->cpuset_ncpus = (unsigned) ncpus;
711 return 0;
712 }
713
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;
719 return 0;
720 }
721
722 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset);
723
724 return 0;
725 }
726
727 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
728
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",
737 };
738
739 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode, ResolvConfMode, RESOLV_CONF_AUTO);
740
741 int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try) {
742 assert(s);
743 assert(ret_mode);
744 assert(ret_try);
745
746 if (streq(s, "auto")) {
747 *ret_mode = LINK_AUTO;
748 *ret_try = false;
749 } else if (streq(s, "no")) {
750 *ret_mode = LINK_NO;
751 *ret_try = false;
752 } else if (streq(s, "guest")) {
753 *ret_mode = LINK_GUEST;
754 *ret_try = false;
755 } else if (streq(s, "host")) {
756 *ret_mode = LINK_HOST;
757 *ret_try = false;
758 } else if (streq(s, "try-guest")) {
759 *ret_mode = LINK_GUEST;
760 *ret_try = true;
761 } else if (streq(s, "try-host")) {
762 *ret_mode = LINK_HOST;
763 *ret_try = true;
764 } else
765 return -EINVAL;
766
767 return 0;
768 }
769
770 int config_parse_link_journal(
771 const char *unit,
772 const char *filename,
773 unsigned line,
774 const char *section,
775 unsigned section_line,
776 const char *lvalue,
777 int ltype,
778 const char *rvalue,
779 void *data,
780 void *userdata) {
781
782 Settings *settings = data;
783 int r;
784
785 assert(rvalue);
786 assert(settings);
787
788 r = parse_link_journal(rvalue, &settings->link_journal, &settings->link_journal_try);
789 if (r < 0) {
790 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse link journal mode, ignoring: %s", rvalue);
791 return 0;
792 }
793
794 return 0;
795 }
796
797 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone, timezone_mode, TimezoneMode, "Failed to parse timezone mode");
798
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",
806 };
807
808 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);