]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
conf-parser: turn three bool function params into a flags fields
[thirdparty/systemd.git] / src / nspawn / nspawn-settings.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2015 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "alloc-util.h"
21 #include "cap-list.h"
22 #include "conf-parser.h"
23 #include "nspawn-network.h"
24 #include "nspawn-settings.h"
25 #include "parse-util.h"
26 #include "process-util.h"
27 #include "socket-util.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "user-util.h"
31 #include "util.h"
32
33 int settings_load(FILE *f, const char *path, Settings **ret) {
34 _cleanup_(settings_freep) Settings *s = NULL;
35 int r;
36
37 assert(path);
38 assert(ret);
39
40 s = new0(Settings, 1);
41 if (!s)
42 return -ENOMEM;
43
44 s->start_mode = _START_MODE_INVALID;
45 s->personality = PERSONALITY_INVALID;
46 s->userns_mode = _USER_NAMESPACE_MODE_INVALID;
47 s->uid_shift = UID_INVALID;
48 s->uid_range = UID_INVALID;
49
50 s->read_only = -1;
51 s->volatile_mode = _VOLATILE_MODE_INVALID;
52 s->userns_chown = -1;
53
54 s->private_network = -1;
55 s->network_veth = -1;
56
57 r = config_parse(NULL, path, f,
58 "Exec\0"
59 "Network\0"
60 "Files\0",
61 config_item_perf_lookup, nspawn_gperf_lookup,
62 CONFIG_PARSE_WARN,
63 s);
64 if (r < 0)
65 return r;
66
67 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
68 * both fields shall be initialized or neither. */
69 if (s->userns_mode == USER_NAMESPACE_PICK)
70 s->userns_chown = true;
71 else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
72 s->userns_chown = false;
73
74 if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
75 s->userns_mode = USER_NAMESPACE_NO;
76
77 *ret = s;
78 s = NULL;
79
80 return 0;
81 }
82
83 Settings* settings_free(Settings *s) {
84
85 if (!s)
86 return NULL;
87
88 strv_free(s->parameters);
89 strv_free(s->environment);
90 free(s->user);
91 free(s->pivot_root_new);
92 free(s->pivot_root_old);
93 free(s->working_directory);
94 strv_free(s->syscall_whitelist);
95 strv_free(s->syscall_blacklist);
96
97 strv_free(s->network_interfaces);
98 strv_free(s->network_macvlan);
99 strv_free(s->network_ipvlan);
100 strv_free(s->network_veth_extra);
101 free(s->network_bridge);
102 free(s->network_zone);
103 expose_port_free_all(s->expose_ports);
104
105 custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
106 return mfree(s);
107 }
108
109 bool settings_private_network(Settings *s) {
110 assert(s);
111
112 return
113 s->private_network > 0 ||
114 s->network_veth > 0 ||
115 s->network_bridge ||
116 s->network_zone ||
117 s->network_interfaces ||
118 s->network_macvlan ||
119 s->network_ipvlan ||
120 s->network_veth_extra;
121 }
122
123 bool settings_network_veth(Settings *s) {
124 assert(s);
125
126 return
127 s->network_veth > 0 ||
128 s->network_bridge ||
129 s->network_zone;
130 }
131
132 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
133
134 int config_parse_expose_port(
135 const char *unit,
136 const char *filename,
137 unsigned line,
138 const char *section,
139 unsigned section_line,
140 const char *lvalue,
141 int ltype,
142 const char *rvalue,
143 void *data,
144 void *userdata) {
145
146 Settings *s = data;
147 int r;
148
149 assert(filename);
150 assert(lvalue);
151 assert(rvalue);
152
153 r = expose_port_parse(&s->expose_ports, rvalue);
154 if (r == -EEXIST) {
155 log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
156 return 0;
157 }
158 if (r < 0) {
159 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
160 return 0;
161 }
162
163 return 0;
164 }
165
166 int config_parse_capability(
167 const char *unit,
168 const char *filename,
169 unsigned line,
170 const char *section,
171 unsigned section_line,
172 const char *lvalue,
173 int ltype,
174 const char *rvalue,
175 void *data,
176 void *userdata) {
177
178 uint64_t u = 0, *result = data;
179 int r;
180
181 assert(filename);
182 assert(lvalue);
183 assert(rvalue);
184
185 for (;;) {
186 _cleanup_free_ char *word = NULL;
187 int cap;
188
189 r = extract_first_word(&rvalue, &word, NULL, 0);
190 if (r < 0) {
191 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
192 return 0;
193 }
194 if (r == 0)
195 break;
196
197 cap = capability_from_name(word);
198 if (cap < 0) {
199 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
200 continue;
201 }
202
203 u |= UINT64_C(1) << cap;
204 }
205
206 if (u == 0)
207 return 0;
208
209 *result |= u;
210 return 0;
211 }
212
213 int config_parse_id128(
214 const char *unit,
215 const char *filename,
216 unsigned line,
217 const char *section,
218 unsigned section_line,
219 const char *lvalue,
220 int ltype,
221 const char *rvalue,
222 void *data,
223 void *userdata) {
224
225 sd_id128_t t, *result = data;
226 int r;
227
228 assert(filename);
229 assert(lvalue);
230 assert(rvalue);
231
232 r = sd_id128_from_string(rvalue, &t);
233 if (r < 0) {
234 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
235 return 0;
236 }
237
238 *result = t;
239 return 0;
240 }
241
242 int config_parse_pivot_root(
243 const char *unit,
244 const char *filename,
245 unsigned line,
246 const char *section,
247 unsigned section_line,
248 const char *lvalue,
249 int ltype,
250 const char *rvalue,
251 void *data,
252 void *userdata) {
253
254 Settings *settings = data;
255 int r;
256
257 assert(filename);
258 assert(lvalue);
259 assert(rvalue);
260
261 r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
262 if (r < 0) {
263 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
264 return 0;
265 }
266
267 return 0;
268 }
269
270 int config_parse_bind(
271 const char *unit,
272 const char *filename,
273 unsigned line,
274 const char *section,
275 unsigned section_line,
276 const char *lvalue,
277 int ltype,
278 const char *rvalue,
279 void *data,
280 void *userdata) {
281
282 Settings *settings = data;
283 int r;
284
285 assert(filename);
286 assert(lvalue);
287 assert(rvalue);
288
289 r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
290 if (r < 0) {
291 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
292 return 0;
293 }
294
295 return 0;
296 }
297
298 int config_parse_tmpfs(
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 Settings *settings = data;
311 int r;
312
313 assert(filename);
314 assert(lvalue);
315 assert(rvalue);
316
317 r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
318 if (r < 0) {
319 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
320 return 0;
321 }
322
323 return 0;
324 }
325
326 int config_parse_overlay(
327 const char *unit,
328 const char *filename,
329 unsigned line,
330 const char *section,
331 unsigned section_line,
332 const char *lvalue,
333 int ltype,
334 const char *rvalue,
335 void *data,
336 void *userdata) {
337
338 Settings *settings = data;
339 int r;
340
341 assert(filename);
342 assert(lvalue);
343 assert(rvalue);
344
345 r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
346 if (r < 0)
347 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
348
349 return 0;
350 }
351
352 int config_parse_veth_extra(
353 const char *unit,
354 const char *filename,
355 unsigned line,
356 const char *section,
357 unsigned section_line,
358 const char *lvalue,
359 int ltype,
360 const char *rvalue,
361 void *data,
362 void *userdata) {
363
364 Settings *settings = data;
365 int r;
366
367 assert(filename);
368 assert(lvalue);
369 assert(rvalue);
370
371 r = veth_extra_parse(&settings->network_veth_extra, rvalue);
372 if (r < 0) {
373 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
374 return 0;
375 }
376
377 return 0;
378 }
379
380 int config_parse_network_zone(
381 const char *unit,
382 const char *filename,
383 unsigned line,
384 const char *section,
385 unsigned section_line,
386 const char *lvalue,
387 int ltype,
388 const char *rvalue,
389 void *data,
390 void *userdata) {
391
392 Settings *settings = data;
393 _cleanup_free_ char *j = NULL;
394
395 assert(filename);
396 assert(lvalue);
397 assert(rvalue);
398
399 j = strappend("vz-", rvalue);
400 if (!ifname_valid(j)) {
401 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
402 return 0;
403 }
404
405 free(settings->network_zone);
406 settings->network_zone = j;
407 j = NULL;
408
409 return 0;
410 }
411
412 int config_parse_boot(
413 const char *unit,
414 const char *filename,
415 unsigned line,
416 const char *section,
417 unsigned section_line,
418 const char *lvalue,
419 int ltype,
420 const char *rvalue,
421 void *data,
422 void *userdata) {
423
424 Settings *settings = data;
425 int r;
426
427 assert(filename);
428 assert(lvalue);
429 assert(rvalue);
430
431 r = parse_boolean(rvalue);
432 if (r < 0) {
433 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
434 return 0;
435 }
436
437 if (r > 0) {
438 if (settings->start_mode == START_PID2)
439 goto conflict;
440
441 settings->start_mode = START_BOOT;
442 } else {
443 if (settings->start_mode == START_BOOT)
444 goto conflict;
445
446 if (settings->start_mode < 0)
447 settings->start_mode = START_PID1;
448 }
449
450 return 0;
451
452 conflict:
453 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
454 return 0;
455 }
456
457 int config_parse_pid2(
458 const char *unit,
459 const char *filename,
460 unsigned line,
461 const char *section,
462 unsigned section_line,
463 const char *lvalue,
464 int ltype,
465 const char *rvalue,
466 void *data,
467 void *userdata) {
468
469 Settings *settings = data;
470 int r;
471
472 assert(filename);
473 assert(lvalue);
474 assert(rvalue);
475
476 r = parse_boolean(rvalue);
477 if (r < 0) {
478 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
479 return 0;
480 }
481
482 if (r > 0) {
483 if (settings->start_mode == START_BOOT)
484 goto conflict;
485
486 settings->start_mode = START_PID2;
487 } else {
488 if (settings->start_mode == START_PID2)
489 goto conflict;
490
491 if (settings->start_mode < 0)
492 settings->start_mode = START_PID1;
493 }
494
495 return 0;
496
497 conflict:
498 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
499 return 0;
500 }
501
502 int config_parse_private_users(
503 const char *unit,
504 const char *filename,
505 unsigned line,
506 const char *section,
507 unsigned section_line,
508 const char *lvalue,
509 int ltype,
510 const char *rvalue,
511 void *data,
512 void *userdata) {
513
514 Settings *settings = data;
515 int r;
516
517 assert(filename);
518 assert(lvalue);
519 assert(rvalue);
520
521 r = parse_boolean(rvalue);
522 if (r == 0) {
523 /* no: User namespacing off */
524 settings->userns_mode = USER_NAMESPACE_NO;
525 settings->uid_shift = UID_INVALID;
526 settings->uid_range = UINT32_C(0x10000);
527 } else if (r > 0) {
528 /* yes: User namespacing on, UID range is read from root dir */
529 settings->userns_mode = USER_NAMESPACE_FIXED;
530 settings->uid_shift = UID_INVALID;
531 settings->uid_range = UINT32_C(0x10000);
532 } else if (streq(rvalue, "pick")) {
533 /* pick: User namespacing on, UID range is picked randomly */
534 settings->userns_mode = USER_NAMESPACE_PICK;
535 settings->uid_shift = UID_INVALID;
536 settings->uid_range = UINT32_C(0x10000);
537 } else {
538 const char *range, *shift;
539 uid_t sh, rn;
540
541 /* anything else: User namespacing on, UID range is explicitly configured */
542
543 range = strchr(rvalue, ':');
544 if (range) {
545 shift = strndupa(rvalue, range - rvalue);
546 range++;
547
548 r = safe_atou32(range, &rn);
549 if (r < 0 || rn <= 0) {
550 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
551 return 0;
552 }
553 } else {
554 shift = rvalue;
555 rn = UINT32_C(0x10000);
556 }
557
558 r = parse_uid(shift, &sh);
559 if (r < 0) {
560 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
561 return 0;
562 }
563
564 settings->userns_mode = USER_NAMESPACE_FIXED;
565 settings->uid_shift = sh;
566 settings->uid_range = rn;
567 }
568
569 return 0;
570 }
571
572 int config_parse_syscall_filter(
573 const char *unit,
574 const char *filename,
575 unsigned line,
576 const char *section,
577 unsigned section_line,
578 const char *lvalue,
579 int ltype,
580 const char *rvalue,
581 void *data,
582 void *userdata) {
583
584 Settings *settings = data;
585 bool negative;
586 const char *items;
587 int r;
588
589 assert(filename);
590 assert(lvalue);
591 assert(rvalue);
592
593 negative = rvalue[0] == '~';
594 items = negative ? rvalue + 1 : rvalue;
595
596 for (;;) {
597 _cleanup_free_ char *word = NULL;
598
599 r = extract_first_word(&items, &word, NULL, 0);
600 if (r == 0)
601 break;
602 if (r == -ENOMEM)
603 return log_oom();
604 if (r < 0) {
605 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
606 return 0;
607 }
608
609 if (negative)
610 r = strv_extend(&settings->syscall_blacklist, word);
611 else
612 r = strv_extend(&settings->syscall_whitelist, word);
613 if (r < 0)
614 return log_oom();
615 }
616
617 return 0;
618 }