1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
10 #include "bus-get-properties.h"
11 #include "bus-object.h"
12 #include "bus-polkit.h"
14 #include "discover-image.h"
18 #include "image-policy.h"
21 #include "path-util.h"
23 #include "portabled.h"
24 #include "portabled-bus.h"
25 #include "portabled-image.h"
26 #include "portabled-image-bus.h"
27 #include "portabled-operation.h"
28 #include "process-util.h"
31 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
33 int bus_image_common_get_os_release(
35 sd_bus_message
*message
,
36 const char *name_or_path
,
38 sd_bus_error
*error
) {
42 assert(name_or_path
|| image
);
50 r
= bus_image_acquire(m
,
54 BUS_IMAGE_AUTHENTICATE_BY_PATH
,
55 "org.freedesktop.portable1.inspect-images",
60 if (r
== 0) /* Will call us back */
63 if (!image
->metadata_valid
) {
64 r
= image_read_metadata(image
, &image_policy_service
);
66 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
69 return bus_reply_pair_array(message
, image
->os_release
);
72 static int bus_image_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
73 return bus_image_common_get_os_release(NULL
, message
, NULL
, userdata
, error
);
76 static int append_fd(sd_bus_message
*m
, PortableMetadata
*d
) {
77 _cleanup_fclose_
FILE *f
= NULL
;
78 _cleanup_free_
char *buf
= NULL
;
87 r
= fdopen_independent(d
->fd
, "r", &f
);
91 r
= read_full_stream(f
, &buf
, &n
);
96 return sd_bus_message_append_array(m
, 'y', buf
, n
);
99 int bus_image_common_get_metadata(
101 sd_bus_message
*message
,
102 const char *name_or_path
,
104 sd_bus_error
*error
) {
106 _cleanup_ordered_hashmap_free_ OrderedHashmap
*extension_releases
= NULL
;
107 _cleanup_(portable_metadata_unrefp
) PortableMetadata
*os_release
= NULL
;
108 _cleanup_strv_free_
char **matches
= NULL
, **extension_images
= NULL
;
109 _cleanup_hashmap_free_ Hashmap
*unit_files
= NULL
;
110 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
111 _cleanup_free_ PortableMetadata
**sorted
= NULL
;
112 PortableFlags flags
= 0;
115 assert(name_or_path
|| image
);
119 m
= ASSERT_PTR(ASSERT_PTR(image
)->userdata
);
121 bool have_exti
= sd_bus_message_is_method_call(message
, NULL
, "GetImageMetadataWithExtensions") ||
122 sd_bus_message_is_method_call(message
, NULL
, "GetMetadataWithExtensions");
125 r
= sd_bus_message_read_strv(message
, &extension_images
);
130 r
= sd_bus_message_read_strv(message
, &matches
);
135 uint64_t input_flags
= 0;
137 r
= sd_bus_message_read(message
, "t", &input_flags
);
141 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
142 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
143 "Invalid 'flags' parameter '%" PRIu64
"'",
145 flags
|= input_flags
;
148 r
= bus_image_acquire(m
,
152 BUS_IMAGE_AUTHENTICATE_BY_PATH
,
153 "org.freedesktop.portable1.inspect-images",
158 if (r
== 0) /* Will call us back */
161 r
= portable_extract(
166 /* image_policy= */ NULL
,
176 r
= portable_metadata_hashmap_to_sorted_array(unit_files
, &sorted
);
180 r
= sd_bus_message_new_method_return(message
, &reply
);
184 r
= sd_bus_message_append(reply
, "s", image
->path
);
188 r
= append_fd(reply
, os_release
);
192 /* If it was requested, also send back the extension path and the content
193 * of each extension-release file. Behind a flag, as it's an incompatible
196 PortableMetadata
*extension_release
;
198 r
= sd_bus_message_open_container(reply
, 'a', "{say}");
202 ORDERED_HASHMAP_FOREACH(extension_release
, extension_releases
) {
204 r
= sd_bus_message_open_container(reply
, 'e', "say");
208 r
= sd_bus_message_append(reply
, "s", extension_release
->image_path
);
212 r
= append_fd(reply
, extension_release
);
216 r
= sd_bus_message_close_container(reply
);
221 r
= sd_bus_message_close_container(reply
);
226 r
= sd_bus_message_open_container(reply
, 'a', "{say}");
230 for (size_t i
= 0; i
< hashmap_size(unit_files
); i
++) {
232 r
= sd_bus_message_open_container(reply
, 'e', "say");
236 r
= sd_bus_message_append(reply
, "s", sorted
[i
]->name
);
240 r
= append_fd(reply
, sorted
[i
]);
244 r
= sd_bus_message_close_container(reply
);
249 r
= sd_bus_message_close_container(reply
);
253 return sd_bus_message_send(reply
);
256 static int bus_image_method_get_metadata(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
257 return bus_image_common_get_metadata(NULL
, message
, NULL
, userdata
, error
);
260 static int bus_image_method_get_state(
261 sd_bus_message
*message
,
263 sd_bus_error
*error
) {
265 _cleanup_strv_free_
char **extension_images
= NULL
;
266 Image
*image
= ASSERT_PTR(userdata
);
267 Manager
*m
= ASSERT_PTR(image
->userdata
);
273 if (sd_bus_message_is_method_call(message
, NULL
, "GetStateWithExtensions")) {
274 uint64_t input_flags
= 0;
276 r
= sd_bus_message_read_strv(message
, &extension_images
);
280 r
= sd_bus_message_read(message
, "t", &input_flags
);
284 /* No flags are supported by this method for now. */
285 if (input_flags
!= 0)
286 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
287 "Invalid 'flags' parameter '%" PRIu64
"'",
291 r
= portable_get_state(
293 sd_bus_message_get_bus(message
),
302 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
305 int bus_image_common_attach(
307 sd_bus_message
*message
,
308 const char *name_or_path
,
310 sd_bus_error
*error
) {
312 _cleanup_strv_free_
char **matches
= NULL
, **extension_images
= NULL
;
313 PortableChange
*changes
= NULL
;
314 PortableFlags flags
= 0;
315 const char *profile
, *copy_mode
;
316 size_t n_changes
= 0;
320 assert(name_or_path
|| image
);
322 CLEANUP_ARRAY(changes
, n_changes
, portable_changes_free
);
329 if (sd_bus_message_is_method_call(message
, NULL
, "AttachImageWithExtensions") ||
330 sd_bus_message_is_method_call(message
, NULL
, "AttachWithExtensions")) {
331 r
= sd_bus_message_read_strv(message
, &extension_images
);
336 r
= sd_bus_message_read_strv(message
, &matches
);
340 r
= sd_bus_message_read(message
, "s", &profile
);
344 if (sd_bus_message_is_method_call(message
, NULL
, "AttachImageWithExtensions") ||
345 sd_bus_message_is_method_call(message
, NULL
, "AttachWithExtensions")) {
346 uint64_t input_flags
= 0;
348 r
= sd_bus_message_read(message
, "st", ©_mode
, &input_flags
);
351 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
352 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
353 "Invalid 'flags' parameter '%" PRIu64
"'",
355 flags
|= input_flags
;
359 r
= sd_bus_message_read(message
, "bs", &runtime
, ©_mode
);
364 flags
|= PORTABLE_RUNTIME
;
367 if (streq(copy_mode
, "symlink"))
368 flags
|= PORTABLE_PREFER_SYMLINK
;
369 else if (streq(copy_mode
, "copy"))
370 flags
|= PORTABLE_PREFER_COPY
;
371 else if (streq(copy_mode
, "mixed"))
372 flags
|= PORTABLE_MIXED_COPY_LINK
;
373 else if (!isempty(copy_mode
))
374 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown copy mode '%s'", copy_mode
);
376 r
= bus_image_acquire(m
,
380 BUS_IMAGE_AUTHENTICATE_ALL
,
381 "org.freedesktop.portable1.attach-images",
386 if (r
== 0) /* Will call us back */
391 sd_bus_message_get_bus(message
),
396 /* image_policy= */ NULL
,
404 return reply_portable_changes(message
, changes
, n_changes
);
407 static int bus_image_method_attach(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
408 return bus_image_common_attach(NULL
, message
, NULL
, userdata
, error
);
411 static int bus_image_method_detach(
412 sd_bus_message
*message
,
414 sd_bus_error
*error
) {
416 _cleanup_strv_free_
char **extension_images
= NULL
;
417 PortableChange
*changes
= NULL
;
418 Image
*image
= ASSERT_PTR(userdata
);
419 Manager
*m
= ASSERT_PTR(image
->userdata
);
420 PortableFlags flags
= 0;
421 size_t n_changes
= 0;
426 CLEANUP_ARRAY(changes
, n_changes
, portable_changes_free
);
428 if (sd_bus_message_is_method_call(message
, NULL
, "DetachWithExtensions")) {
429 r
= sd_bus_message_read_strv(message
, &extension_images
);
434 if (sd_bus_message_is_method_call(message
, NULL
, "DetachWithExtensions")) {
435 uint64_t input_flags
= 0;
437 r
= sd_bus_message_read(message
, "t", &input_flags
);
441 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
442 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
443 "Invalid 'flags' parameter '%" PRIu64
"'",
445 flags
|= input_flags
;
449 r
= sd_bus_message_read(message
, "b", &runtime
);
454 flags
|= PORTABLE_RUNTIME
;
457 r
= bus_verify_polkit_async(
459 "org.freedesktop.portable1.attach-images",
466 return 1; /* Will call us back */
470 sd_bus_message_get_bus(message
),
480 return reply_portable_changes(message
, changes
, n_changes
);
483 int bus_image_common_remove(
485 sd_bus_message
*message
,
486 const char *name_or_path
,
488 sd_bus_error
*error
) {
490 _cleanup_close_pair_
int errno_pipe_fd
[2] = EBADF_PAIR
;
491 _cleanup_(sigkill_waitp
) pid_t child
= 0;
496 assert(name_or_path
|| image
);
503 if (m
->n_operations
>= OPERATIONS_MAX
)
504 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
506 r
= bus_image_acquire(m
,
510 BUS_IMAGE_AUTHENTICATE_ALL
,
511 "org.freedesktop.portable1.manage-images",
517 return 1; /* Will call us back */
519 r
= portable_get_state(
521 sd_bus_message_get_bus(message
),
530 if (state
!= PORTABLE_DETACHED
)
531 return sd_bus_error_set_errnof(error
, EBUSY
, "Image '%s' is not detached, refusing.", image
->path
);
533 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
534 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
536 r
= safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS
, &child
);
538 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
540 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
542 r
= image_remove(image
);
544 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
551 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
553 r
= operation_new(m
, child
, message
, errno_pipe_fd
[0], NULL
);
558 errno_pipe_fd
[0] = -EBADF
;
563 static int bus_image_method_remove(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
564 return bus_image_common_remove(NULL
, message
, NULL
, userdata
, error
);
567 /* Given two PortableChange arrays, return a new array that has all elements of the first that are
568 * not also present in the second, comparing the basename of the path values. */
569 static int normalize_portable_changes(
570 const PortableChange
*changes_attached
,
571 size_t n_changes_attached
,
572 const PortableChange
*changes_detached
,
573 size_t n_changes_detached
,
574 PortableChange
**ret_changes
,
575 size_t *ret_n_changes
) {
577 PortableChange
*changes
= NULL
;
578 size_t n_changes
= 0;
580 assert(ret_n_changes
);
583 if (n_changes_detached
== 0)
584 return 0; /* Nothing to do */
586 changes
= new0(PortableChange
, n_changes_attached
+ n_changes_detached
);
590 CLEANUP_ARRAY(changes
, n_changes
, portable_changes_free
);
592 /* Corner case: only detached, nothing attached */
593 if (n_changes_attached
== 0) {
594 memcpy(changes
, changes_detached
, sizeof(PortableChange
) * n_changes_detached
);
595 *ret_changes
= TAKE_PTR(changes
);
596 *ret_n_changes
= n_changes_detached
;
600 for (size_t i
= 0; i
< n_changes_detached
; ++i
) {
603 for (size_t j
= 0; j
< n_changes_attached
; ++j
)
604 if (path_equal_filename(changes_detached
[i
].path
, changes_attached
[j
].path
)) {
610 _cleanup_free_
char *path
= NULL
, *source
= NULL
;
612 path
= strdup(changes_detached
[i
].path
);
616 if (changes_detached
[i
].source
) {
617 source
= strdup(changes_detached
[i
].source
);
622 changes
[n_changes
++] = (PortableChange
) {
623 .type_or_errno
= changes_detached
[i
].type_or_errno
,
624 .path
= TAKE_PTR(path
),
625 .source
= TAKE_PTR(source
),
630 *ret_n_changes
= n_changes
;
631 *ret_changes
= TAKE_PTR(changes
);
636 int bus_image_common_reattach(
638 sd_bus_message
*message
,
639 const char *name_or_path
,
641 sd_bus_error
*error
) {
643 PortableChange
*changes_detached
= NULL
, *changes_attached
= NULL
, *changes_gone
= NULL
;
644 size_t n_changes_detached
= 0, n_changes_attached
= 0, n_changes_gone
= 0;
645 _cleanup_strv_free_
char **matches
= NULL
, **extension_images
= NULL
;
646 PortableFlags flags
= PORTABLE_REATTACH
;
647 const char *profile
, *copy_mode
;
651 assert(name_or_path
|| image
);
653 CLEANUP_ARRAY(changes_detached
, n_changes_detached
, portable_changes_free
);
654 CLEANUP_ARRAY(changes_attached
, n_changes_attached
, portable_changes_free
);
655 CLEANUP_ARRAY(changes_gone
, n_changes_gone
, portable_changes_free
);
662 if (sd_bus_message_is_method_call(message
, NULL
, "ReattachImageWithExtensions") ||
663 sd_bus_message_is_method_call(message
, NULL
, "ReattachWithExtensions")) {
664 r
= sd_bus_message_read_strv(message
, &extension_images
);
669 r
= sd_bus_message_read_strv(message
, &matches
);
673 r
= sd_bus_message_read(message
, "s", &profile
);
677 if (sd_bus_message_is_method_call(message
, NULL
, "ReattachImageWithExtensions") ||
678 sd_bus_message_is_method_call(message
, NULL
, "ReattachWithExtensions")) {
679 uint64_t input_flags
= 0;
681 r
= sd_bus_message_read(message
, "st", ©_mode
, &input_flags
);
685 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
686 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
687 "Invalid 'flags' parameter '%" PRIu64
"'",
689 flags
|= input_flags
;
693 r
= sd_bus_message_read(message
, "bs", &runtime
, ©_mode
);
698 flags
|= PORTABLE_RUNTIME
;
701 if (streq(copy_mode
, "symlink"))
702 flags
|= PORTABLE_PREFER_SYMLINK
;
703 else if (streq(copy_mode
, "copy"))
704 flags
|= PORTABLE_PREFER_COPY
;
705 else if (streq(copy_mode
, "mixed"))
706 flags
|= PORTABLE_MIXED_COPY_LINK
;
707 else if (!isempty(copy_mode
))
708 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown copy mode '%s'", copy_mode
);
710 r
= bus_image_acquire(m
,
714 BUS_IMAGE_AUTHENTICATE_ALL
,
715 "org.freedesktop.portable1.attach-images",
720 if (r
== 0) /* Will call us back */
725 sd_bus_message_get_bus(message
),
737 sd_bus_message_get_bus(message
),
742 /* image_policy= */ NULL
,
750 /* We want to return the list of units really removed by the detach,
751 * and not added again by the attach */
752 r
= normalize_portable_changes(changes_attached
, n_changes_attached
,
753 changes_detached
, n_changes_detached
,
754 &changes_gone
, &n_changes_gone
);
758 /* First, return the units that are gone (so that the caller can stop them)
759 * Then, return the units that are changed/added (so that the caller can
760 * start/restart/enable them) */
761 return reply_portable_changes_pair(message
,
762 changes_gone
, n_changes_gone
,
763 changes_attached
, n_changes_attached
);
766 static int bus_image_method_reattach(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
767 return bus_image_common_reattach(NULL
, message
, NULL
, userdata
, error
);
770 int bus_image_common_mark_read_only(
772 sd_bus_message
*message
,
773 const char *name_or_path
,
775 sd_bus_error
*error
) {
780 assert(name_or_path
|| image
);
787 r
= sd_bus_message_read(message
, "b", &read_only
);
791 r
= bus_image_acquire(m
,
795 BUS_IMAGE_AUTHENTICATE_ALL
,
796 "org.freedesktop.portable1.manage-images",
802 return 1; /* Will call us back */
804 r
= image_read_only(image
, read_only
);
808 return sd_bus_reply_method_return(message
, NULL
);
811 static int bus_image_method_mark_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
812 return bus_image_common_mark_read_only(NULL
, message
, NULL
, userdata
, error
);
815 int bus_image_common_set_limit(
817 sd_bus_message
*message
,
818 const char *name_or_path
,
820 sd_bus_error
*error
) {
826 assert(name_or_path
|| image
);
833 r
= sd_bus_message_read(message
, "t", &limit
);
836 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
837 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
839 r
= bus_image_acquire(m
,
843 BUS_IMAGE_AUTHENTICATE_ALL
,
844 "org.freedesktop.portable1.manage-images",
850 return 1; /* Will call us back */
852 r
= image_set_limit(image
, limit
);
856 return sd_bus_reply_method_return(message
, NULL
);
859 static int bus_image_method_set_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
860 return bus_image_common_set_limit(NULL
, message
, NULL
, userdata
, error
);
863 const sd_bus_vtable image_vtable
[] = {
864 SD_BUS_VTABLE_START(0),
865 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
866 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
867 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
868 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
869 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
870 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
871 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
872 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
873 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
874 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
875 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
877 SD_BUS_RESULT("a{ss}", os_release
),
878 bus_image_method_get_os_release
,
879 SD_BUS_VTABLE_UNPRIVILEGED
),
880 SD_BUS_METHOD_WITH_ARGS("GetMetadata",
881 SD_BUS_ARGS("as", matches
),
882 SD_BUS_RESULT("s", image
,
885 bus_image_method_get_metadata
,
886 SD_BUS_VTABLE_UNPRIVILEGED
),
887 SD_BUS_METHOD_WITH_ARGS("GetMetadataWithExtensions",
888 SD_BUS_ARGS("as", extensions
,
891 SD_BUS_RESULT("s", image
,
893 "a{say}", extensions
,
895 bus_image_method_get_metadata
,
896 SD_BUS_VTABLE_UNPRIVILEGED
),
897 SD_BUS_METHOD_WITH_ARGS("GetState",
899 SD_BUS_RESULT("s", state
),
900 bus_image_method_get_state
,
901 SD_BUS_VTABLE_UNPRIVILEGED
),
902 SD_BUS_METHOD_WITH_ARGS("GetStateWithExtensions",
903 SD_BUS_ARGS("as", extensions
,
905 SD_BUS_RESULT("s", state
),
906 bus_image_method_get_state
,
907 SD_BUS_VTABLE_UNPRIVILEGED
),
908 SD_BUS_METHOD_WITH_ARGS("Attach",
909 SD_BUS_ARGS("as", matches
,
913 SD_BUS_RESULT("a(sss)", changes
),
914 bus_image_method_attach
,
915 SD_BUS_VTABLE_UNPRIVILEGED
),
916 SD_BUS_METHOD_WITH_ARGS("AttachWithExtensions",
917 SD_BUS_ARGS("as", extensions
,
922 SD_BUS_RESULT("a(sss)", changes
),
923 bus_image_method_attach
,
924 SD_BUS_VTABLE_UNPRIVILEGED
),
925 SD_BUS_METHOD_WITH_ARGS("Detach",
926 SD_BUS_ARGS("b", runtime
),
927 SD_BUS_RESULT("a(sss)", changes
),
928 bus_image_method_detach
,
929 SD_BUS_VTABLE_UNPRIVILEGED
),
930 SD_BUS_METHOD_WITH_ARGS("DetachWithExtensions",
931 SD_BUS_ARGS("as", extensions
,
933 SD_BUS_RESULT("a(sss)", changes
),
934 bus_image_method_detach
,
935 SD_BUS_VTABLE_UNPRIVILEGED
),
936 SD_BUS_METHOD_WITH_ARGS("Reattach",
937 SD_BUS_ARGS("as", matches
,
941 SD_BUS_RESULT("a(sss)", changes_removed
,
942 "a(sss)", changes_updated
),
943 bus_image_method_reattach
,
944 SD_BUS_VTABLE_UNPRIVILEGED
),
945 SD_BUS_METHOD_WITH_ARGS("ReattachWithExtensions",
946 SD_BUS_ARGS("as", extensions
,
951 SD_BUS_RESULT("a(sss)", changes_removed
,
952 "a(sss)", changes_updated
),
953 bus_image_method_reattach
,
954 SD_BUS_VTABLE_UNPRIVILEGED
),
955 SD_BUS_METHOD_WITH_ARGS("Remove",
958 bus_image_method_remove
,
959 SD_BUS_VTABLE_UNPRIVILEGED
),
960 SD_BUS_METHOD_WITH_ARGS("MarkReadOnly",
961 SD_BUS_ARGS("b", read_only
),
963 bus_image_method_mark_read_only
,
964 SD_BUS_VTABLE_UNPRIVILEGED
),
965 SD_BUS_METHOD_WITH_ARGS("SetLimit",
966 SD_BUS_ARGS("t", limit
),
968 bus_image_method_set_limit
,
969 SD_BUS_VTABLE_UNPRIVILEGED
),
970 /* Deprecated silly typo */
971 SD_BUS_METHOD_WITH_ARGS("ReattacheWithExtensions",
972 SD_BUS_ARGS("as", extensions
,
977 SD_BUS_RESULT("a(sss)", changes_removed
,
978 "a(sss)", changes_updated
),
979 bus_image_method_reattach
,
980 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_HIDDEN
),
984 int bus_image_path(Image
*image
, char **ret
) {
988 if (!image
->discoverable
)
991 return sd_bus_path_encode("/org/freedesktop/portable1/image", image
->name
, ret
);
994 int bus_image_acquire(
996 sd_bus_message
*message
,
997 const char *name_or_path
,
999 ImageAcquireMode mode
,
1000 const char *polkit_action
,
1002 sd_bus_error
*error
) {
1004 _cleanup_(image_unrefp
) Image
*loaded
= NULL
;
1010 assert(name_or_path
|| image
);
1012 assert(mode
< _BUS_IMAGE_ACQUIRE_MODE_MAX
);
1013 assert(polkit_action
|| mode
== BUS_IMAGE_REFUSE_BY_PATH
);
1016 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
1018 if (mode
== BUS_IMAGE_AUTHENTICATE_ALL
) {
1019 r
= bus_verify_polkit_async(
1022 /* details= */ NULL
,
1023 &m
->polkit_registry
,
1027 if (r
== 0) { /* Will call us back */
1033 /* Already passed in? */
1039 /* Let's see if this image is already cached? */
1040 cached
= manager_image_cache_get(m
, name_or_path
);
1046 if (image_name_is_valid(name_or_path
)) {
1048 /* If it's a short name, let's search for it */
1049 r
= image_find(m
->runtime_scope
, IMAGE_PORTABLE
, name_or_path
, NULL
, &loaded
);
1051 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE
,
1052 "No image '%s' found.", name_or_path
);
1054 /* other errors are handled below… */
1056 /* Don't accept path if this is always forbidden */
1057 if (mode
== BUS_IMAGE_REFUSE_BY_PATH
)
1058 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
1059 "Expected image name, not path in place of '%s'.", name_or_path
);
1061 if (!path_is_absolute(name_or_path
))
1062 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
1063 "Image name '%s' is not valid or not a valid path.", name_or_path
);
1065 if (!path_is_normalized(name_or_path
))
1066 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
,
1067 "Image path '%s' is not normalized.", name_or_path
);
1069 if (mode
== BUS_IMAGE_AUTHENTICATE_BY_PATH
) {
1070 r
= bus_verify_polkit_async(
1073 /* details= */ NULL
,
1074 &m
->polkit_registry
,
1078 if (r
== 0) { /* Will call us back */
1084 r
= image_from_path(name_or_path
, &loaded
);
1086 if (r
== -EMEDIUMTYPE
) {
1087 sd_bus_error_setf(error
, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE
,
1088 "Type of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.",
1095 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
1096 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
1097 * means we don't actually need to ref the return object. */
1098 r
= manager_image_cache_add(m
, loaded
);
1106 int bus_image_object_find(
1109 const char *interface
,
1112 sd_bus_error
*error
) {
1114 _cleanup_free_
char *e
= NULL
;
1115 Manager
*m
= userdata
;
1116 Image
*image
= NULL
;
1124 r
= sd_bus_path_decode(path
, "/org/freedesktop/portable1/image", &e
);
1130 /* The path is "/org/freedesktop/portable1/image" itself */
1133 r
= bus_image_acquire(m
, sd_bus_get_current_message(bus
), e
, NULL
, BUS_IMAGE_REFUSE_BY_PATH
, NULL
, &image
, error
);
1147 int bus_image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1148 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
1149 _cleanup_strv_free_
char **l
= NULL
;
1150 Manager
*m
= userdata
;
1159 r
= manager_image_cache_discover(m
, &images
, error
);
1163 HASHMAP_FOREACH(image
, images
) {
1166 r
= bus_image_path(image
, &p
);
1170 if (!GREEDY_REALLOC(l
, n
+2)) {
1179 *nodes
= TAKE_PTR(l
);
1184 const BusObjectImplementation image_object
= {
1185 "/org/freedesktop/portable1/image",
1186 "org.freedesktop.portable1.Image",
1187 .fallback_vtables
= BUS_FALLBACK_VTABLES({image_vtable
, bus_image_object_find
}),
1188 .node_enumerator
= bus_image_node_enumerator
,