1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include "alloc-util.h"
10 #include "dissect-image.h"
14 #include "image-dbus.h"
16 #include "loop-util.h"
17 #include "machine-image.h"
18 #include "mount-util.h"
19 #include "process-util.h"
20 #include "raw-clone.h"
22 #include "user-util.h"
24 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
26 int bus_image_method_remove(
27 sd_bus_message
*message
,
29 sd_bus_error
*error
) {
31 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
32 Image
*image
= userdata
;
33 Manager
*m
= image
->userdata
;
40 if (m
->n_operations
>= OPERATIONS_MAX
)
41 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
43 r
= bus_verify_polkit_async(
46 "org.freedesktop.machine1.manage-images",
55 return 1; /* Will call us back */
57 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
58 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
60 r
= safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS
, &child
);
62 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
64 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
66 r
= image_remove(image
);
68 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
75 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
77 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
79 (void) sigkill_wait(child
);
83 errno_pipe_fd
[0] = -1;
88 int bus_image_method_rename(
89 sd_bus_message
*message
,
91 sd_bus_error
*error
) {
93 Image
*image
= userdata
;
94 Manager
*m
= image
->userdata
;
101 r
= sd_bus_message_read(message
, "s", &new_name
);
105 if (!image_name_is_valid(new_name
))
106 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
108 r
= bus_verify_polkit_async(
111 "org.freedesktop.machine1.manage-images",
120 return 1; /* Will call us back */
122 r
= image_rename(image
, new_name
);
126 return sd_bus_reply_method_return(message
, NULL
);
129 int bus_image_method_clone(
130 sd_bus_message
*message
,
132 sd_bus_error
*error
) {
134 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
135 Image
*image
= userdata
;
136 Manager
*m
= image
->userdata
;
137 const char *new_name
;
145 if (m
->n_operations
>= OPERATIONS_MAX
)
146 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
148 r
= sd_bus_message_read(message
, "sb", &new_name
, &read_only
);
152 if (!image_name_is_valid(new_name
))
153 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
155 r
= bus_verify_polkit_async(
158 "org.freedesktop.machine1.manage-images",
167 return 1; /* Will call us back */
169 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
170 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
172 r
= safe_fork("(imgclone)", FORK_RESET_SIGNALS
, &child
);
174 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
176 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
178 r
= image_clone(image
, new_name
, read_only
);
180 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
187 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
189 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
191 (void) sigkill_wait(child
);
195 errno_pipe_fd
[0] = -1;
200 int bus_image_method_mark_read_only(
201 sd_bus_message
*message
,
203 sd_bus_error
*error
) {
205 Image
*image
= userdata
;
206 Manager
*m
= image
->userdata
;
211 r
= sd_bus_message_read(message
, "b", &read_only
);
215 r
= bus_verify_polkit_async(
218 "org.freedesktop.machine1.manage-images",
227 return 1; /* Will call us back */
229 r
= image_read_only(image
, read_only
);
233 return sd_bus_reply_method_return(message
, NULL
);
236 int bus_image_method_set_limit(
237 sd_bus_message
*message
,
239 sd_bus_error
*error
) {
241 Image
*image
= userdata
;
242 Manager
*m
= image
->userdata
;
248 r
= sd_bus_message_read(message
, "t", &limit
);
251 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
252 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
254 r
= bus_verify_polkit_async(
257 "org.freedesktop.machine1.manage-images",
266 return 1; /* Will call us back */
268 r
= image_set_limit(image
, limit
);
272 return sd_bus_reply_method_return(message
, NULL
);
275 int bus_image_method_get_hostname(
276 sd_bus_message
*message
,
278 sd_bus_error
*error
) {
280 Image
*image
= userdata
;
283 if (!image
->metadata_valid
) {
284 r
= image_read_metadata(image
);
286 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
289 return sd_bus_reply_method_return(message
, "s", image
->hostname
);
292 int bus_image_method_get_machine_id(
293 sd_bus_message
*message
,
295 sd_bus_error
*error
) {
297 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
298 Image
*image
= userdata
;
301 if (!image
->metadata_valid
) {
302 r
= image_read_metadata(image
);
304 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
307 r
= sd_bus_message_new_method_return(message
, &reply
);
311 if (sd_id128_is_null(image
->machine_id
)) /* Add an empty array if the ID is zero */
312 r
= sd_bus_message_append(reply
, "ay", 0);
314 r
= sd_bus_message_append_array(reply
, 'y', image
->machine_id
.bytes
, 16);
318 return sd_bus_send(NULL
, reply
, NULL
);
321 int bus_image_method_get_machine_info(
322 sd_bus_message
*message
,
324 sd_bus_error
*error
) {
326 Image
*image
= userdata
;
329 if (!image
->metadata_valid
) {
330 r
= image_read_metadata(image
);
332 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
335 return bus_reply_pair_array(message
, image
->machine_info
);
338 int bus_image_method_get_os_release(
339 sd_bus_message
*message
,
341 sd_bus_error
*error
) {
343 Image
*image
= userdata
;
346 if (!image
->metadata_valid
) {
347 r
= image_read_metadata(image
);
349 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
352 return bus_reply_pair_array(message
, image
->os_release
);
355 const sd_bus_vtable image_vtable
[] = {
356 SD_BUS_VTABLE_START(0),
357 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
358 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
359 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
360 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
361 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
362 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
363 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
364 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
365 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
366 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
367 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_image_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
368 SD_BUS_METHOD("Rename", "s", NULL
, bus_image_method_rename
, SD_BUS_VTABLE_UNPRIVILEGED
),
369 SD_BUS_METHOD("Clone", "sb", NULL
, bus_image_method_clone
, SD_BUS_VTABLE_UNPRIVILEGED
),
370 SD_BUS_METHOD("MarkReadOnly", "b", NULL
, bus_image_method_mark_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
371 SD_BUS_METHOD("SetLimit", "t", NULL
, bus_image_method_set_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
372 SD_BUS_METHOD("GetHostname", NULL
, "s", bus_image_method_get_hostname
, SD_BUS_VTABLE_UNPRIVILEGED
),
373 SD_BUS_METHOD("GetMachineID", NULL
, "ay", bus_image_method_get_machine_id
, SD_BUS_VTABLE_UNPRIVILEGED
),
374 SD_BUS_METHOD("GetMachineInfo", NULL
, "a{ss}", bus_image_method_get_machine_info
, SD_BUS_VTABLE_UNPRIVILEGED
),
375 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_image_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
379 static int image_flush_cache(sd_event_source
*s
, void *userdata
) {
380 Manager
*m
= userdata
;
385 hashmap_clear_with_destructor(m
->image_cache
, image_unref
);
389 int image_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
390 _cleanup_free_
char *e
= NULL
;
391 Manager
*m
= userdata
;
401 p
= startswith(path
, "/org/freedesktop/machine1/image/");
405 e
= bus_label_unescape(p
);
409 image
= hashmap_get(m
->image_cache
, e
);
415 r
= hashmap_ensure_allocated(&m
->image_cache
, &string_hash_ops
);
419 if (!m
->image_cache_defer_event
) {
420 r
= sd_event_add_defer(m
->event
, &m
->image_cache_defer_event
, image_flush_cache
, m
);
424 r
= sd_event_source_set_priority(m
->image_cache_defer_event
, SD_EVENT_PRIORITY_IDLE
);
429 r
= sd_event_source_set_enabled(m
->image_cache_defer_event
, SD_EVENT_ONESHOT
);
433 r
= image_find(IMAGE_MACHINE
, e
, &image
);
441 r
= hashmap_put(m
->image_cache
, image
->name
, image
);
451 char *image_bus_path(const char *name
) {
452 _cleanup_free_
char *e
= NULL
;
456 e
= bus_label_escape(name
);
460 return strappend("/org/freedesktop/machine1/image/", e
);
463 int image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
464 _cleanup_(image_hashmap_freep
) Hashmap
*images
= NULL
;
465 _cleanup_strv_free_
char **l
= NULL
;
474 images
= hashmap_new(&string_hash_ops
);
478 r
= image_discover(IMAGE_MACHINE
, images
);
482 HASHMAP_FOREACH(image
, images
, i
) {
485 p
= image_bus_path(image
->name
);
489 r
= strv_consume(&l
, p
);
494 *nodes
= TAKE_PTR(l
);