]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image-dbus.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / machine / image-dbus.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ebeccf9e
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
fe993888 21#include <sys/file.h>
9153b02b
LP
22#include <sys/mount.h>
23
b5efdb8a 24#include "alloc-util.h"
ebeccf9e 25#include "bus-label.h"
1ddb263d 26#include "bus-util.h"
9153b02b
LP
27#include "copy.h"
28#include "dissect-image.h"
56599585 29#include "fd-util.h"
9153b02b
LP
30#include "fileio.h"
31#include "fs-util.h"
003dffde 32#include "image-dbus.h"
a90fb858 33#include "io-util.h"
9153b02b 34#include "loop-util.h"
ee104e11 35#include "machine-image.h"
9153b02b 36#include "mount-util.h"
56599585 37#include "process-util.h"
9153b02b 38#include "raw-clone.h"
ee104e11
LP
39#include "strv.h"
40#include "user-util.h"
ebeccf9e 41
1ddb263d 42static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
ebeccf9e 43
1ddb263d 44int bus_image_method_remove(
08682124
LP
45 sd_bus_message *message,
46 void *userdata,
47 sd_bus_error *error) {
48
5d2036b5 49 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1ddb263d 50 Image *image = userdata;
70244d1d 51 Manager *m = image->userdata;
5d2036b5 52 pid_t child;
08682124
LP
53 int r;
54
08682124 55 assert(message);
1ddb263d 56 assert(image);
08682124 57
5d2036b5
LP
58 if (m->n_operations >= OPERATIONS_MAX)
59 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
60
70244d1d
LP
61 r = bus_verify_polkit_async(
62 message,
63 CAP_SYS_ADMIN,
64 "org.freedesktop.machine1.manage-images",
403ed0e5 65 NULL,
70244d1d 66 false,
c529695e 67 UID_INVALID,
70244d1d
LP
68 &m->polkit_registry,
69 error);
70 if (r < 0)
71 return r;
72 if (r == 0)
73 return 1; /* Will call us back */
74
5d2036b5
LP
75 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
76 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
77
78 child = fork();
79 if (child < 0)
80 return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
81 if (child == 0) {
82 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
83
84 r = image_remove(image);
85 if (r < 0) {
86 (void) write(errno_pipe_fd[1], &r, sizeof(r));
87 _exit(EXIT_FAILURE);
88 }
89
90 _exit(EXIT_SUCCESS);
91 }
92
93 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
94
03c2b288 95 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
5d2036b5
LP
96 if (r < 0) {
97 (void) sigkill_wait(child);
08682124 98 return r;
5d2036b5
LP
99 }
100
101 errno_pipe_fd[0] = -1;
08682124 102
5d2036b5 103 return 1;
08682124
LP
104}
105
1ddb263d 106int bus_image_method_rename(
ebd93cb6
LP
107 sd_bus_message *message,
108 void *userdata,
109 sd_bus_error *error) {
110
1ddb263d 111 Image *image = userdata;
70244d1d 112 Manager *m = image->userdata;
ebd93cb6
LP
113 const char *new_name;
114 int r;
115
ebd93cb6 116 assert(message);
1ddb263d 117 assert(image);
ebd93cb6
LP
118
119 r = sd_bus_message_read(message, "s", &new_name);
120 if (r < 0)
121 return r;
122
123 if (!image_name_is_valid(new_name))
124 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
125
70244d1d
LP
126 r = bus_verify_polkit_async(
127 message,
128 CAP_SYS_ADMIN,
129 "org.freedesktop.machine1.manage-images",
403ed0e5 130 NULL,
70244d1d 131 false,
c529695e 132 UID_INVALID,
70244d1d
LP
133 &m->polkit_registry,
134 error);
135 if (r < 0)
136 return r;
137 if (r == 0)
138 return 1; /* Will call us back */
139
ebd93cb6
LP
140 r = image_rename(image, new_name);
141 if (r < 0)
142 return r;
143
144 return sd_bus_reply_method_return(message, NULL);
145}
146
1ddb263d 147int bus_image_method_clone(
ebd93cb6
LP
148 sd_bus_message *message,
149 void *userdata,
150 sd_bus_error *error) {
151
56599585 152 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1ddb263d 153 Image *image = userdata;
70244d1d 154 Manager *m = image->userdata;
ebd93cb6
LP
155 const char *new_name;
156 int r, read_only;
56599585 157 pid_t child;
ebd93cb6 158
ebd93cb6 159 assert(message);
1ddb263d 160 assert(image);
56599585
LP
161 assert(m);
162
163 if (m->n_operations >= OPERATIONS_MAX)
164 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
ebd93cb6
LP
165
166 r = sd_bus_message_read(message, "sb", &new_name, &read_only);
167 if (r < 0)
168 return r;
169
170 if (!image_name_is_valid(new_name))
171 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
172
70244d1d
LP
173 r = bus_verify_polkit_async(
174 message,
175 CAP_SYS_ADMIN,
176 "org.freedesktop.machine1.manage-images",
403ed0e5 177 NULL,
70244d1d 178 false,
c529695e 179 UID_INVALID,
70244d1d
LP
180 &m->polkit_registry,
181 error);
182 if (r < 0)
183 return r;
184 if (r == 0)
185 return 1; /* Will call us back */
186
56599585
LP
187 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
188 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
189
190 child = fork();
191 if (child < 0)
192 return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
193 if (child == 0) {
194 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
195
196 r = image_clone(image, new_name, read_only);
197 if (r < 0) {
198 (void) write(errno_pipe_fd[1], &r, sizeof(r));
199 _exit(EXIT_FAILURE);
200 }
201
202 _exit(EXIT_SUCCESS);
203 }
204
205 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
206
03c2b288 207 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
56599585 208 if (r < 0) {
89c9030d 209 (void) sigkill_wait(child);
ebd93cb6 210 return r;
56599585 211 }
ebd93cb6 212
56599585
LP
213 errno_pipe_fd[0] = -1;
214
215 return 1;
ebd93cb6
LP
216}
217
1ddb263d 218int bus_image_method_mark_read_only(
ebd93cb6
LP
219 sd_bus_message *message,
220 void *userdata,
221 sd_bus_error *error) {
222
1ddb263d 223 Image *image = userdata;
70244d1d 224 Manager *m = image->userdata;
ebd93cb6
LP
225 int r, read_only;
226
ebd93cb6
LP
227 assert(message);
228
ebd93cb6
LP
229 r = sd_bus_message_read(message, "b", &read_only);
230 if (r < 0)
231 return r;
232
70244d1d
LP
233 r = bus_verify_polkit_async(
234 message,
235 CAP_SYS_ADMIN,
236 "org.freedesktop.machine1.manage-images",
403ed0e5 237 NULL,
70244d1d 238 false,
c529695e 239 UID_INVALID,
70244d1d
LP
240 &m->polkit_registry,
241 error);
242 if (r < 0)
243 return r;
244 if (r == 0)
245 return 1; /* Will call us back */
246
ebd93cb6
LP
247 r = image_read_only(image, read_only);
248 if (r < 0)
249 return r;
250
251 return sd_bus_reply_method_return(message, NULL);
252}
253
d6ce17c7 254int bus_image_method_set_limit(
d6ce17c7
LP
255 sd_bus_message *message,
256 void *userdata,
257 sd_bus_error *error) {
258
259 Image *image = userdata;
260 Manager *m = image->userdata;
261 uint64_t limit;
262 int r;
263
d6ce17c7
LP
264 assert(message);
265
266 r = sd_bus_message_read(message, "t", &limit);
267 if (r < 0)
268 return r;
a90fb858
LP
269 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
270 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
d6ce17c7
LP
271
272 r = bus_verify_polkit_async(
273 message,
274 CAP_SYS_ADMIN,
275 "org.freedesktop.machine1.manage-images",
403ed0e5 276 NULL,
d6ce17c7
LP
277 false,
278 UID_INVALID,
279 &m->polkit_registry,
280 error);
281 if (r < 0)
282 return r;
283 if (r == 0)
284 return 1; /* Will call us back */
285
286 r = image_set_limit(image, limit);
287 if (r < 0)
288 return r;
289
290 return sd_bus_reply_method_return(message, NULL);
291}
292
9153b02b
LP
293#define EXIT_NOT_FOUND 2
294
295static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
296
297 _cleanup_free_ char *path = NULL;
9153b02b
LP
298 int r;
299
300 assert(image);
301 assert(ret);
302
303 r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path);
304 if (r == -ENOENT)
305 r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path);
306 if (r == -ENOENT)
307 return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
308 if (r < 0)
309 return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path);
310
311 r = load_env_file_pairs(NULL, path, NULL, ret);
312 if (r < 0)
313 return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path);
314
315 return 0;
316}
317
318static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
319 _cleanup_(rmdir_and_freep) char *t = NULL;
320 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
321 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
322 _cleanup_(sigkill_waitp) pid_t child = 0;
323 _cleanup_close_pair_ int pair[2] = { -1, -1 };
324 _cleanup_fclose_ FILE *f = NULL;
325 _cleanup_strv_free_ char **v = NULL;
326 siginfo_t si;
327 int r;
328
329 assert(image);
330 assert(ret);
331
332 r = mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t);
333 if (r < 0)
334 return sd_bus_error_set_errnof(error, r, "Failed to create temporary directory: %m");
335
336 r = loop_device_make_by_path(image->path, O_RDONLY, &d);
337 if (r < 0)
338 return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
339
e0f9e7bd 340 r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
9153b02b
LP
341 if (r == -ENOPKG)
342 return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
343 if (r < 0)
344 return sd_bus_error_set_errnof(error, r, "Failed to dissect image %s: %m", image->path);
345
346 if (pipe2(pair, O_CLOEXEC) < 0)
347 return sd_bus_error_set_errnof(error, errno, "Failed to create communication pipe: %m");
348
349 child = raw_clone(SIGCHLD|CLONE_NEWNS);
350 if (child < 0)
351 return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
352
353 if (child == 0) {
354 int fd;
355
356 pair[0] = safe_close(pair[0]);
357
358 /* Make sure we never propagate to the host */
359 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
360 _exit(EXIT_FAILURE);
361
18b5886e 362 r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
9153b02b
LP
363 if (r < 0)
364 _exit(EXIT_FAILURE);
365
366 r = mount_move_root(t);
367 if (r < 0)
368 _exit(EXIT_FAILURE);
369
370 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
371 if (fd < 0 && errno == ENOENT) {
372 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
373 if (fd < 0 && errno == ENOENT)
374 _exit(EXIT_NOT_FOUND);
375 }
376 if (fd < 0)
377 _exit(EXIT_FAILURE);
378
1c876927 379 r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
9153b02b
LP
380 if (r < 0)
381 _exit(EXIT_FAILURE);
382
383 _exit(EXIT_SUCCESS);
384 }
385
386 pair[1] = safe_close(pair[1]);
387
388 f = fdopen(pair[0], "re");
389 if (!f)
390 return -errno;
391
392 pair[0] = -1;
393
394 r = load_env_file_pairs(f, "os-release", NULL, &v);
395 if (r < 0)
396 return r;
397
398 r = wait_for_terminate(child, &si);
399 if (r < 0)
400 return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
401 child = 0;
402 if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
403 return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
404 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
405 return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
406
407 *ret = v;
408 v = NULL;
409
410 return 0;
411}
412
413int bus_image_method_get_os_release(
414 sd_bus_message *message,
415 void *userdata,
416 sd_bus_error *error) {
417
418 _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
419 _cleanup_strv_free_ char **v = NULL;
420 Image *image = userdata;
421 int r;
422
423 r = image_path_lock(image->path, LOCK_SH|LOCK_NB, &tree_global_lock, &tree_local_lock);
424 if (r < 0)
425 return sd_bus_error_set_errnof(error, r, "Failed to lock image: %m");
426
427 switch (image->type) {
428
429 case IMAGE_DIRECTORY:
430 case IMAGE_SUBVOLUME:
431 r = directory_image_get_os_release(image, &v, error);
432 break;
433
434 case IMAGE_RAW:
eb38edce 435 case IMAGE_BLOCK:
9153b02b
LP
436 r = raw_image_get_os_release(image, &v, error);
437 break;
438
439 default:
440 assert_not_reached("Unknown image type");
441 }
442 if (r < 0)
443 return r;
444
445 return bus_reply_pair_array(message, v);
446}
447
ebeccf9e
LP
448const sd_bus_vtable image_vtable[] = {
449 SD_BUS_VTABLE_START(0),
1ddb263d
LP
450 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
451 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
452 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
453 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
454 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
455 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
c19de711 456 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
1ddb263d 457 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
c19de711 458 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
1ddb263d 459 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
70244d1d
LP
460 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
461 SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
462 SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
463 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
d6ce17c7 464 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
9153b02b 465 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
ebeccf9e
LP
466 SD_BUS_VTABLE_END
467};
468
1ddb263d
LP
469static int image_flush_cache(sd_event_source *s, void *userdata) {
470 Manager *m = userdata;
471 Image *i;
472
473 assert(s);
474 assert(m);
475
476 while ((i = hashmap_steal_first(m->image_cache)))
477 image_unref(i);
478
479 return 0;
480}
481
ebeccf9e 482int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1ddb263d
LP
483 _cleanup_free_ char *e = NULL;
484 Manager *m = userdata;
485 Image *image = NULL;
486 const char *p;
ebeccf9e
LP
487 int r;
488
489 assert(bus);
490 assert(path);
491 assert(interface);
492 assert(found);
493
1ddb263d
LP
494 p = startswith(path, "/org/freedesktop/machine1/image/");
495 if (!p)
496 return 0;
497
498 e = bus_label_unescape(p);
499 if (!e)
500 return -ENOMEM;
501
502 image = hashmap_get(m->image_cache, e);
503 if (image) {
504 *found = image;
505 return 1;
506 }
507
508 r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
509 if (r < 0)
510 return r;
511
512 if (!m->image_cache_defer_event) {
513 r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
514 if (r < 0)
515 return r;
516
517 r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
518 if (r < 0)
519 return r;
520 }
521
522 r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
523 if (r < 0)
524 return r;
525
526 r = image_find(e, &image);
ebeccf9e
LP
527 if (r <= 0)
528 return r;
529
70244d1d
LP
530 image->userdata = m;
531
1ddb263d
LP
532 r = hashmap_put(m->image_cache, image->name, image);
533 if (r < 0) {
534 image_unref(image);
535 return r;
536 }
537
538 *found = image;
ebeccf9e
LP
539 return 1;
540}
541
542char *image_bus_path(const char *name) {
543 _cleanup_free_ char *e = NULL;
544
545 assert(name);
546
547 e = bus_label_escape(name);
548 if (!e)
549 return NULL;
550
551 return strappend("/org/freedesktop/machine1/image/", e);
552}
553
554int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
555 _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
556 _cleanup_strv_free_ char **l = NULL;
557 Image *image;
558 Iterator i;
559 int r;
560
561 assert(bus);
562 assert(path);
563 assert(nodes);
564
565 images = hashmap_new(&string_hash_ops);
566 if (!images)
567 return -ENOMEM;
568
569 r = image_discover(images);
570 if (r < 0)
571 return r;
572
573 HASHMAP_FOREACH(image, images, i) {
574 char *p;
575
576 p = image_bus_path(image->name);
577 if (!p)
578 return -ENOMEM;
579
580 r = strv_consume(&l, p);
581 if (r < 0)
582 return r;
583 }
584
585 *nodes = l;
586 l = NULL;
587
588 return 1;
589}