]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/portable/portabled-image-bus.c
shared: split out polkit stuff from bus-util.c → bus-polkit.c
[thirdparty/systemd.git] / src / portable / portabled-image-bus.c
CommitLineData
61d0578b
LP
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
ca78ad1d
ZJS
3#include <fcntl.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6#include <unistd.h>
7
61d0578b
LP
8#include "alloc-util.h"
9#include "bus-common-errors.h"
10#include "bus-label.h"
269e4d2d 11#include "bus-polkit.h"
61d0578b
LP
12#include "bus-util.h"
13#include "fd-util.h"
14#include "fileio.h"
15#include "io-util.h"
16#include "machine-image.h"
204f52e3 17#include "missing_capability.h"
61d0578b
LP
18#include "portable.h"
19#include "portabled-bus.h"
20#include "portabled-image-bus.h"
21#include "portabled-image.h"
22#include "portabled.h"
23#include "process-util.h"
24#include "strv.h"
25#include "user-util.h"
26
27static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
28
29int bus_image_common_get_os_release(
30 Manager *m,
31 sd_bus_message *message,
32 const char *name_or_path,
33 Image *image,
34 sd_bus_error *error) {
35
36 int r;
37
38 assert(name_or_path || image);
39 assert(message);
40
41 if (!m) {
42 assert(image);
43 m = image->userdata;
44 }
45
46 r = bus_image_acquire(m,
47 message,
48 name_or_path,
49 image,
50 BUS_IMAGE_AUTHENTICATE_BY_PATH,
51 "org.freedesktop.portable1.inspect-images",
52 &image,
53 error);
54 if (r < 0)
55 return r;
56 if (r == 0) /* Will call us back */
57 return 1;
58
59 if (!image->metadata_valid) {
60 r = image_read_metadata(image);
61 if (r < 0)
62 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
63 }
64
65 return bus_reply_pair_array(message, image->os_release);
66}
67
68static int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
69 return bus_image_common_get_os_release(NULL, message, NULL, userdata, error);
70}
71
72static int append_fd(sd_bus_message *m, PortableMetadata *d) {
73 _cleanup_fclose_ FILE *f = NULL;
74 _cleanup_free_ char *buf = NULL;
75 size_t n;
76 int r;
77
78 assert(m);
79 assert(d);
80 assert(d->fd >= 0);
81
e92aaed3 82 f = fdopen(d->fd, "r");
61d0578b
LP
83 if (!f)
84 return -errno;
85
86 d->fd = -1;
87
88 r = read_full_stream(f, &buf, &n);
89 if (r < 0)
90 return r;
91
92 return sd_bus_message_append_array(m, 'y', buf, n);
93}
94
95int bus_image_common_get_metadata(
96 Manager *m,
97 sd_bus_message *message,
98 const char *name_or_path,
99 Image *image,
100 sd_bus_error *error) {
101
102 _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
67818055 103 _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
61d0578b
LP
104 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
105 _cleanup_free_ PortableMetadata **sorted = NULL;
106 _cleanup_strv_free_ char **matches = NULL;
107 size_t i;
108 int r;
109
110 assert(name_or_path || image);
111 assert(message);
112
113 if (!m) {
114 assert(image);
115 m = image->userdata;
116 }
117
118 r = sd_bus_message_read_strv(message, &matches);
119 if (r < 0)
120 return r;
121
122 r = bus_image_acquire(m,
123 message,
124 name_or_path,
125 image,
126 BUS_IMAGE_AUTHENTICATE_BY_PATH,
127 "org.freedesktop.portable1.inspect-images",
128 &image,
129 error);
130 if (r < 0)
131 return r;
132 if (r == 0) /* Will call us back */
133 return 1;
134
135 r = portable_extract(
136 image->path,
137 matches,
138 &os_release,
139 &unit_files,
140 error);
141 if (r < 0)
142 return r;
143
144 r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
145 if (r < 0)
146 return r;
147
148 r = sd_bus_message_new_method_return(message, &reply);
149 if (r < 0)
150 return r;
151
152 r = sd_bus_message_append(reply, "s", image->path);
153 if (r < 0)
154 return r;
155
156 r = append_fd(reply, os_release);
157 if (r < 0)
158 return r;
159
160 r = sd_bus_message_open_container(reply, 'a', "{say}");
161 if (r < 0)
162 return r;
163
164 for (i = 0; i < hashmap_size(unit_files); i++) {
165
61d0578b
LP
166 r = sd_bus_message_open_container(reply, 'e', "say");
167 if (r < 0)
168 return r;
169
170 r = sd_bus_message_append(reply, "s", sorted[i]->name);
171 if (r < 0)
172 return r;
173
174 r = append_fd(reply, sorted[i]);
175 if (r < 0)
176 return r;
177
178 r = sd_bus_message_close_container(reply);
179 if (r < 0)
180 return r;
181 }
182
183 r = sd_bus_message_close_container(reply);
184 if (r < 0)
185 return r;
186
187 return sd_bus_send(NULL, reply, NULL);
188}
189
190static int bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
191 return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
192}
193
194static int bus_image_method_get_state(
195 sd_bus_message *message,
196 void *userdata,
197 sd_bus_error *error) {
198
199 Image *image = userdata;
200 PortableState state;
201 int r;
202
203 assert(message);
204 assert(image);
205
206 r = portable_get_state(
207 sd_bus_message_get_bus(message),
208 image->path,
209 0,
210 &state,
211 error);
212 if (r < 0)
213 return r;
214
215 return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
216}
217
218int bus_image_common_attach(
219 Manager *m,
220 sd_bus_message *message,
221 const char *name_or_path,
222 Image *image,
223 sd_bus_error *error) {
224
c5a935b0 225 _cleanup_strv_free_ char **matches = NULL;
61d0578b
LP
226 PortableChange *changes = NULL;
227 PortableFlags flags = 0;
c5a935b0 228 const char *profile, *copy_mode;
61d0578b 229 size_t n_changes = 0;
61d0578b
LP
230 int runtime, r;
231
232 assert(message);
233 assert(name_or_path || image);
234
235 if (!m) {
236 assert(image);
237 m = image->userdata;
238 }
239
240 r = sd_bus_message_read_strv(message, &matches);
241 if (r < 0)
242 return r;
243
244 r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
245 if (r < 0)
246 return r;
247
248 if (streq(copy_mode, "symlink"))
249 flags |= PORTABLE_PREFER_SYMLINK;
250 else if (streq(copy_mode, "copy"))
251 flags |= PORTABLE_PREFER_COPY;
252 else if (!isempty(copy_mode))
253 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
254
255 if (runtime)
256 flags |= PORTABLE_RUNTIME;
257
258 r = bus_image_acquire(m,
259 message,
260 name_or_path,
261 image,
262 BUS_IMAGE_AUTHENTICATE_ALL,
263 "org.freedesktop.portable1.attach-images",
264 &image,
265 error);
266 if (r < 0)
267 return r;
268 if (r == 0) /* Will call us back */
269 return 1;
270
271 r = portable_attach(
272 sd_bus_message_get_bus(message),
273 image->path,
274 matches,
275 profile,
276 flags,
277 &changes,
278 &n_changes,
279 error);
280 if (r < 0)
281 goto finish;
282
283 r = reply_portable_changes(message, changes, n_changes);
284
285finish:
286 portable_changes_free(changes, n_changes);
287 return r;
288}
289
290static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
291 return bus_image_common_attach(NULL, message, NULL, userdata, error);
292}
293
294static int bus_image_method_detach(
295 sd_bus_message *message,
296 void *userdata,
297 sd_bus_error *error) {
298
299 PortableChange *changes = NULL;
300 Image *image = userdata;
301 Manager *m = image->userdata;
302 size_t n_changes = 0;
303 int r, runtime;
304
305 assert(message);
306 assert(image);
307 assert(m);
308
309 r = sd_bus_message_read(message, "b", &runtime);
310 if (r < 0)
311 return r;
312
313 r = bus_verify_polkit_async(
314 message,
315 CAP_SYS_ADMIN,
316 "org.freedesktop.portable1.attach-images",
317 NULL,
318 false,
319 UID_INVALID,
320 &m->polkit_registry,
321 error);
322 if (r < 0)
323 return r;
324 if (r == 0)
325 return 1; /* Will call us back */
326
327 r = portable_detach(
328 sd_bus_message_get_bus(message),
329 image->path,
330 runtime ? PORTABLE_RUNTIME : 0,
331 &changes,
332 &n_changes,
333 error);
334 if (r < 0)
335 goto finish;
336
337 r = reply_portable_changes(message, changes, n_changes);
338
339finish:
340 portable_changes_free(changes, n_changes);
341 return r;
342}
343
344int bus_image_common_remove(
345 Manager *m,
346 sd_bus_message *message,
347 const char *name_or_path,
348 Image *image,
349 sd_bus_error *error) {
350
351 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
352 _cleanup_(sigkill_waitp) pid_t child = 0;
353 PortableState state;
354 int r;
355
356 assert(message);
357 assert(name_or_path || image);
358
359 if (!m) {
360 assert(image);
361 m = image->userdata;
362 }
363
364 if (m->n_operations >= OPERATIONS_MAX)
365 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
366
367 r = bus_image_acquire(m,
368 message,
369 name_or_path,
370 image,
371 BUS_IMAGE_AUTHENTICATE_ALL,
372 "org.freedesktop.portable1.manage-images",
373 &image,
374 error);
375 if (r < 0)
376 return r;
377 if (r == 0)
378 return 1; /* Will call us back */
379
380 r = portable_get_state(
381 sd_bus_message_get_bus(message),
382 image->path,
383 0,
384 &state,
385 error);
386 if (r < 0)
387 return r;
388
389 if (state != PORTABLE_DETACHED)
390 return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
391
392 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
393 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
394
395 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
396 if (r < 0)
397 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
398 if (r == 0) {
399 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
400
401 r = image_remove(image);
402 if (r < 0) {
403 (void) write(errno_pipe_fd[1], &r, sizeof(r));
404 _exit(EXIT_FAILURE);
405 }
406
407 _exit(EXIT_SUCCESS);
408 }
409
410 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
411
412 r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
413 if (r < 0)
414 return r;
415
416 child = 0;
417 errno_pipe_fd[0] = -1;
418
419 return 1;
420}
421
422static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
423 return bus_image_common_remove(NULL, message, NULL, userdata, error);
424}
425
426int bus_image_common_mark_read_only(
427 Manager *m,
428 sd_bus_message *message,
429 const char *name_or_path,
430 Image *image,
431 sd_bus_error *error) {
432
433 int r, read_only;
434
435 assert(message);
436 assert(name_or_path || image);
437
438 if (!m) {
439 assert(image);
440 m = image->userdata;
441 }
442
443 r = sd_bus_message_read(message, "b", &read_only);
444 if (r < 0)
445 return r;
446
447 r = bus_image_acquire(m,
448 message,
449 name_or_path,
450 image,
451 BUS_IMAGE_AUTHENTICATE_ALL,
452 "org.freedesktop.portable1.manage-images",
453 &image,
454 error);
455 if (r < 0)
456 return r;
457 if (r == 0)
458 return 1; /* Will call us back */
459
460 r = image_read_only(image, read_only);
461 if (r < 0)
462 return r;
463
464 return sd_bus_reply_method_return(message, NULL);
465}
466
467static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
468 return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
469}
470
471int bus_image_common_set_limit(
472 Manager *m,
473 sd_bus_message *message,
474 const char *name_or_path,
475 Image *image,
476 sd_bus_error *error) {
477
478 uint64_t limit;
479 int r;
480
481 assert(message);
482 assert(name_or_path || image);
483
484 if (!m) {
485 assert(image);
486 m = image->userdata;
487 }
488
489 r = sd_bus_message_read(message, "t", &limit);
490 if (r < 0)
491 return r;
492 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
493 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
494
495 r = bus_image_acquire(m,
496 message,
497 name_or_path,
498 image,
499 BUS_IMAGE_AUTHENTICATE_ALL,
500 "org.freedesktop.portable1.manage-images",
501 &image,
502 error);
503 if (r < 0)
504 return r;
505 if (r == 0)
506 return 1; /* Will call us back */
507
508 r = image_set_limit(image, limit);
509 if (r < 0)
510 return r;
511
512 return sd_bus_reply_method_return(message, NULL);
513}
514
515static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
516 return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
517}
518
519const sd_bus_vtable image_vtable[] = {
520 SD_BUS_VTABLE_START(0),
521 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
522 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
523 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
524 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
525 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
526 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
527 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
528 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
529 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
530 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
531 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
70f7b85a 532 SD_BUS_METHOD("GetMetadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
61d0578b
LP
533 SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED),
534 SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED),
535 SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED),
536 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
537 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
538 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
539 SD_BUS_VTABLE_END
540};
541
542int bus_image_path(Image *image, char **ret) {
543 assert(image);
544 assert(ret);
545
546 if (!image->discoverable)
547 return -EINVAL;
548
549 return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
550}
551
552int bus_image_acquire(
553 Manager *m,
554 sd_bus_message *message,
555 const char *name_or_path,
556 Image *image,
557 ImageAcquireMode mode,
558 const char *polkit_action,
559 Image **ret,
560 sd_bus_error *error) {
561
562 _cleanup_(image_unrefp) Image *loaded = NULL;
563 Image *cached;
564 int r;
565
566 assert(m);
567 assert(message);
568 assert(name_or_path || image);
569 assert(mode >= 0);
570 assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
571 assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
572 assert(ret);
573
574 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
575
576 if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
577 r = bus_verify_polkit_async(
578 message,
579 CAP_SYS_ADMIN,
580 polkit_action,
581 NULL,
582 false,
583 UID_INVALID,
584 &m->polkit_registry,
585 error);
586 if (r < 0)
587 return r;
588 if (r == 0) { /* Will call us back */
589 *ret = NULL;
590 return 0;
591 }
592 }
593
594 /* Already passed in? */
595 if (image) {
596 *ret = image;
597 return 1;
598 }
599
600 /* Let's see if this image is already cached? */
601 cached = manager_image_cache_get(m, name_or_path);
602 if (cached) {
603 *ret = cached;
604 return 1;
605 }
606
607 if (image_name_is_valid(name_or_path)) {
608
609 /* If it's a short name, let's search for it */
610 r = image_find(IMAGE_PORTABLE, name_or_path, &loaded);
611 if (r == -ENOENT)
612 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
613
614 /* other errors are handled below… */
615 } else {
616 /* Don't accept path if this is always forbidden */
617 if (mode == BUS_IMAGE_REFUSE_BY_PATH)
618 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path);
619
620 if (!path_is_absolute(name_or_path))
621 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is not valid or not a valid path.", name_or_path);
622
623 if (!path_is_normalized(name_or_path))
624 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path);
625
626 if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
627 r = bus_verify_polkit_async(
628 message,
629 CAP_SYS_ADMIN,
630 polkit_action,
631 NULL,
632 false,
633 UID_INVALID,
634 &m->polkit_registry,
635 error);
636 if (r < 0)
637 return r;
638 if (r == 0) { /* Will call us back */
639 *ret = NULL;
640 return 0;
641 }
642 }
643
644 r = image_from_path(name_or_path, &loaded);
645 }
787dfb82
LP
646 if (r == -EMEDIUMTYPE) {
647 sd_bus_error_setf(error, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, "Typ of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.", name_or_path);
648 return r;
649 }
61d0578b
LP
650 if (r < 0)
651 return r;
652
653 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
654 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
655 * means we don't actually need to ref the return object. */
656 r = manager_image_cache_add(m, loaded);
657 if (r < 0)
658 return r;
659
660 *ret = loaded;
661 return 1;
662}
663
664int bus_image_object_find(
665 sd_bus *bus,
666 const char *path,
667 const char *interface,
668 void *userdata,
669 void **found,
670 sd_bus_error *error) {
671
672 _cleanup_free_ char *e = NULL;
673 Manager *m = userdata;
674 Image *image = NULL;
675 int r;
676
677 assert(bus);
678 assert(path);
679 assert(interface);
680 assert(found);
681
682 r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
683 if (r < 0)
684 return 0;
685 if (r == 0)
686 goto not_found;
687
688 r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
689 if (r == -ENOENT)
690 goto not_found;
691 if (r < 0)
692 return r;
693
694 *found = image;
695 return 1;
696
697not_found:
698 *found = NULL;
699 return 0;
700}
701
702int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
b07ec5a1 703 _cleanup_hashmap_free_ Hashmap *images = NULL;
61d0578b
LP
704 _cleanup_strv_free_ char **l = NULL;
705 size_t n_allocated = 0, n = 0;
706 Manager *m = userdata;
707 Image *image;
708 Iterator i;
709 int r;
710
711 assert(bus);
712 assert(path);
713 assert(nodes);
714
b07ec5a1 715 images = hashmap_new(&image_hash_ops);
61d0578b
LP
716 if (!images)
717 return -ENOMEM;
718
719 r = manager_image_cache_discover(m, images, error);
720 if (r < 0)
721 return r;
722
723 HASHMAP_FOREACH(image, images, i) {
724 char *p;
725
726 r = bus_image_path(image, &p);
727 if (r < 0)
728 return r;
729
730 if (!GREEDY_REALLOC(l, n_allocated, n+2)) {
731 free(p);
732 return -ENOMEM;
733 }
734
735 l[n++] = p;
736 l[n] = NULL;
737 }
738
739 *nodes = TAKE_PTR(l);
740
741 return 1;
742}