]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
tree-wide: use mfree more
[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 return mfree(s);
105 }
106
107 bool settings_private_network(Settings *s) {
108 assert(s);
109
110 return
111 s->private_network > 0 ||
112 s->network_veth > 0 ||
113 s->network_bridge ||
114 s->network_zone ||
115 s->network_interfaces ||
116 s->network_macvlan ||
117 s->network_ipvlan ||
118 s->network_veth_extra;
119 }
120
121 bool settings_network_veth(Settings *s) {
122 assert(s);
123
124 return
125 s->network_veth > 0 ||
126 s->network_bridge ||
127 s->network_zone;
128 }
129
130 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
131
132 int config_parse_expose_port(
133 const char *unit,
134 const char *filename,
135 unsigned line,
136 const char *section,
137 unsigned section_line,
138 const char *lvalue,
139 int ltype,
140 const char *rvalue,
141 void *data,
142 void *userdata) {
143
144 Settings *s = data;
145 int r;
146
147 assert(filename);
148 assert(lvalue);
149 assert(rvalue);
150
151 r = expose_port_parse(&s->expose_ports, rvalue);
152 if (r == -EEXIST) {
153 log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
154 return 0;
155 }
156 if (r < 0) {
157 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
158 return 0;
159 }
160
161 return 0;
162 }
163
164 int config_parse_capability(
165 const char *unit,
166 const char *filename,
167 unsigned line,
168 const char *section,
169 unsigned section_line,
170 const char *lvalue,
171 int ltype,
172 const char *rvalue,
173 void *data,
174 void *userdata) {
175
176 uint64_t u = 0, *result = data;
177 int r;
178
179 assert(filename);
180 assert(lvalue);
181 assert(rvalue);
182
183 for (;;) {
184 _cleanup_free_ char *word = NULL;
185 int cap;
186
187 r = extract_first_word(&rvalue, &word, NULL, 0);
188 if (r < 0) {
189 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
190 return 0;
191 }
192 if (r == 0)
193 break;
194
195 cap = capability_from_name(word);
196 if (cap < 0) {
197 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
198 continue;
199 }
200
201 u |= 1 << ((uint64_t) cap);
202 }
203
204 if (u == 0)
205 return 0;
206
207 *result |= u;
208 return 0;
209 }
210
211 int config_parse_id128(
212 const char *unit,
213 const char *filename,
214 unsigned line,
215 const char *section,
216 unsigned section_line,
217 const char *lvalue,
218 int ltype,
219 const char *rvalue,
220 void *data,
221 void *userdata) {
222
223 sd_id128_t t, *result = data;
224 int r;
225
226 assert(filename);
227 assert(lvalue);
228 assert(rvalue);
229
230 r = sd_id128_from_string(rvalue, &t);
231 if (r < 0) {
232 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
233 return 0;
234 }
235
236 *result = t;
237 return 0;
238 }
239
240 int config_parse_bind(
241 const char *unit,
242 const char *filename,
243 unsigned line,
244 const char *section,
245 unsigned section_line,
246 const char *lvalue,
247 int ltype,
248 const char *rvalue,
249 void *data,
250 void *userdata) {
251
252 Settings *settings = data;
253 int r;
254
255 assert(filename);
256 assert(lvalue);
257 assert(rvalue);
258
259 r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
260 if (r < 0) {
261 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
262 return 0;
263 }
264
265 return 0;
266 }
267
268 int config_parse_tmpfs(
269 const char *unit,
270 const char *filename,
271 unsigned line,
272 const char *section,
273 unsigned section_line,
274 const char *lvalue,
275 int ltype,
276 const char *rvalue,
277 void *data,
278 void *userdata) {
279
280 Settings *settings = data;
281 int r;
282
283 assert(filename);
284 assert(lvalue);
285 assert(rvalue);
286
287 r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
288 if (r < 0) {
289 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
290 return 0;
291 }
292
293 return 0;
294 }
295
296 int config_parse_veth_extra(
297 const char *unit,
298 const char *filename,
299 unsigned line,
300 const char *section,
301 unsigned section_line,
302 const char *lvalue,
303 int ltype,
304 const char *rvalue,
305 void *data,
306 void *userdata) {
307
308 Settings *settings = data;
309 int r;
310
311 assert(filename);
312 assert(lvalue);
313 assert(rvalue);
314
315 r = veth_extra_parse(&settings->network_veth_extra, rvalue);
316 if (r < 0) {
317 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
318 return 0;
319 }
320
321 return 0;
322 }
323
324 int config_parse_network_zone(
325 const char *unit,
326 const char *filename,
327 unsigned line,
328 const char *section,
329 unsigned section_line,
330 const char *lvalue,
331 int ltype,
332 const char *rvalue,
333 void *data,
334 void *userdata) {
335
336 Settings *settings = data;
337 _cleanup_free_ char *j = NULL;
338
339 assert(filename);
340 assert(lvalue);
341 assert(rvalue);
342
343 j = strappend("vz-", rvalue);
344 if (!ifname_valid(j)) {
345 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
346 return 0;
347 }
348
349 free(settings->network_zone);
350 settings->network_zone = j;
351 j = NULL;
352
353 return 0;
354 }
355
356 int config_parse_boot(
357 const char *unit,
358 const char *filename,
359 unsigned line,
360 const char *section,
361 unsigned section_line,
362 const char *lvalue,
363 int ltype,
364 const char *rvalue,
365 void *data,
366 void *userdata) {
367
368 Settings *settings = data;
369 int r;
370
371 assert(filename);
372 assert(lvalue);
373 assert(rvalue);
374
375 r = parse_boolean(rvalue);
376 if (r < 0) {
377 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
378 return 0;
379 }
380
381 if (r > 0) {
382 if (settings->start_mode == START_PID2)
383 goto conflict;
384
385 settings->start_mode = START_BOOT;
386 } else {
387 if (settings->start_mode == START_BOOT)
388 goto conflict;
389
390 if (settings->start_mode < 0)
391 settings->start_mode = START_PID1;
392 }
393
394 return 0;
395
396 conflict:
397 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
398 return 0;
399 }
400
401 int config_parse_pid2(
402 const char *unit,
403 const char *filename,
404 unsigned line,
405 const char *section,
406 unsigned section_line,
407 const char *lvalue,
408 int ltype,
409 const char *rvalue,
410 void *data,
411 void *userdata) {
412
413 Settings *settings = data;
414 int r;
415
416 assert(filename);
417 assert(lvalue);
418 assert(rvalue);
419
420 r = parse_boolean(rvalue);
421 if (r < 0) {
422 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
423 return 0;
424 }
425
426 if (r > 0) {
427 if (settings->start_mode == START_BOOT)
428 goto conflict;
429
430 settings->start_mode = START_PID2;
431 } else {
432 if (settings->start_mode == START_PID2)
433 goto conflict;
434
435 if (settings->start_mode < 0)
436 settings->start_mode = START_PID1;
437 }
438
439 return 0;
440
441 conflict:
442 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
443 return 0;
444 }
445
446 int config_parse_private_users(
447 const char *unit,
448 const char *filename,
449 unsigned line,
450 const char *section,
451 unsigned section_line,
452 const char *lvalue,
453 int ltype,
454 const char *rvalue,
455 void *data,
456 void *userdata) {
457
458 Settings *settings = data;
459 int r;
460
461 assert(filename);
462 assert(lvalue);
463 assert(rvalue);
464
465 r = parse_boolean(rvalue);
466 if (r == 0) {
467 /* no: User namespacing off */
468 settings->userns_mode = USER_NAMESPACE_NO;
469 settings->uid_shift = UID_INVALID;
470 settings->uid_range = UINT32_C(0x10000);
471 } else if (r > 0) {
472 /* yes: User namespacing on, UID range is read from root dir */
473 settings->userns_mode = USER_NAMESPACE_FIXED;
474 settings->uid_shift = UID_INVALID;
475 settings->uid_range = UINT32_C(0x10000);
476 } else if (streq(rvalue, "pick")) {
477 /* pick: User namespacing on, UID range is picked randomly */
478 settings->userns_mode = USER_NAMESPACE_PICK;
479 settings->uid_shift = UID_INVALID;
480 settings->uid_range = UINT32_C(0x10000);
481 } else {
482 const char *range, *shift;
483 uid_t sh, rn;
484
485 /* anything else: User namespacing on, UID range is explicitly configured */
486
487 range = strchr(rvalue, ':');
488 if (range) {
489 shift = strndupa(rvalue, range - rvalue);
490 range++;
491
492 r = safe_atou32(range, &rn);
493 if (r < 0 || rn <= 0) {
494 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
495 return 0;
496 }
497 } else {
498 shift = rvalue;
499 rn = UINT32_C(0x10000);
500 }
501
502 r = parse_uid(shift, &sh);
503 if (r < 0) {
504 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
505 return 0;
506 }
507
508 settings->userns_mode = USER_NAMESPACE_FIXED;
509 settings->uid_shift = sh;
510 settings->uid_range = rn;
511 }
512
513 return 0;
514 }