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 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 return sd_bus_reply_method_return(message
, "s", image
->hostname
);
310 int bus_image_method_get_machine_id(
311 sd_bus_message
*message
,
313 sd_bus_error
*error
) {
315 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
316 Image
*image
= userdata
;
319 if (!image
->metadata_valid
) {
320 r
= image_read_metadata(image
);
322 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
325 r
= sd_bus_message_new_method_return(message
, &reply
);
329 if (sd_id128_is_null(image
->machine_id
)) /* Add an empty array if the ID is zero */
330 r
= sd_bus_message_append(reply
, "ay", 0);
332 r
= sd_bus_message_append_array(reply
, 'y', image
->machine_id
.bytes
, 16);
336 return sd_bus_send(NULL
, reply
, NULL
);
339 int bus_image_method_get_machine_info(
340 sd_bus_message
*message
,
342 sd_bus_error
*error
) {
344 Image
*image
= userdata
;
347 if (!image
->metadata_valid
) {
348 r
= image_read_metadata(image
);
350 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
353 return bus_reply_pair_array(message
, image
->machine_info
);
356 int bus_image_method_get_os_release(
357 sd_bus_message
*message
,
359 sd_bus_error
*error
) {
361 Image
*image
= userdata
;
364 if (!image
->metadata_valid
) {
365 r
= image_read_metadata(image
);
367 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
370 return bus_reply_pair_array(message
, image
->os_release
);
373 const sd_bus_vtable image_vtable
[] = {
374 SD_BUS_VTABLE_START(0),
375 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
376 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
377 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
378 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
379 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
380 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
381 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
382 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
383 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
384 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
385 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_image_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
386 SD_BUS_METHOD("Rename", "s", NULL
, bus_image_method_rename
, SD_BUS_VTABLE_UNPRIVILEGED
),
387 SD_BUS_METHOD("Clone", "sb", NULL
, bus_image_method_clone
, SD_BUS_VTABLE_UNPRIVILEGED
),
388 SD_BUS_METHOD("MarkReadOnly", "b", NULL
, bus_image_method_mark_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
389 SD_BUS_METHOD("SetLimit", "t", NULL
, bus_image_method_set_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
390 SD_BUS_METHOD("GetHostname", NULL
, "s", bus_image_method_get_hostname
, SD_BUS_VTABLE_UNPRIVILEGED
),
391 SD_BUS_METHOD("GetMachineID", NULL
, "ay", bus_image_method_get_machine_id
, SD_BUS_VTABLE_UNPRIVILEGED
),
392 SD_BUS_METHOD("GetMachineInfo", NULL
, "a{ss}", bus_image_method_get_machine_info
, SD_BUS_VTABLE_UNPRIVILEGED
),
393 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_image_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
397 static int image_flush_cache(sd_event_source
*s
, void *userdata
) {
398 Manager
*m
= userdata
;
403 hashmap_clear_with_destructor(m
->image_cache
, image_unref
);
407 int image_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
408 _cleanup_free_
char *e
= NULL
;
409 Manager
*m
= userdata
;
419 p
= startswith(path
, "/org/freedesktop/machine1/image/");
423 e
= bus_label_unescape(p
);
427 image
= hashmap_get(m
->image_cache
, e
);
433 r
= hashmap_ensure_allocated(&m
->image_cache
, &string_hash_ops
);
437 if (!m
->image_cache_defer_event
) {
438 r
= sd_event_add_defer(m
->event
, &m
->image_cache_defer_event
, image_flush_cache
, m
);
442 r
= sd_event_source_set_priority(m
->image_cache_defer_event
, SD_EVENT_PRIORITY_IDLE
);
447 r
= sd_event_source_set_enabled(m
->image_cache_defer_event
, SD_EVENT_ONESHOT
);
451 r
= image_find(e
, &image
);
457 r
= hashmap_put(m
->image_cache
, image
->name
, image
);
467 char *image_bus_path(const char *name
) {
468 _cleanup_free_
char *e
= NULL
;
472 e
= bus_label_escape(name
);
476 return strappend("/org/freedesktop/machine1/image/", e
);
479 int image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
480 _cleanup_(image_hashmap_freep
) Hashmap
*images
= NULL
;
481 _cleanup_strv_free_
char **l
= NULL
;
490 images
= hashmap_new(&string_hash_ops
);
494 r
= image_discover(images
);
498 HASHMAP_FOREACH(image
, images
, i
) {
501 p
= image_bus_path(image
->name
);
505 r
= strv_consume(&l
, p
);