1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
10 #include "bus-label.h"
11 #include "bus-polkit.h"
16 #include "machine-image.h"
17 #include "missing_capability.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"
25 #include "user-util.h"
27 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
29 int bus_image_common_get_os_release(
31 sd_bus_message
*message
,
32 const char *name_or_path
,
34 sd_bus_error
*error
) {
38 assert(name_or_path
|| image
);
46 r
= bus_image_acquire(m
,
50 BUS_IMAGE_AUTHENTICATE_BY_PATH
,
51 "org.freedesktop.portable1.inspect-images",
56 if (r
== 0) /* Will call us back */
59 if (!image
->metadata_valid
) {
60 r
= image_read_metadata(image
);
62 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
65 return bus_reply_pair_array(message
, image
->os_release
);
68 static 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
);
72 static int append_fd(sd_bus_message
*m
, PortableMetadata
*d
) {
73 _cleanup_fclose_
FILE *f
= NULL
;
74 _cleanup_free_
char *buf
= NULL
;
82 f
= fdopen(d
->fd
, "r");
88 r
= read_full_stream(f
, &buf
, &n
);
92 return sd_bus_message_append_array(m
, 'y', buf
, n
);
95 int bus_image_common_get_metadata(
97 sd_bus_message
*message
,
98 const char *name_or_path
,
100 sd_bus_error
*error
) {
102 _cleanup_(portable_metadata_unrefp
) PortableMetadata
*os_release
= NULL
;
103 _cleanup_hashmap_free_ Hashmap
*unit_files
= NULL
;
104 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
105 _cleanup_free_ PortableMetadata
**sorted
= NULL
;
106 _cleanup_strv_free_
char **matches
= NULL
;
110 assert(name_or_path
|| image
);
118 r
= sd_bus_message_read_strv(message
, &matches
);
122 r
= bus_image_acquire(m
,
126 BUS_IMAGE_AUTHENTICATE_BY_PATH
,
127 "org.freedesktop.portable1.inspect-images",
132 if (r
== 0) /* Will call us back */
135 r
= portable_extract(
144 r
= portable_metadata_hashmap_to_sorted_array(unit_files
, &sorted
);
148 r
= sd_bus_message_new_method_return(message
, &reply
);
152 r
= sd_bus_message_append(reply
, "s", image
->path
);
156 r
= append_fd(reply
, os_release
);
160 r
= sd_bus_message_open_container(reply
, 'a', "{say}");
164 for (i
= 0; i
< hashmap_size(unit_files
); i
++) {
166 r
= sd_bus_message_open_container(reply
, 'e', "say");
170 r
= sd_bus_message_append(reply
, "s", sorted
[i
]->name
);
174 r
= append_fd(reply
, sorted
[i
]);
178 r
= sd_bus_message_close_container(reply
);
183 r
= sd_bus_message_close_container(reply
);
187 return sd_bus_send(NULL
, reply
, NULL
);
190 static 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
);
194 static int bus_image_method_get_state(
195 sd_bus_message
*message
,
197 sd_bus_error
*error
) {
199 Image
*image
= userdata
;
206 r
= portable_get_state(
207 sd_bus_message_get_bus(message
),
215 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
218 int bus_image_common_attach(
220 sd_bus_message
*message
,
221 const char *name_or_path
,
223 sd_bus_error
*error
) {
225 _cleanup_strv_free_
char **matches
= NULL
;
226 PortableChange
*changes
= NULL
;
227 PortableFlags flags
= 0;
228 const char *profile
, *copy_mode
;
229 size_t n_changes
= 0;
233 assert(name_or_path
|| image
);
240 r
= sd_bus_message_read_strv(message
, &matches
);
244 r
= sd_bus_message_read(message
, "sbs", &profile
, &runtime
, ©_mode
);
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
);
256 flags
|= PORTABLE_RUNTIME
;
258 r
= bus_image_acquire(m
,
262 BUS_IMAGE_AUTHENTICATE_ALL
,
263 "org.freedesktop.portable1.attach-images",
268 if (r
== 0) /* Will call us back */
272 sd_bus_message_get_bus(message
),
283 r
= reply_portable_changes(message
, changes
, n_changes
);
286 portable_changes_free(changes
, n_changes
);
290 static 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
);
294 static int bus_image_method_detach(
295 sd_bus_message
*message
,
297 sd_bus_error
*error
) {
299 PortableChange
*changes
= NULL
;
300 Image
*image
= userdata
;
301 Manager
*m
= image
->userdata
;
302 size_t n_changes
= 0;
309 r
= sd_bus_message_read(message
, "b", &runtime
);
313 r
= bus_verify_polkit_async(
316 "org.freedesktop.portable1.attach-images",
325 return 1; /* Will call us back */
328 sd_bus_message_get_bus(message
),
330 runtime
? PORTABLE_RUNTIME
: 0,
337 r
= reply_portable_changes(message
, changes
, n_changes
);
340 portable_changes_free(changes
, n_changes
);
344 int bus_image_common_remove(
346 sd_bus_message
*message
,
347 const char *name_or_path
,
349 sd_bus_error
*error
) {
351 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
352 _cleanup_(sigkill_waitp
) pid_t child
= 0;
357 assert(name_or_path
|| image
);
364 if (m
->n_operations
>= OPERATIONS_MAX
)
365 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
367 r
= bus_image_acquire(m
,
371 BUS_IMAGE_AUTHENTICATE_ALL
,
372 "org.freedesktop.portable1.manage-images",
378 return 1; /* Will call us back */
380 r
= portable_get_state(
381 sd_bus_message_get_bus(message
),
389 if (state
!= PORTABLE_DETACHED
)
390 return sd_bus_error_set_errnof(error
, EBUSY
, "Image '%s' is not detached, refusing.", image
->path
);
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");
395 r
= safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS
, &child
);
397 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
399 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
401 r
= image_remove(image
);
403 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
410 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
412 r
= operation_new(m
, child
, message
, errno_pipe_fd
[0], NULL
);
417 errno_pipe_fd
[0] = -1;
422 static 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
);
426 int bus_image_common_mark_read_only(
428 sd_bus_message
*message
,
429 const char *name_or_path
,
431 sd_bus_error
*error
) {
436 assert(name_or_path
|| image
);
443 r
= sd_bus_message_read(message
, "b", &read_only
);
447 r
= bus_image_acquire(m
,
451 BUS_IMAGE_AUTHENTICATE_ALL
,
452 "org.freedesktop.portable1.manage-images",
458 return 1; /* Will call us back */
460 r
= image_read_only(image
, read_only
);
464 return sd_bus_reply_method_return(message
, NULL
);
467 static 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
);
471 int bus_image_common_set_limit(
473 sd_bus_message
*message
,
474 const char *name_or_path
,
476 sd_bus_error
*error
) {
482 assert(name_or_path
|| image
);
489 r
= sd_bus_message_read(message
, "t", &limit
);
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");
495 r
= bus_image_acquire(m
,
499 BUS_IMAGE_AUTHENTICATE_ALL
,
500 "org.freedesktop.portable1.manage-images",
506 return 1; /* Will call us back */
508 r
= image_set_limit(image
, limit
);
512 return sd_bus_reply_method_return(message
, NULL
);
515 static 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
);
519 const 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
),
532 SD_BUS_METHOD("GetMetadata", "as", "saya{say}", bus_image_method_get_metadata
, SD_BUS_VTABLE_UNPRIVILEGED
),
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
),
542 int bus_image_path(Image
*image
, char **ret
) {
546 if (!image
->discoverable
)
549 return sd_bus_path_encode("/org/freedesktop/portable1/image", image
->name
, ret
);
552 int bus_image_acquire(
554 sd_bus_message
*message
,
555 const char *name_or_path
,
557 ImageAcquireMode mode
,
558 const char *polkit_action
,
560 sd_bus_error
*error
) {
562 _cleanup_(image_unrefp
) Image
*loaded
= NULL
;
568 assert(name_or_path
|| image
);
570 assert(mode
< _BUS_IMAGE_ACQUIRE_MODE_MAX
);
571 assert(polkit_action
|| mode
== BUS_IMAGE_REFUSE_BY_PATH
);
574 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
576 if (mode
== BUS_IMAGE_AUTHENTICATE_ALL
) {
577 r
= bus_verify_polkit_async(
588 if (r
== 0) { /* Will call us back */
594 /* Already passed in? */
600 /* Let's see if this image is already cached? */
601 cached
= manager_image_cache_get(m
, name_or_path
);
607 if (image_name_is_valid(name_or_path
)) {
609 /* If it's a short name, let's search for it */
610 r
= image_find(IMAGE_PORTABLE
, name_or_path
, &loaded
);
612 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE
, "No image '%s' found.", name_or_path
);
614 /* other errors are handled below… */
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
);
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
);
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
);
626 if (mode
== BUS_IMAGE_AUTHENTICATE_BY_PATH
) {
627 r
= bus_verify_polkit_async(
638 if (r
== 0) { /* Will call us back */
644 r
= image_from_path(name_or_path
, &loaded
);
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
);
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
);
664 int bus_image_object_find(
667 const char *interface
,
670 sd_bus_error
*error
) {
672 _cleanup_free_
char *e
= NULL
;
673 Manager
*m
= userdata
;
682 r
= sd_bus_path_decode(path
, "/org/freedesktop/portable1/image", &e
);
688 r
= bus_image_acquire(m
, sd_bus_get_current_message(bus
), e
, NULL
, BUS_IMAGE_REFUSE_BY_PATH
, NULL
, &image
, error
);
702 int bus_image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
703 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
704 _cleanup_strv_free_
char **l
= NULL
;
705 size_t n_allocated
= 0, n
= 0;
706 Manager
*m
= userdata
;
715 images
= hashmap_new(&image_hash_ops
);
719 r
= manager_image_cache_discover(m
, images
, error
);
723 HASHMAP_FOREACH(image
, images
, i
) {
726 r
= bus_image_path(image
, &p
);
730 if (!GREEDY_REALLOC(l
, n_allocated
, n
+2)) {
739 *nodes
= TAKE_PTR(l
);