1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
8 #include "bus-get-properties.h"
10 #include "bus-object.h"
11 #include "bus-polkit.h"
13 #include "discover-image.h"
16 #include "image-dbus.h"
17 #include "image-policy.h"
20 #include "operation.h"
22 #include "process-util.h"
25 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
27 int bus_image_method_remove(
28 sd_bus_message
*message
,
30 sd_bus_error
*error
) {
32 _cleanup_close_pair_
int errno_pipe_fd
[2] = EBADF_PAIR
;
33 Image
*image
= ASSERT_PTR(userdata
);
34 Manager
*m
= image
->userdata
;
40 if (m
->n_operations
>= OPERATIONS_MAX
)
41 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
43 const char *details
[] = {
49 r
= bus_verify_polkit_async(
51 "org.freedesktop.machine1.manage-images",
58 return 1; /* Will call us back */
60 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
61 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
63 r
= safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS
, &child
);
65 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
67 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
68 r
= image_remove(image
);
69 report_errno_and_exit(errno_pipe_fd
[1], r
);
72 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
74 r
= operation_new_with_bus_reply(m
, /* machine= */ NULL
, child
, message
, errno_pipe_fd
[0], /* ret= */ NULL
);
76 (void) sigkill_wait(child
);
80 errno_pipe_fd
[0] = -EBADF
;
85 int bus_image_method_rename(
86 sd_bus_message
*message
,
88 sd_bus_error
*error
) {
90 Image
*image
= ASSERT_PTR(userdata
);
91 Manager
*m
= image
->userdata
;
97 r
= sd_bus_message_read(message
, "s", &new_name
);
101 if (!image_name_is_valid(new_name
))
102 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
104 const char *details
[] = {
105 "image", image
->name
,
107 "new_name", new_name
,
111 r
= bus_verify_polkit_async(
113 "org.freedesktop.machine1.manage-images",
120 return 1; /* Will call us back */
122 r
= rename_image_and_update_cache(m
, image
, new_name
);
124 return sd_bus_error_set_errnof(error
, r
, "Failed to rename image: %m");
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] = EBADF_PAIR
;
135 Image
*image
= ASSERT_PTR(userdata
);
136 Manager
*m
= ASSERT_PTR(image
->userdata
);
137 const char *new_name
;
143 if (m
->n_operations
>= OPERATIONS_MAX
)
144 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
146 r
= sd_bus_message_read(message
, "sb", &new_name
, &read_only
);
150 if (!image_name_is_valid(new_name
))
151 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
153 const char *details
[] = {
154 "image", image
->name
,
156 "new_name", new_name
,
160 r
= bus_verify_polkit_async(
162 "org.freedesktop.machine1.manage-images",
169 return 1; /* Will call us back */
171 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
172 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
174 r
= safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS
, &child
);
176 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
178 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
179 r
= image_clone(image
, new_name
, read_only
, m
->runtime_scope
);
180 report_errno_and_exit(errno_pipe_fd
[1], r
);
183 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
185 r
= operation_new_with_bus_reply(m
, /* machine= */ NULL
, child
, message
, errno_pipe_fd
[0], /* ret= */ NULL
);
187 (void) sigkill_wait(child
);
191 errno_pipe_fd
[0] = -EBADF
;
196 int bus_image_method_mark_read_only(
197 sd_bus_message
*message
,
199 sd_bus_error
*error
) {
201 Image
*image
= userdata
;
202 Manager
*m
= image
->userdata
;
207 r
= sd_bus_message_read(message
, "b", &read_only
);
211 const char *details
[] = {
212 "image", image
->name
,
213 "verb", "mark_read_only",
214 "read_only", one_zero(read_only
),
218 r
= bus_verify_polkit_async(
220 "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_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
254 const char *details
[] = {
255 "machine", image
->name
,
260 r
= bus_verify_polkit_async(
262 "org.freedesktop.machine1.manage-images",
269 return 1; /* Will call us back */
271 r
= image_set_limit(image
, limit
);
275 return sd_bus_reply_method_return(message
, NULL
);
278 int bus_image_method_get_hostname(
279 sd_bus_message
*message
,
281 sd_bus_error
*error
) {
283 Image
*image
= userdata
;
286 if (!image
->metadata_valid
) {
287 r
= image_read_metadata(image
, &image_policy_container
);
289 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
292 return sd_bus_reply_method_return(message
, "s", image
->hostname
);
295 int bus_image_method_get_machine_id(
296 sd_bus_message
*message
,
298 sd_bus_error
*error
) {
300 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
301 Image
*image
= userdata
;
304 if (!image
->metadata_valid
) {
305 r
= image_read_metadata(image
, &image_policy_container
);
307 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
310 r
= sd_bus_message_new_method_return(message
, &reply
);
314 if (sd_id128_is_null(image
->machine_id
)) /* Add an empty array if the ID is zero */
315 r
= sd_bus_message_append(reply
, "ay", 0);
317 r
= sd_bus_message_append_array(reply
, 'y', image
->machine_id
.bytes
, 16);
321 return sd_bus_message_send(reply
);
324 int bus_image_method_get_machine_info(
325 sd_bus_message
*message
,
327 sd_bus_error
*error
) {
329 Image
*image
= userdata
;
332 if (!image
->metadata_valid
) {
333 r
= image_read_metadata(image
, &image_policy_container
);
335 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
338 return bus_reply_pair_array(message
, image
->machine_info
);
341 int bus_image_method_get_os_release(
342 sd_bus_message
*message
,
344 sd_bus_error
*error
) {
346 Image
*image
= userdata
;
349 if (!image
->metadata_valid
) {
350 r
= image_read_metadata(image
, &image_policy_container
);
352 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
355 return bus_reply_pair_array(message
, image
->os_release
);
358 static int image_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
359 _cleanup_free_
char *e
= NULL
;
360 Manager
*m
= userdata
;
370 p
= startswith(path
, "/org/freedesktop/machine1/image/");
374 e
= bus_label_unescape(p
);
378 r
= manager_acquire_image(m
, e
, &image
);
388 char* image_bus_path(const char *name
) {
389 _cleanup_free_
char *e
= NULL
;
393 e
= bus_label_escape(name
);
397 return strjoin("/org/freedesktop/machine1/image/", e
);
400 static int image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
401 Manager
*m
= ASSERT_PTR(userdata
);
408 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
409 r
= image_discover(m
->runtime_scope
, IMAGE_MACHINE
, NULL
, &images
);
413 _cleanup_strv_free_
char **l
= NULL
;
415 HASHMAP_FOREACH(image
, images
) {
418 p
= image_bus_path(image
->name
);
422 r
= strv_consume(&l
, p
);
427 *nodes
= TAKE_PTR(l
);
432 const sd_bus_vtable image_vtable
[] = {
433 SD_BUS_VTABLE_START(0),
434 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
435 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
436 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
437 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
438 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
439 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
440 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
441 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
442 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
443 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
444 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_image_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
445 SD_BUS_METHOD("Rename", "s", NULL
, bus_image_method_rename
, SD_BUS_VTABLE_UNPRIVILEGED
),
446 SD_BUS_METHOD("Clone", "sb", NULL
, bus_image_method_clone
, SD_BUS_VTABLE_UNPRIVILEGED
),
447 SD_BUS_METHOD("MarkReadOnly", "b", NULL
, bus_image_method_mark_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
448 SD_BUS_METHOD("SetLimit", "t", NULL
, bus_image_method_set_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
449 SD_BUS_METHOD("GetHostname", NULL
, "s", bus_image_method_get_hostname
, SD_BUS_VTABLE_UNPRIVILEGED
),
450 SD_BUS_METHOD("GetMachineID", NULL
, "ay", bus_image_method_get_machine_id
, SD_BUS_VTABLE_UNPRIVILEGED
),
451 SD_BUS_METHOD("GetMachineInfo", NULL
, "a{ss}", bus_image_method_get_machine_info
, SD_BUS_VTABLE_UNPRIVILEGED
),
452 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_image_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
456 const BusObjectImplementation image_object
= {
457 "/org/freedesktop/machine1/image",
458 "org.freedesktop.machine1.Image",
459 .fallback_vtables
= BUS_FALLBACK_VTABLES({image_vtable
, image_object_find
}),
460 .node_enumerator
= image_node_enumerator
,