1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/mount.h>
24 #include "alloc-util.h"
25 #include "bus-label.h"
28 #include "dissect-image.h"
32 #include "image-dbus.h"
34 #include "loop-util.h"
35 #include "machine-image.h"
36 #include "mount-util.h"
37 #include "process-util.h"
38 #include "raw-clone.h"
40 #include "user-util.h"
42 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
44 int bus_image_method_remove(
45 sd_bus_message
*message
,
47 sd_bus_error
*error
) {
49 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
50 Image
*image
= userdata
;
51 Manager
*m
= image
->userdata
;
58 if (m
->n_operations
>= OPERATIONS_MAX
)
59 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
61 r
= bus_verify_polkit_async(
64 "org.freedesktop.machine1.manage-images",
73 return 1; /* Will call us back */
75 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
76 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
80 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
82 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
84 r
= image_remove(image
);
86 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
93 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
95 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
97 (void) sigkill_wait(child
);
101 errno_pipe_fd
[0] = -1;
106 int bus_image_method_rename(
107 sd_bus_message
*message
,
109 sd_bus_error
*error
) {
111 Image
*image
= userdata
;
112 Manager
*m
= image
->userdata
;
113 const char *new_name
;
119 r
= sd_bus_message_read(message
, "s", &new_name
);
123 if (!image_name_is_valid(new_name
))
124 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
126 r
= bus_verify_polkit_async(
129 "org.freedesktop.machine1.manage-images",
138 return 1; /* Will call us back */
140 r
= image_rename(image
, new_name
);
144 return sd_bus_reply_method_return(message
, NULL
);
147 int bus_image_method_clone(
148 sd_bus_message
*message
,
150 sd_bus_error
*error
) {
152 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
153 Image
*image
= userdata
;
154 Manager
*m
= image
->userdata
;
155 const char *new_name
;
163 if (m
->n_operations
>= OPERATIONS_MAX
)
164 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
166 r
= sd_bus_message_read(message
, "sb", &new_name
, &read_only
);
170 if (!image_name_is_valid(new_name
))
171 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
173 r
= bus_verify_polkit_async(
176 "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");
192 return sd_bus_error_set_errnof(error
, errno
, "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] = -1;
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 r
= bus_verify_polkit_async(
236 "org.freedesktop.machine1.manage-images",
245 return 1; /* Will call us back */
247 r
= image_read_only(image
, read_only
);
251 return sd_bus_reply_method_return(message
, NULL
);
254 int bus_image_method_set_limit(
255 sd_bus_message
*message
,
257 sd_bus_error
*error
) {
259 Image
*image
= userdata
;
260 Manager
*m
= image
->userdata
;
266 r
= sd_bus_message_read(message
, "t", &limit
);
269 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
270 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
272 r
= bus_verify_polkit_async(
275 "org.freedesktop.machine1.manage-images",
284 return 1; /* Will call us back */
286 r
= image_set_limit(image
, limit
);
290 return sd_bus_reply_method_return(message
, NULL
);
293 int bus_image_method_get_hostname(
294 sd_bus_message
*message
,
296 sd_bus_error
*error
) {
298 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
299 Image
*image
= userdata
;
302 if (!image
->metadata_valid
) {
303 r
= image_read_metadata(image
);
305 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
308 return sd_bus_reply_method_return(message
, "s", image
->hostname
);
311 int bus_image_method_get_machine_id(
312 sd_bus_message
*message
,
314 sd_bus_error
*error
) {
316 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
317 Image
*image
= userdata
;
320 if (!image
->metadata_valid
) {
321 r
= image_read_metadata(image
);
323 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
326 r
= sd_bus_message_new_method_return(message
, &reply
);
330 if (sd_id128_is_null(image
->machine_id
)) /* Add an empty array if the ID is zero */
331 r
= sd_bus_message_append(reply
, "ay", 0);
333 r
= sd_bus_message_append_array(reply
, 'y', image
->machine_id
.bytes
, 16);
337 return sd_bus_send(NULL
, reply
, NULL
);
340 int bus_image_method_get_machine_info(
341 sd_bus_message
*message
,
343 sd_bus_error
*error
) {
345 Image
*image
= userdata
;
348 if (!image
->metadata_valid
) {
349 r
= image_read_metadata(image
);
351 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
354 return bus_reply_pair_array(message
, image
->machine_info
);
357 int bus_image_method_get_os_release(
358 sd_bus_message
*message
,
360 sd_bus_error
*error
) {
362 Image
*image
= userdata
;
365 if (!image
->metadata_valid
) {
366 r
= image_read_metadata(image
);
368 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
371 return bus_reply_pair_array(message
, image
->os_release
);
374 const sd_bus_vtable image_vtable
[] = {
375 SD_BUS_VTABLE_START(0),
376 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
377 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
378 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
379 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
380 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
381 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
382 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
383 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
384 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
385 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
386 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_image_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
387 SD_BUS_METHOD("Rename", "s", NULL
, bus_image_method_rename
, SD_BUS_VTABLE_UNPRIVILEGED
),
388 SD_BUS_METHOD("Clone", "sb", NULL
, bus_image_method_clone
, SD_BUS_VTABLE_UNPRIVILEGED
),
389 SD_BUS_METHOD("MarkReadOnly", "b", NULL
, bus_image_method_mark_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
390 SD_BUS_METHOD("SetLimit", "t", NULL
, bus_image_method_set_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
391 SD_BUS_METHOD("GetHostname", NULL
, "s", bus_image_method_get_hostname
, SD_BUS_VTABLE_UNPRIVILEGED
),
392 SD_BUS_METHOD("GetMachineID", NULL
, "ay", bus_image_method_get_machine_id
, SD_BUS_VTABLE_UNPRIVILEGED
),
393 SD_BUS_METHOD("GetMachineInfo", NULL
, "a{ss}", bus_image_method_get_machine_info
, SD_BUS_VTABLE_UNPRIVILEGED
),
394 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_image_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
398 static int image_flush_cache(sd_event_source
*s
, void *userdata
) {
399 Manager
*m
= userdata
;
405 while ((i
= hashmap_steal_first(m
->image_cache
)))
411 int image_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
412 _cleanup_free_
char *e
= NULL
;
413 Manager
*m
= userdata
;
423 p
= startswith(path
, "/org/freedesktop/machine1/image/");
427 e
= bus_label_unescape(p
);
431 image
= hashmap_get(m
->image_cache
, e
);
437 r
= hashmap_ensure_allocated(&m
->image_cache
, &string_hash_ops
);
441 if (!m
->image_cache_defer_event
) {
442 r
= sd_event_add_defer(m
->event
, &m
->image_cache_defer_event
, image_flush_cache
, m
);
446 r
= sd_event_source_set_priority(m
->image_cache_defer_event
, SD_EVENT_PRIORITY_IDLE
);
451 r
= sd_event_source_set_enabled(m
->image_cache_defer_event
, SD_EVENT_ONESHOT
);
455 r
= image_find(e
, &image
);
461 r
= hashmap_put(m
->image_cache
, image
->name
, image
);
471 char *image_bus_path(const char *name
) {
472 _cleanup_free_
char *e
= NULL
;
476 e
= bus_label_escape(name
);
480 return strappend("/org/freedesktop/machine1/image/", e
);
483 int image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
484 _cleanup_(image_hashmap_freep
) Hashmap
*images
= NULL
;
485 _cleanup_strv_free_
char **l
= NULL
;
494 images
= hashmap_new(&string_hash_ops
);
498 r
= image_discover(images
);
502 HASHMAP_FOREACH(image
, images
, i
) {
505 p
= image_bus_path(image
->name
);
509 r
= strv_consume(&l
, p
);