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-polkit.h"
9 #include "machine-image.h"
10 #include "missing_capability.h"
12 #include "portabled-bus.h"
13 #include "portabled-image-bus.h"
14 #include "portabled-image.h"
15 #include "portabled.h"
17 #include "user-util.h"
19 static int property_get_pool_path(
22 const char *interface
,
24 sd_bus_message
*reply
,
26 sd_bus_error
*error
) {
31 return sd_bus_message_append(reply
, "s", "/var/lib/portables");
34 static int property_get_pool_usage(
37 const char *interface
,
39 sd_bus_message
*reply
,
41 sd_bus_error
*error
) {
43 _cleanup_close_
int fd
= -1;
44 uint64_t usage
= (uint64_t) -1;
49 fd
= open("/var/lib/portables", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
53 if (btrfs_subvol_get_subtree_quota_fd(fd
, 0, &q
) >= 0)
57 return sd_bus_message_append(reply
, "t", usage
);
60 static int property_get_pool_limit(
63 const char *interface
,
65 sd_bus_message
*reply
,
67 sd_bus_error
*error
) {
69 _cleanup_close_
int fd
= -1;
70 uint64_t size
= (uint64_t) -1;
75 fd
= open("/var/lib/portables", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
79 if (btrfs_subvol_get_subtree_quota_fd(fd
, 0, &q
) >= 0)
80 size
= q
.referenced_max
;
83 return sd_bus_message_append(reply
, "t", size
);
86 static int property_get_profiles(
89 const char *interface
,
91 sd_bus_message
*reply
,
93 sd_bus_error
*error
) {
95 _cleanup_strv_free_
char **l
= NULL
;
101 r
= portable_get_profiles(&l
);
105 return sd_bus_message_append_strv(reply
, l
);
108 static int method_get_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
109 _cleanup_free_
char *p
= NULL
;
110 Manager
*m
= 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
= userdata
;
143 images
= hashmap_new(&image_hash_ops
);
147 r
= manager_image_cache_discover(m
, images
, error
);
151 r
= sd_bus_message_new_method_return(message
, &reply
);
155 r
= sd_bus_message_open_container(reply
, 'a', "(ssbtttso)");
159 HASHMAP_FOREACH(image
, images
) {
160 _cleanup_(sd_bus_error_free
) sd_bus_error error_state
= SD_BUS_ERROR_NULL
;
161 PortableState state
= _PORTABLE_STATE_INVALID
;
162 _cleanup_free_
char *p
= NULL
;
164 r
= bus_image_path(image
, &p
);
168 r
= portable_get_state(
169 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 const char *name_or_path
;
233 r
= sd_bus_message_read(message
, "s", &name_or_path
);
237 r
= portable_get_state(
238 sd_bus_message_get_bus(message
),
246 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
249 static int method_attach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
250 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_attach
);
253 static int method_detach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
254 PortableChange
*changes
= NULL
;
255 Manager
*m
= userdata
;
256 size_t n_changes
= 0;
257 const char *name_or_path
;
263 /* Note that we do not redirect detaching to the image object here, because we want to allow that users can
264 * detach already deleted images too, in case the user already deleted an image before properly detaching
267 r
= sd_bus_message_read(message
, "sb", &name_or_path
, &runtime
);
271 r
= bus_verify_polkit_async(
274 "org.freedesktop.portable1.attach-images",
283 return 1; /* Will call us back */
286 sd_bus_message_get_bus(message
),
288 runtime
? PORTABLE_RUNTIME
: 0,
295 r
= reply_portable_changes(message
, changes
, n_changes
);
298 portable_changes_free(changes
, n_changes
);
302 static int method_remove_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
303 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_remove
);
306 static int method_mark_image_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
307 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_mark_read_only
);
310 static int method_set_image_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
311 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_set_limit
);
314 static int method_set_pool_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
315 Manager
*m
= userdata
;
321 r
= sd_bus_message_read(message
, "t", &limit
);
324 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
325 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
327 r
= bus_verify_polkit_async(
330 "org.freedesktop.portable1.manage-images",
339 return 1; /* Will call us back */
341 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit
);
343 r
= btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit
);
345 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Quota is only supported on btrfs.");
347 return sd_bus_error_set_errnof(error
, r
, "Failed to adjust quota limit: %m");
349 return sd_bus_reply_method_return(message
, NULL
);
352 const sd_bus_vtable manager_vtable
[] = {
353 SD_BUS_VTABLE_START(0),
354 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path
, 0, 0),
355 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage
, 0, 0),
356 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit
, 0, 0),
357 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles
, 0, 0),
358 SD_BUS_METHOD("GetImage", "s", "o", method_get_image
, SD_BUS_VTABLE_UNPRIVILEGED
),
359 SD_BUS_METHOD("ListImages", NULL
, "a(ssbtttso)", method_list_images
, SD_BUS_VTABLE_UNPRIVILEGED
),
360 SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
361 SD_BUS_METHOD("GetImageMetadata", "sas", "saya{say}", method_get_image_metadata
, SD_BUS_VTABLE_UNPRIVILEGED
),
362 SD_BUS_METHOD("GetImageState", "s", "s", method_get_image_state
, SD_BUS_VTABLE_UNPRIVILEGED
),
363 SD_BUS_METHOD("AttachImage", "sassbs", "a(sss)", method_attach_image
, SD_BUS_VTABLE_UNPRIVILEGED
),
364 SD_BUS_METHOD("DetachImage", "sb", "a(sss)", method_detach_image
, SD_BUS_VTABLE_UNPRIVILEGED
),
365 SD_BUS_METHOD("RemoveImage", "s", NULL
, method_remove_image
, SD_BUS_VTABLE_UNPRIVILEGED
),
366 SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL
, method_mark_image_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
367 SD_BUS_METHOD("SetImageLimit", "st", NULL
, method_set_image_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
368 SD_BUS_METHOD("SetPoolLimit", "t", NULL
, method_set_pool_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
372 int reply_portable_changes(sd_bus_message
*m
, const PortableChange
*changes
, size_t n_changes
) {
373 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
378 assert(changes
|| n_changes
== 0);
380 r
= sd_bus_message_new_method_return(m
, &reply
);
384 r
= sd_bus_message_open_container(reply
, 'a', "(sss)");
388 for (i
= 0; i
< n_changes
; i
++) {
389 r
= sd_bus_message_append(reply
, "(sss)",
390 portable_change_type_to_string(changes
[i
].type
),
397 r
= sd_bus_message_close_container(reply
);
401 return sd_bus_send(NULL
, reply
, NULL
);