2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <sys/mount.h>
22 #include "alloc-util.h"
23 #include "bus-label.h"
26 #include "dissect-image.h"
30 #include "image-dbus.h"
32 #include "loop-util.h"
33 #include "machine-image.h"
34 #include "mount-util.h"
35 #include "process-util.h"
36 #include "raw-clone.h"
38 #include "user-util.h"
40 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
42 int bus_image_method_remove(
43 sd_bus_message
*message
,
45 sd_bus_error
*error
) {
47 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
48 Image
*image
= userdata
;
49 Manager
*m
= image
->userdata
;
56 if (m
->n_operations
>= OPERATIONS_MAX
)
57 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
59 r
= bus_verify_polkit_async(
62 "org.freedesktop.machine1.manage-images",
71 return 1; /* Will call us back */
73 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
74 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
78 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
80 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
82 r
= image_remove(image
);
84 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
91 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
93 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
95 (void) sigkill_wait(child
);
99 errno_pipe_fd
[0] = -1;
104 int bus_image_method_rename(
105 sd_bus_message
*message
,
107 sd_bus_error
*error
) {
109 Image
*image
= userdata
;
110 Manager
*m
= image
->userdata
;
111 const char *new_name
;
117 r
= sd_bus_message_read(message
, "s", &new_name
);
121 if (!image_name_is_valid(new_name
))
122 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
124 r
= bus_verify_polkit_async(
127 "org.freedesktop.machine1.manage-images",
136 return 1; /* Will call us back */
138 r
= image_rename(image
, new_name
);
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] = { -1, -1 };
151 Image
*image
= userdata
;
152 Manager
*m
= image
->userdata
;
153 const char *new_name
;
161 if (m
->n_operations
>= OPERATIONS_MAX
)
162 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
164 r
= sd_bus_message_read(message
, "sb", &new_name
, &read_only
);
168 if (!image_name_is_valid(new_name
))
169 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image name '%s' is invalid.", new_name
);
171 r
= bus_verify_polkit_async(
174 "org.freedesktop.machine1.manage-images",
183 return 1; /* Will call us back */
185 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
186 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
190 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
192 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
194 r
= image_clone(image
, new_name
, read_only
);
196 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
203 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
205 r
= operation_new(m
, NULL
, child
, message
, errno_pipe_fd
[0], NULL
);
207 (void) sigkill_wait(child
);
211 errno_pipe_fd
[0] = -1;
216 int bus_image_method_mark_read_only(
217 sd_bus_message
*message
,
219 sd_bus_error
*error
) {
221 Image
*image
= userdata
;
222 Manager
*m
= image
->userdata
;
227 r
= sd_bus_message_read(message
, "b", &read_only
);
231 r
= bus_verify_polkit_async(
234 "org.freedesktop.machine1.manage-images",
243 return 1; /* Will call us back */
245 r
= image_read_only(image
, read_only
);
249 return sd_bus_reply_method_return(message
, NULL
);
252 int bus_image_method_set_limit(
253 sd_bus_message
*message
,
255 sd_bus_error
*error
) {
257 Image
*image
= userdata
;
258 Manager
*m
= image
->userdata
;
264 r
= sd_bus_message_read(message
, "t", &limit
);
267 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
268 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
270 r
= bus_verify_polkit_async(
273 "org.freedesktop.machine1.manage-images",
282 return 1; /* Will call us back */
284 r
= image_set_limit(image
, limit
);
288 return sd_bus_reply_method_return(message
, NULL
);
291 #define EXIT_NOT_FOUND 2
293 static int directory_image_get_os_release(Image
*image
, char ***ret
, sd_bus_error
*error
) {
295 _cleanup_free_
char *path
= NULL
;
296 _cleanup_close_
int fd
= -1;
302 r
= chase_symlinks("/etc/os-release", image
->path
, CHASE_PREFIX_ROOT
, &path
);
304 r
= chase_symlinks("/usr/lib/os-release", image
->path
, CHASE_PREFIX_ROOT
, &path
);
306 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Image does not contain OS release information");
308 return sd_bus_error_set_errnof(error
, r
, "Failed to resolve %s: %m", image
->path
);
310 r
= load_env_file_pairs(NULL
, path
, NULL
, ret
);
312 return sd_bus_error_set_errnof(error
, r
, "Failed to open %s: %m", path
);
317 static int raw_image_get_os_release(Image
*image
, char ***ret
, sd_bus_error
*error
) {
318 _cleanup_(rmdir_and_freep
) char *t
= NULL
;
319 _cleanup_(loop_device_unrefp
) LoopDevice
*d
= NULL
;
320 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
321 _cleanup_(sigkill_waitp
) pid_t child
= 0;
322 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
323 _cleanup_fclose_
FILE *f
= NULL
;
324 _cleanup_strv_free_
char **v
= NULL
;
331 r
= mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t
);
333 return sd_bus_error_set_errnof(error
, r
, "Failed to create temporary directory: %m");
335 r
= loop_device_make_by_path(image
->path
, O_RDONLY
, &d
);
337 return sd_bus_error_set_errnof(error
, r
, "Failed to set up loop block device for %s: %m", image
->path
);
339 r
= dissect_image(d
->fd
, NULL
, 0, DISSECT_IMAGE_REQUIRE_ROOT
, &m
);
341 return sd_bus_error_set_errnof(error
, r
, "Disk image %s not understood: %m", image
->path
);
343 return sd_bus_error_set_errnof(error
, r
, "Failed to dissect image %s: %m", image
->path
);
345 if (pipe2(pair
, O_CLOEXEC
) < 0)
346 return sd_bus_error_set_errnof(error
, errno
, "Failed to create communication pipe: %m");
348 child
= raw_clone(SIGCHLD
|CLONE_NEWNS
);
350 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
355 pair
[0] = safe_close(pair
[0]);
357 /* Make sure we never propagate to the host */
358 if (mount(NULL
, "/", NULL
, MS_SLAVE
| MS_REC
, NULL
) < 0)
361 r
= dissected_image_mount(m
, t
, DISSECT_IMAGE_READ_ONLY
);
365 r
= mount_move_root(t
);
369 fd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
370 if (fd
< 0 && errno
== ENOENT
) {
371 fd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
372 if (fd
< 0 && errno
== ENOENT
)
373 _exit(EXIT_NOT_FOUND
);
378 r
= copy_bytes(fd
, pair
[1], (uint64_t) -1, false);
385 pair
[1] = safe_close(pair
[1]);
387 f
= fdopen(pair
[0], "re");
393 r
= load_env_file_pairs(f
, "os-release", NULL
, &v
);
397 r
= wait_for_terminate(child
, &si
);
399 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
401 if (si
.si_code
== CLD_EXITED
&& si
.si_status
== EXIT_NOT_FOUND
)
402 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Image does not contain OS release information");
403 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
404 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
412 int bus_image_method_get_os_release(
413 sd_bus_message
*message
,
415 sd_bus_error
*error
) {
417 _cleanup_release_lock_file_ LockFile tree_global_lock
= LOCK_FILE_INIT
, tree_local_lock
= LOCK_FILE_INIT
;
418 _cleanup_strv_free_
char **v
= NULL
;
419 Image
*image
= userdata
;
422 r
= image_path_lock(image
->path
, LOCK_SH
|LOCK_NB
, &tree_global_lock
, &tree_local_lock
);
424 return sd_bus_error_set_errnof(error
, r
, "Failed to lock image: %m");
426 switch (image
->type
) {
428 case IMAGE_DIRECTORY
:
429 case IMAGE_SUBVOLUME
:
430 r
= directory_image_get_os_release(image
, &v
, error
);
434 r
= raw_image_get_os_release(image
, &v
, error
);
438 assert_not_reached("Unknown image type");
443 return bus_reply_pair_array(message
, v
);
446 const sd_bus_vtable image_vtable
[] = {
447 SD_BUS_VTABLE_START(0),
448 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
449 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
450 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
451 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
452 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
453 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
454 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
455 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
456 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
457 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
458 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_image_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
459 SD_BUS_METHOD("Rename", "s", NULL
, bus_image_method_rename
, SD_BUS_VTABLE_UNPRIVILEGED
),
460 SD_BUS_METHOD("Clone", "sb", NULL
, bus_image_method_clone
, SD_BUS_VTABLE_UNPRIVILEGED
),
461 SD_BUS_METHOD("MarkReadOnly", "b", NULL
, bus_image_method_mark_read_only
, SD_BUS_VTABLE_UNPRIVILEGED
),
462 SD_BUS_METHOD("SetLimit", "t", NULL
, bus_image_method_set_limit
, SD_BUS_VTABLE_UNPRIVILEGED
),
463 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_image_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
467 static int image_flush_cache(sd_event_source
*s
, void *userdata
) {
468 Manager
*m
= userdata
;
474 while ((i
= hashmap_steal_first(m
->image_cache
)))
480 int image_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
481 _cleanup_free_
char *e
= NULL
;
482 Manager
*m
= userdata
;
492 p
= startswith(path
, "/org/freedesktop/machine1/image/");
496 e
= bus_label_unescape(p
);
500 image
= hashmap_get(m
->image_cache
, e
);
506 r
= hashmap_ensure_allocated(&m
->image_cache
, &string_hash_ops
);
510 if (!m
->image_cache_defer_event
) {
511 r
= sd_event_add_defer(m
->event
, &m
->image_cache_defer_event
, image_flush_cache
, m
);
515 r
= sd_event_source_set_priority(m
->image_cache_defer_event
, SD_EVENT_PRIORITY_IDLE
);
520 r
= sd_event_source_set_enabled(m
->image_cache_defer_event
, SD_EVENT_ONESHOT
);
524 r
= image_find(e
, &image
);
530 r
= hashmap_put(m
->image_cache
, image
->name
, image
);
540 char *image_bus_path(const char *name
) {
541 _cleanup_free_
char *e
= NULL
;
545 e
= bus_label_escape(name
);
549 return strappend("/org/freedesktop/machine1/image/", e
);
552 int image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
553 _cleanup_(image_hashmap_freep
) Hashmap
*images
= NULL
;
554 _cleanup_strv_free_
char **l
= NULL
;
563 images
= hashmap_new(&string_hash_ops
);
567 r
= image_discover(images
);
571 HASHMAP_FOREACH(image
, images
, i
) {
574 p
= image_bus_path(image
->name
);
578 r
= strv_consume(&l
, p
);