1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "btrfs-util.h"
5 #include "bus-common-errors.h"
6 #include "bus-object.h"
7 #include "bus-polkit.h"
8 #include "discover-image.h"
11 #include "missing_capability.h"
13 #include "portabled-bus.h"
14 #include "portabled-image-bus.h"
15 #include "portabled-image.h"
16 #include "portabled.h"
18 #include "user-util.h"
20 static int property_get_pool_path(
23 const char *interface
,
25 sd_bus_message
*reply
,
27 sd_bus_error
*error
) {
32 return sd_bus_message_append(reply
, "s", "/var/lib/portables");
35 static int property_get_pool_usage(
38 const char *interface
,
40 sd_bus_message
*reply
,
42 sd_bus_error
*error
) {
44 _cleanup_close_
int fd
= -EBADF
;
45 uint64_t usage
= UINT64_MAX
;
50 fd
= open("/var/lib/portables", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
54 if (btrfs_subvol_get_subtree_quota_fd(fd
, 0, &q
) >= 0)
58 return sd_bus_message_append(reply
, "t", usage
);
61 static int property_get_pool_limit(
64 const char *interface
,
66 sd_bus_message
*reply
,
68 sd_bus_error
*error
) {
70 _cleanup_close_
int fd
= -EBADF
;
71 uint64_t size
= UINT64_MAX
;
76 fd
= open("/var/lib/portables", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
80 if (btrfs_subvol_get_subtree_quota_fd(fd
, 0, &q
) >= 0)
81 size
= q
.referenced_max
;
84 return sd_bus_message_append(reply
, "t", size
);
87 static int property_get_profiles(
90 const char *interface
,
92 sd_bus_message
*reply
,
94 sd_bus_error
*error
) {
96 _cleanup_strv_free_
char **l
= NULL
;
102 r
= portable_get_profiles(&l
);
106 return sd_bus_message_append_strv(reply
, l
);
109 static int method_get_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
110 _cleanup_free_
char *p
= NULL
;
111 Manager
*m
= ASSERT_PTR(userdata
);
118 r
= sd_bus_message_read(message
, "s", &name
);
122 r
= bus_image_acquire(m
, message
, name
, NULL
, BUS_IMAGE_REFUSE_BY_PATH
, NULL
, &image
, error
);
126 r
= bus_image_path(image
, &p
);
130 return sd_bus_reply_method_return(message
, "o", p
);
133 static int method_list_images(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
134 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
135 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
136 Manager
*m
= ASSERT_PTR(userdata
);
142 images
= hashmap_new(&image_hash_ops
);
146 r
= manager_image_cache_discover(m
, images
, error
);
150 r
= sd_bus_message_new_method_return(message
, &reply
);
154 r
= sd_bus_message_open_container(reply
, 'a', "(ssbtttso)");
158 HASHMAP_FOREACH(image
, images
) {
159 _cleanup_(sd_bus_error_free
) sd_bus_error error_state
= SD_BUS_ERROR_NULL
;
160 PortableState state
= _PORTABLE_STATE_INVALID
;
161 _cleanup_free_
char *p
= NULL
;
163 r
= bus_image_path(image
, &p
);
167 r
= portable_get_state(
168 sd_bus_message_get_bus(message
),
175 log_debug_errno(r
, "Failed to get state of image '%s', ignoring: %s",
176 image
->path
, bus_error_message(&error_state
, r
));
178 r
= sd_bus_message_append(reply
, "(ssbtttso)",
180 image_type_to_string(image
->type
),
185 portable_state_to_string(state
),
191 r
= sd_bus_message_close_container(reply
);
195 return sd_bus_send(NULL
, reply
, NULL
);
198 static int redirect_method_to_image(
200 sd_bus_message
*message
,
202 int (*method
)(Manager
*m
, sd_bus_message
*message
, const char *name_or_path
, Image
*image
, sd_bus_error
* error
)) {
204 const char *name_or_path
;
211 r
= sd_bus_message_read(message
, "s", &name_or_path
);
215 return method(m
, message
, name_or_path
, NULL
, error
);
218 static int method_get_image_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
219 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_get_os_release
);
222 static int method_get_image_metadata(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
223 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_get_metadata
);
226 static int method_get_image_state(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
227 _cleanup_strv_free_
char **extension_images
= NULL
;
228 const char *name_or_path
;
234 r
= sd_bus_message_read(message
, "s", &name_or_path
);
238 if (sd_bus_message_is_method_call(message
, NULL
, "GetImageStateWithExtensions")) {
239 uint64_t input_flags
= 0;
241 r
= sd_bus_message_read_strv(message
, &extension_images
);
245 r
= sd_bus_message_read(message
, "t", &input_flags
);
249 /* No flags are supported by this method for now. */
250 if (input_flags
!= 0)
251 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
252 "Invalid 'flags' parameter '%" PRIu64
"'",
256 r
= portable_get_state(
257 sd_bus_message_get_bus(message
),
266 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
269 static int method_attach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
270 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_attach
);
273 static int method_detach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
274 _cleanup_strv_free_
char **extension_images
= NULL
;
275 PortableChange
*changes
= NULL
;
276 PortableFlags flags
= 0;
277 Manager
*m
= ASSERT_PTR(userdata
);
278 size_t n_changes
= 0;
279 const char *name_or_path
;
284 CLEANUP_ARRAY(changes
, n_changes
, portable_changes_free
);
286 /* Note that we do not redirect detaching to the image object here, because we want to allow that users can
287 * detach already deleted images too, in case the user already deleted an image before properly detaching
290 r
= sd_bus_message_read(message
, "s", &name_or_path
);
294 if (sd_bus_message_is_method_call(message
, NULL
, "DetachImageWithExtensions")) {
295 uint64_t input_flags
= 0;
297 r
= sd_bus_message_read_strv(message
, &extension_images
);
301 r
= sd_bus_message_read(message
, "t", &input_flags
);
305 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
306 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
307 "Invalid 'flags' parameter '%" PRIu64
"'",
309 flags
|= input_flags
;
313 r
= sd_bus_message_read(message
, "b", &runtime
);
318 flags
|= PORTABLE_RUNTIME
;
321 r
= bus_verify_polkit_async(
323 "org.freedesktop.portable1.attach-images",
330 return 1; /* Will call us back */
333 sd_bus_message_get_bus(message
),
343 return reply_portable_changes(message
, changes
, n_changes
);
346 static int method_reattach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
347 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_reattach
);
350 static int method_remove_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
351 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_remove
);
354 static int method_mark_image_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
355 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_mark_read_only
);
358 static int method_set_image_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
359 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_set_limit
);
362 static int method_set_pool_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
363 Manager
*m
= userdata
;
369 r
= sd_bus_message_read(message
, "t", &limit
);
372 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
373 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
375 r
= bus_verify_polkit_async(
377 "org.freedesktop.portable1.manage-images",
384 return 1; /* Will call us back */
386 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit
);
388 r
= btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit
);
390 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Quota is only supported on btrfs.");
392 return sd_bus_error_set_errnof(error
, r
, "Failed to adjust quota limit: %m");
394 return sd_bus_reply_method_return(message
, NULL
);
397 const sd_bus_vtable manager_vtable
[] = {
398 SD_BUS_VTABLE_START(0),
399 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path
, 0, 0),
400 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage
, 0, 0),
401 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit
, 0, 0),
402 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles
, 0, 0),
403 SD_BUS_METHOD_WITH_ARGS("GetImage",
404 SD_BUS_ARGS("s", image
),
405 SD_BUS_RESULT("o", object
),
407 SD_BUS_VTABLE_UNPRIVILEGED
),
408 SD_BUS_METHOD_WITH_ARGS("ListImages",
410 SD_BUS_RESULT("a(ssbtttso)", images
),
412 SD_BUS_VTABLE_UNPRIVILEGED
),
413 SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
414 SD_BUS_ARGS("s", image
),
415 SD_BUS_RESULT("a{ss}", os_release
),
416 method_get_image_os_release
,
417 SD_BUS_VTABLE_UNPRIVILEGED
),
418 SD_BUS_METHOD_WITH_ARGS("GetImageMetadata",
419 SD_BUS_ARGS("s", image
,
421 SD_BUS_RESULT("s", image
,
424 method_get_image_metadata
,
425 SD_BUS_VTABLE_UNPRIVILEGED
),
426 SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
427 SD_BUS_ARGS("s", image
,
431 SD_BUS_RESULT("s", image
,
433 "a{say}", extensions
,
435 method_get_image_metadata
,
436 SD_BUS_VTABLE_UNPRIVILEGED
),
437 SD_BUS_METHOD_WITH_ARGS("GetImageState",
438 SD_BUS_ARGS("s", image
),
439 SD_BUS_RESULT("s", state
),
440 method_get_image_state
,
441 SD_BUS_VTABLE_UNPRIVILEGED
),
442 SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
443 SD_BUS_ARGS("s", image
,
446 SD_BUS_RESULT("s", state
),
447 method_get_image_state
,
448 SD_BUS_VTABLE_UNPRIVILEGED
),
449 SD_BUS_METHOD_WITH_ARGS("AttachImage",
450 SD_BUS_ARGS("s", image
,
455 SD_BUS_RESULT("a(sss)", changes
),
457 SD_BUS_VTABLE_UNPRIVILEGED
),
458 SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
459 SD_BUS_ARGS("s", image
,
465 SD_BUS_RESULT("a(sss)", changes
),
467 SD_BUS_VTABLE_UNPRIVILEGED
),
468 SD_BUS_METHOD_WITH_ARGS("DetachImage",
469 SD_BUS_ARGS("s", image
,
471 SD_BUS_RESULT("a(sss)", changes
),
473 SD_BUS_VTABLE_UNPRIVILEGED
),
474 SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
475 SD_BUS_ARGS("s", image
,
478 SD_BUS_RESULT("a(sss)", changes
),
480 SD_BUS_VTABLE_UNPRIVILEGED
),
481 SD_BUS_METHOD_WITH_ARGS("ReattachImage",
482 SD_BUS_ARGS("s", image
,
487 SD_BUS_RESULT("a(sss)", changes_removed
,
488 "a(sss)", changes_updated
),
489 method_reattach_image
,
490 SD_BUS_VTABLE_UNPRIVILEGED
),
491 SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
492 SD_BUS_ARGS("s", image
,
498 SD_BUS_RESULT("a(sss)", changes_removed
,
499 "a(sss)", changes_updated
),
500 method_reattach_image
,
501 SD_BUS_VTABLE_UNPRIVILEGED
),
502 SD_BUS_METHOD_WITH_ARGS("RemoveImage",
503 SD_BUS_ARGS("s", image
),
506 SD_BUS_VTABLE_UNPRIVILEGED
),
507 SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
508 SD_BUS_ARGS("s", image
,
511 method_mark_image_read_only
,
512 SD_BUS_VTABLE_UNPRIVILEGED
),
513 SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
514 SD_BUS_ARGS("s", image
,
517 method_set_image_limit
,
518 SD_BUS_VTABLE_UNPRIVILEGED
),
519 SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
520 SD_BUS_ARGS("t", limit
),
522 method_set_pool_limit
,
523 SD_BUS_VTABLE_UNPRIVILEGED
),
527 const BusObjectImplementation manager_object
= {
528 "/org/freedesktop/portable1",
529 "org.freedesktop.portable1.Manager",
530 .vtables
= BUS_VTABLES(manager_vtable
),
531 .children
= BUS_IMPLEMENTATIONS(&image_object
),
534 static int reply_portable_compose_message(sd_bus_message
*reply
, const PortableChange
*changes
, size_t n_changes
) {
539 assert(changes
|| n_changes
== 0);
541 r
= sd_bus_message_open_container(reply
, 'a', "(sss)");
545 for (i
= 0; i
< n_changes
; i
++) {
546 if (changes
[i
].type_or_errno
< 0)
549 r
= sd_bus_message_append(reply
, "(sss)",
550 portable_change_type_to_string(changes
[i
].type_or_errno
),
557 r
= sd_bus_message_close_container(reply
);
564 int reply_portable_changes(sd_bus_message
*m
, const PortableChange
*changes
, size_t n_changes
) {
565 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
570 r
= sd_bus_message_new_method_return(m
, &reply
);
574 r
= reply_portable_compose_message(reply
, changes
, n_changes
);
578 return sd_bus_send(NULL
, reply
, NULL
);
581 int reply_portable_changes_pair(
583 const PortableChange
*changes_first
,
584 size_t n_changes_first
,
585 const PortableChange
*changes_second
,
586 size_t n_changes_second
) {
588 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
593 r
= sd_bus_message_new_method_return(m
, &reply
);
597 r
= reply_portable_compose_message(reply
, changes_first
, n_changes_first
);
601 r
= reply_portable_compose_message(reply
, changes_second
, n_changes_second
);
605 return sd_bus_send(NULL
, reply
, NULL
);