1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "bus-get-properties.h"
9 #include "bus-polkit.h"
11 #include "discover-image.h"
12 #include "dissect-image.h"
16 #include "image-dbus.h"
18 #include "loop-util.h"
19 #include "missing_capability.h"
20 #include "mount-util.h"
22 #include "process-util.h"
23 #include "raw-clone.h"
25 #include "user-util.h"
27 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
29 int bus_image_method_remove(
30 sd_bus_message
*message
,
32 sd_bus_error
*error
) {
34 _cleanup_close_pair_
int errno_pipe_fd
[2] = EBADF_PAIR
;
35 Image
*image
= ASSERT_PTR(userdata
);
36 Manager
*m
= image
->userdata
;
42 if (m
->n_operations
>= OPERATIONS_MAX
)
43 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
45 const char *details
[] = {
51 r
= bus_verify_polkit_async(
53 "org.freedesktop.machine1.manage-images",
60 return 1; /* Will call us back */
62 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
63 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
65 r
= safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS
, &child
);
67 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
69 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
71 r
= image_remove(image
);
73 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
80 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
82 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
84 (void) sigkill_wait(child
);
88 errno_pipe_fd
[0] = -EBADF
;
93 int bus_image_method_rename(
94 sd_bus_message
*message
,
96 sd_bus_error
*error
) {
98 Image
*image
= ASSERT_PTR(userdata
);
99 Manager
*m
= image
->userdata
;
100 const char *new_name
;
105 r
= sd_bus_message_read(message
, "s", &new_name
);
109 if (!image_name_is_valid(new_name
))
110 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
112 const char *details
[] = {
113 "image", image
->name
,
115 "new_name", new_name
,
119 r
= bus_verify_polkit_async(
121 "org.freedesktop.machine1.manage-images",
128 return 1; /* Will call us back */
130 /* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
131 assert_se(hashmap_remove_value(m
->image_cache
, image
->name
, image
));
133 r
= image_rename(image
, new_name
);
139 /* Then save the object again in the cache. */
140 assert_se(hashmap_put(m
->image_cache
, image
->name
, image
) > 0);
142 return sd_bus_reply_method_return(message
, NULL
);
145 int bus_image_method_clone(
146 sd_bus_message
*message
,
148 sd_bus_error
*error
) {
150 _cleanup_close_pair_
int errno_pipe_fd
[2] = EBADF_PAIR
;
151 Image
*image
= ASSERT_PTR(userdata
);
152 Manager
*m
= ASSERT_PTR(image
->userdata
);
153 const char *new_name
;
159 if (m
->n_operations
>= OPERATIONS_MAX
)
160 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
162 r
= sd_bus_message_read(message
, "sb", &new_name
, &read_only
);
166 if (!image_name_is_valid(new_name
))
167 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
169 const char *details
[] = {
170 "image", image
->name
,
172 "new_name", new_name
,
176 r
= bus_verify_polkit_async(
178 "org.freedesktop.machine1.manage-images",
185 return 1; /* Will call us back */
187 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
188 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
190 r
= safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS
, &child
);
192 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
194 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
196 r
= image_clone(image
, new_name
, read_only
);
198 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
205 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
207 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
209 (void) sigkill_wait(child
);
213 errno_pipe_fd
[0] = -EBADF
;
218 int bus_image_method_mark_read_only(
219 sd_bus_message
*message
,
221 sd_bus_error
*error
) {
223 Image
*image
= userdata
;
224 Manager
*m
= image
->userdata
;
229 r
= sd_bus_message_read(message
, "b", &read_only
);
233 const char *details
[] = {
234 "image", image
->name
,
235 "verb", "mark_read_only",
236 "read_only", one_zero(read_only
),
240 r
= bus_verify_polkit_async(
242 "org.freedesktop.machine1.manage-images",
249 return 1; /* Will call us back */
251 r
= image_read_only(image
, read_only
);
255 return sd_bus_reply_method_return(message
, NULL
);
258 int bus_image_method_set_limit(
259 sd_bus_message
*message
,
261 sd_bus_error
*error
) {
263 Image
*image
= userdata
;
264 Manager
*m
= image
->userdata
;
270 r
= sd_bus_message_read(message
, "t", &limit
);
273 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
274 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
276 const char *details
[] = {
277 "machine", image
->name
,
282 r
= bus_verify_polkit_async(
284 "org.freedesktop.machine1.manage-images",
291 return 1; /* Will call us back */
293 r
= image_set_limit(image
, limit
);
297 return sd_bus_reply_method_return(message
, NULL
);
300 int bus_image_method_get_hostname(
301 sd_bus_message
*message
,
303 sd_bus_error
*error
) {
305 Image
*image
= userdata
;
308 if (!image
->metadata_valid
) {
309 r
= image_read_metadata(image
, &image_policy_container
);
311 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
314 return sd_bus_reply_method_return(message
, "s", image
->hostname
);
317 int bus_image_method_get_machine_id(
318 sd_bus_message
*message
,
320 sd_bus_error
*error
) {
322 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
323 Image
*image
= userdata
;
326 if (!image
->metadata_valid
) {
327 r
= image_read_metadata(image
, &image_policy_container
);
329 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
332 r
= sd_bus_message_new_method_return(message
, &reply
);
336 if (sd_id128_is_null(image
->machine_id
)) /* Add an empty array if the ID is zero */
337 r
= sd_bus_message_append(reply
, "ay", 0);
339 r
= sd_bus_message_append_array(reply
, 'y', image
->machine_id
.bytes
, 16);
343 return sd_bus_send(NULL
, reply
, NULL
);
346 int bus_image_method_get_machine_info(
347 sd_bus_message
*message
,
349 sd_bus_error
*error
) {
351 Image
*image
= userdata
;
354 if (!image
->metadata_valid
) {
355 r
= image_read_metadata(image
, &image_policy_container
);
357 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
360 return bus_reply_pair_array(message
, image
->machine_info
);
363 int bus_image_method_get_os_release(
364 sd_bus_message
*message
,
366 sd_bus_error
*error
) {
368 Image
*image
= userdata
;
371 if (!image
->metadata_valid
) {
372 r
= image_read_metadata(image
, &image_policy_container
);
374 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
377 return bus_reply_pair_array(message
, image
->os_release
);
380 static int image_flush_cache(sd_event_source
*s
, void *userdata
) {
381 Manager
*m
= ASSERT_PTR(userdata
);
385 hashmap_clear(m
->image_cache
);
389 int manager_acquire_image(Manager
*m
, const char *name
, Image
**ret
) {
395 Image
*existing
= hashmap_get(m
->image_cache
, name
);
402 if (!m
->image_cache_defer_event
) {
403 r
= sd_event_add_defer(m
->event
, &m
->image_cache_defer_event
, image_flush_cache
, m
);
407 r
= sd_event_source_set_priority(m
->image_cache_defer_event
, SD_EVENT_PRIORITY_IDLE
);
412 r
= sd_event_source_set_enabled(m
->image_cache_defer_event
, SD_EVENT_ONESHOT
);
416 _cleanup_(image_unrefp
) Image
*image
= NULL
;
417 r
= image_find(IMAGE_MACHINE
, name
, NULL
, &image
);
423 r
= hashmap_ensure_put(&m
->image_cache
, &image_hash_ops
, image
->name
, image
);
434 static int image_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
435 _cleanup_free_
char *e
= NULL
;
436 Manager
*m
= userdata
;
446 p
= startswith(path
, "/org/freedesktop/machine1/image/");
450 e
= bus_label_unescape(p
);
454 r
= manager_acquire_image(m
, e
, &image
);
464 char *image_bus_path(const char *name
) {
465 _cleanup_free_
char *e
= NULL
;
469 e
= bus_label_escape(name
);
473 return strjoin("/org/freedesktop/machine1/image/", e
);
476 static int image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
477 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
478 _cleanup_strv_free_
char **l
= NULL
;
486 images
= hashmap_new(&image_hash_ops
);
490 r
= image_discover(IMAGE_MACHINE
, NULL
, images
);
494 HASHMAP_FOREACH(image
, images
) {
497 p
= image_bus_path(image
->name
);
501 r
= strv_consume(&l
, p
);
506 *nodes
= TAKE_PTR(l
);
511 const sd_bus_vtable image_vtable
[] = {
512 SD_BUS_VTABLE_START(0),
513 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
514 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
515 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
516 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
517 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
518 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
519 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
520 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
521 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
522 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
523 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_image_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
524 SD_BUS_METHOD("Rename", "s", NULL
, bus_image_method_rename
, SD_BUS_VTABLE_UNPRIVILEGED
),
525 SD_BUS_METHOD("Clone", "sb", NULL
, bus_image_method_clone
, SD_BUS_VTABLE_UNPRIVILEGED
),
526 SD_BUS_METHOD("MarkReadOnly", "b", NULL
, bus_image_method_mark_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
527 SD_BUS_METHOD("SetLimit", "t", NULL
, bus_image_method_set_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
528 SD_BUS_METHOD("GetHostname", NULL
, "s", bus_image_method_get_hostname
, SD_BUS_VTABLE_UNPRIVILEGED
),
529 SD_BUS_METHOD("GetMachineID", NULL
, "ay", bus_image_method_get_machine_id
, SD_BUS_VTABLE_UNPRIVILEGED
),
530 SD_BUS_METHOD("GetMachineInfo", NULL
, "a{ss}", bus_image_method_get_machine_info
, SD_BUS_VTABLE_UNPRIVILEGED
),
531 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_image_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
535 const BusObjectImplementation image_object
= {
536 "/org/freedesktop/machine1/image",
537 "org.freedesktop.machine1.Image",
538 .fallback_vtables
= BUS_FALLBACK_VTABLES({image_vtable
, image_object_find
}),
539 .node_enumerator
= image_node_enumerator
,