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(
324 "org.freedesktop.portable1.attach-images",
333 return 1; /* Will call us back */
336 sd_bus_message_get_bus(message
),
346 return reply_portable_changes(message
, changes
, n_changes
);
349 static int method_reattach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
350 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_reattach
);
353 static int method_remove_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
354 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_remove
);
357 static int method_mark_image_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
358 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_mark_read_only
);
361 static int method_set_image_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
362 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_set_limit
);
365 static int method_set_pool_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
366 Manager
*m
= userdata
;
372 r
= sd_bus_message_read(message
, "t", &limit
);
375 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
376 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
378 r
= bus_verify_polkit_async(
381 "org.freedesktop.portable1.manage-images",
390 return 1; /* Will call us back */
392 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit
);
394 r
= btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit
);
396 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Quota is only supported on btrfs.");
398 return sd_bus_error_set_errnof(error
, r
, "Failed to adjust quota limit: %m");
400 return sd_bus_reply_method_return(message
, NULL
);
403 const sd_bus_vtable manager_vtable
[] = {
404 SD_BUS_VTABLE_START(0),
405 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path
, 0, 0),
406 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage
, 0, 0),
407 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit
, 0, 0),
408 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles
, 0, 0),
409 SD_BUS_METHOD_WITH_ARGS("GetImage",
410 SD_BUS_ARGS("s", image
),
411 SD_BUS_RESULT("o", object
),
413 SD_BUS_VTABLE_UNPRIVILEGED
),
414 SD_BUS_METHOD_WITH_ARGS("ListImages",
416 SD_BUS_RESULT("a(ssbtttso)", images
),
418 SD_BUS_VTABLE_UNPRIVILEGED
),
419 SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
420 SD_BUS_ARGS("s", image
),
421 SD_BUS_RESULT("a{ss}", os_release
),
422 method_get_image_os_release
,
423 SD_BUS_VTABLE_UNPRIVILEGED
),
424 SD_BUS_METHOD_WITH_ARGS("GetImageMetadata",
425 SD_BUS_ARGS("s", image
,
427 SD_BUS_RESULT("s", image
,
430 method_get_image_metadata
,
431 SD_BUS_VTABLE_UNPRIVILEGED
),
432 SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
433 SD_BUS_ARGS("s", image
,
437 SD_BUS_RESULT("s", image
,
439 "a{say}", extensions
,
441 method_get_image_metadata
,
442 SD_BUS_VTABLE_UNPRIVILEGED
),
443 SD_BUS_METHOD_WITH_ARGS("GetImageState",
444 SD_BUS_ARGS("s", image
),
445 SD_BUS_RESULT("s", state
),
446 method_get_image_state
,
447 SD_BUS_VTABLE_UNPRIVILEGED
),
448 SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
449 SD_BUS_ARGS("s", image
,
452 SD_BUS_RESULT("s", state
),
453 method_get_image_state
,
454 SD_BUS_VTABLE_UNPRIVILEGED
),
455 SD_BUS_METHOD_WITH_ARGS("AttachImage",
456 SD_BUS_ARGS("s", image
,
461 SD_BUS_RESULT("a(sss)", changes
),
463 SD_BUS_VTABLE_UNPRIVILEGED
),
464 SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
465 SD_BUS_ARGS("s", image
,
471 SD_BUS_RESULT("a(sss)", changes
),
473 SD_BUS_VTABLE_UNPRIVILEGED
),
474 SD_BUS_METHOD_WITH_ARGS("DetachImage",
475 SD_BUS_ARGS("s", image
,
477 SD_BUS_RESULT("a(sss)", changes
),
479 SD_BUS_VTABLE_UNPRIVILEGED
),
480 SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
481 SD_BUS_ARGS("s", image
,
484 SD_BUS_RESULT("a(sss)", changes
),
486 SD_BUS_VTABLE_UNPRIVILEGED
),
487 SD_BUS_METHOD_WITH_ARGS("ReattachImage",
488 SD_BUS_ARGS("s", image
,
493 SD_BUS_RESULT("a(sss)", changes_removed
,
494 "a(sss)", changes_updated
),
495 method_reattach_image
,
496 SD_BUS_VTABLE_UNPRIVILEGED
),
497 SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
498 SD_BUS_ARGS("s", image
,
504 SD_BUS_RESULT("a(sss)", changes_removed
,
505 "a(sss)", changes_updated
),
506 method_reattach_image
,
507 SD_BUS_VTABLE_UNPRIVILEGED
),
508 SD_BUS_METHOD_WITH_ARGS("RemoveImage",
509 SD_BUS_ARGS("s", image
),
512 SD_BUS_VTABLE_UNPRIVILEGED
),
513 SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
514 SD_BUS_ARGS("s", image
,
517 method_mark_image_read_only
,
518 SD_BUS_VTABLE_UNPRIVILEGED
),
519 SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
520 SD_BUS_ARGS("s", image
,
523 method_set_image_limit
,
524 SD_BUS_VTABLE_UNPRIVILEGED
),
525 SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
526 SD_BUS_ARGS("t", limit
),
528 method_set_pool_limit
,
529 SD_BUS_VTABLE_UNPRIVILEGED
),
533 const BusObjectImplementation manager_object
= {
534 "/org/freedesktop/portable1",
535 "org.freedesktop.portable1.Manager",
536 .vtables
= BUS_VTABLES(manager_vtable
),
537 .children
= BUS_IMPLEMENTATIONS(&image_object
),
540 static int reply_portable_compose_message(sd_bus_message
*reply
, const PortableChange
*changes
, size_t n_changes
) {
545 assert(changes
|| n_changes
== 0);
547 r
= sd_bus_message_open_container(reply
, 'a', "(sss)");
551 for (i
= 0; i
< n_changes
; i
++) {
552 if (changes
[i
].type_or_errno
< 0)
555 r
= sd_bus_message_append(reply
, "(sss)",
556 portable_change_type_to_string(changes
[i
].type_or_errno
),
563 r
= sd_bus_message_close_container(reply
);
570 int reply_portable_changes(sd_bus_message
*m
, const PortableChange
*changes
, size_t n_changes
) {
571 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
576 r
= sd_bus_message_new_method_return(m
, &reply
);
580 r
= reply_portable_compose_message(reply
, changes
, n_changes
);
584 return sd_bus_send(NULL
, reply
, NULL
);
587 int reply_portable_changes_pair(
589 const PortableChange
*changes_first
,
590 size_t n_changes_first
,
591 const PortableChange
*changes_second
,
592 size_t n_changes_second
) {
594 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
599 r
= sd_bus_message_new_method_return(m
, &reply
);
603 r
= reply_portable_compose_message(reply
, changes_first
, n_changes_first
);
607 r
= reply_portable_compose_message(reply
, changes_second
, n_changes_second
);
611 return sd_bus_send(NULL
, reply
, NULL
);