]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
Merge pull request #653 from dvdhrm/bus-gold
[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 false,
63 false,
64 true,
65 s);
66 if (r < 0)
67 return r;
68
69 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
70 * both fields shall be initialized or neither. */
71 if (s->userns_mode == USER_NAMESPACE_PICK)
72 s->userns_chown = true;
73 else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
74 s->userns_chown = false;
75
76 if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
77 s->userns_mode = USER_NAMESPACE_NO;
78
79 *ret = s;
80 s = NULL;
81
82 return 0;
83 }
84
85 Settings* settings_free(Settings *s) {
86
87 if (!s)
88 return NULL;
89
90 strv_free(s->parameters);
91 strv_free(s->environment);
92 free(s->user);
93 free(s->working_directory);
94
95 strv_free(s->network_interfaces);
96 strv_free(s->network_macvlan);
97 strv_free(s->network_ipvlan);
98 strv_free(s->network_veth_extra);
99 free(s->network_bridge);
100 free(s->network_zone);
101 expose_port_free_all(s->expose_ports);
102
103 custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
104 free(s);
105
106 return NULL;
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 |= 1 << ((uint64_t) 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_bind(
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 = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
262 if (r < 0) {
263 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
264 return 0;
265 }
266
267 return 0;
268 }
269
270 int config_parse_tmpfs(
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 = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
290 if (r < 0) {
291 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
292 return 0;
293 }
294
295 return 0;
296 }
297
298 int config_parse_veth_extra(
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 = veth_extra_parse(&settings->network_veth_extra, rvalue);
318 if (r < 0) {
319 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
320 return 0;
321 }
322
323 return 0;
324 }
325
326 int config_parse_network_zone(
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 _cleanup_free_ char *j = NULL;
340
341 assert(filename);
342 assert(lvalue);
343 assert(rvalue);
344
345 j = strappend("vz-", rvalue);
346 if (!ifname_valid(j)) {
347 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
348 return 0;
349 }
350
351 free(settings->network_zone);
352 settings->network_zone = j;
353 j = NULL;
354
355 return 0;
356 }
357
358 int config_parse_boot(
359 const char *unit,
360 const char *filename,
361 unsigned line,
362 const char *section,
363 unsigned section_line,
364 const char *lvalue,
365 int ltype,
366 const char *rvalue,
367 void *data,
368 void *userdata) {
369
370 Settings *settings = data;
371 int r;
372
373 assert(filename);
374 assert(lvalue);
375 assert(rvalue);
376
377 r = parse_boolean(rvalue);
378 if (r < 0) {
379 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
380 return 0;
381 }
382
383 if (r > 0) {
384 if (settings->start_mode == START_PID2)
385 goto conflict;
386
387 settings->start_mode = START_BOOT;
388 } else {
389 if (settings->start_mode == START_BOOT)
390 goto conflict;
391
392 if (settings->start_mode < 0)
393 settings->start_mode = START_PID1;
394 }
395
396 return 0;
397
398 conflict:
399 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
400 return 0;
401 }
402
403 int config_parse_pid2(
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 ProcessTwo= parameter %s, ignoring: %m", rvalue);
425 return 0;
426 }
427
428 if (r > 0) {
429 if (settings->start_mode == START_BOOT)
430 goto conflict;
431
432 settings->start_mode = START_PID2;
433 } else {
434 if (settings->start_mode == START_PID2)
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_private_users(
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 /* no: User namespacing off */
470 settings->userns_mode = USER_NAMESPACE_NO;
471 settings->uid_shift = UID_INVALID;
472 settings->uid_range = UINT32_C(0x10000);
473 } else if (r > 0) {
474 /* yes: User namespacing on, UID range is read from root dir */
475 settings->userns_mode = USER_NAMESPACE_FIXED;
476 settings->uid_shift = UID_INVALID;
477 settings->uid_range = UINT32_C(0x10000);
478 } else if (streq(rvalue, "pick")) {
479 /* pick: User namespacing on, UID range is picked randomly */
480 settings->userns_mode = USER_NAMESPACE_PICK;
481 settings->uid_shift = UID_INVALID;
482 settings->uid_range = UINT32_C(0x10000);
483 } else {
484 const char *range, *shift;
485 uid_t sh, rn;
486
487 /* anything else: User namespacing on, UID range is explicitly configured */
488
489 range = strchr(rvalue, ':');
490 if (range) {
491 shift = strndupa(rvalue, range - rvalue);
492 range++;
493
494 r = safe_atou32(range, &rn);
495 if (r < 0 || rn <= 0) {
496 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
497 return 0;
498 }
499 } else {
500 shift = rvalue;
501 rn = UINT32_C(0x10000);
502 }
503
504 r = parse_uid(shift, &sh);
505 if (r < 0) {
506 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
507 return 0;
508 }
509
510 settings->userns_mode = USER_NAMESPACE_FIXED;
511 settings->uid_shift = sh;
512 settings->uid_range = rn;
513 }
514
515 return 0;
516 }