]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image-dbus.c
Merge pull request #32942 from yuwata/test-journal-sync-more
[thirdparty/systemd.git] / src / machine / image-dbus.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
ebeccf9e 2
fe993888 3#include <sys/file.h>
9153b02b
LP
4#include <sys/mount.h>
5
b5efdb8a 6#include "alloc-util.h"
40af3d02 7#include "bus-get-properties.h"
ebeccf9e 8#include "bus-label.h"
269e4d2d 9#include "bus-polkit.h"
9153b02b 10#include "copy.h"
57f1b61b 11#include "discover-image.h"
9153b02b 12#include "dissect-image.h"
56599585 13#include "fd-util.h"
9153b02b
LP
14#include "fileio.h"
15#include "fs-util.h"
003dffde 16#include "image-dbus.h"
a90fb858 17#include "io-util.h"
9153b02b 18#include "loop-util.h"
204f52e3 19#include "missing_capability.h"
9153b02b 20#include "mount-util.h"
6ef06723 21#include "os-util.h"
56599585 22#include "process-util.h"
9153b02b 23#include "raw-clone.h"
ee104e11
LP
24#include "strv.h"
25#include "user-util.h"
ebeccf9e 26
1ddb263d 27static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
ebeccf9e 28
1ddb263d 29int bus_image_method_remove(
08682124
LP
30 sd_bus_message *message,
31 void *userdata,
32 sd_bus_error *error) {
33
71136404 34 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
99534007 35 Image *image = ASSERT_PTR(userdata);
70244d1d 36 Manager *m = image->userdata;
5d2036b5 37 pid_t child;
08682124
LP
38 int r;
39
08682124 40 assert(message);
08682124 41
5d2036b5 42 if (m->n_operations >= OPERATIONS_MAX)
1b09b81c 43 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
5d2036b5 44
8dd3f6a3
LN
45 const char *details[] = {
46 "image", image->name,
47 "verb", "remove",
48 NULL
49 };
50
70244d1d
LP
51 r = bus_verify_polkit_async(
52 message,
70244d1d 53 "org.freedesktop.machine1.manage-images",
8dd3f6a3 54 details,
70244d1d
LP
55 &m->polkit_registry,
56 error);
57 if (r < 0)
58 return r;
59 if (r == 0)
60 return 1; /* Will call us back */
61
5d2036b5
LP
62 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
63 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
64
4c253ed1
LP
65 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
66 if (r < 0)
67 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
68 if (r == 0) {
5d2036b5
LP
69 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
70
71 r = image_remove(image);
72 if (r < 0) {
73 (void) write(errno_pipe_fd[1], &r, sizeof(r));
74 _exit(EXIT_FAILURE);
75 }
76
77 _exit(EXIT_SUCCESS);
78 }
79
80 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
81
03c2b288 82 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
5d2036b5
LP
83 if (r < 0) {
84 (void) sigkill_wait(child);
08682124 85 return r;
5d2036b5
LP
86 }
87
254d1313 88 errno_pipe_fd[0] = -EBADF;
08682124 89
5d2036b5 90 return 1;
08682124
LP
91}
92
1ddb263d 93int bus_image_method_rename(
ebd93cb6
LP
94 sd_bus_message *message,
95 void *userdata,
96 sd_bus_error *error) {
97
99534007 98 Image *image = ASSERT_PTR(userdata);
70244d1d 99 Manager *m = image->userdata;
ebd93cb6
LP
100 const char *new_name;
101 int r;
102
ebd93cb6 103 assert(message);
ebd93cb6
LP
104
105 r = sd_bus_message_read(message, "s", &new_name);
106 if (r < 0)
107 return r;
108
109 if (!image_name_is_valid(new_name))
110 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
111
8dd3f6a3
LN
112 const char *details[] = {
113 "image", image->name,
114 "verb", "rename",
115 "new_name", new_name,
116 NULL
117 };
118
70244d1d
LP
119 r = bus_verify_polkit_async(
120 message,
70244d1d 121 "org.freedesktop.machine1.manage-images",
8dd3f6a3 122 details,
70244d1d
LP
123 &m->polkit_registry,
124 error);
125 if (r < 0)
126 return r;
127 if (r == 0)
128 return 1; /* Will call us back */
129
3b1b2d4e
YW
130 /* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
131 assert_se(hashmap_remove_value(m->image_cache, image->name, image));
132
ebd93cb6 133 r = image_rename(image, new_name);
3b1b2d4e
YW
134 if (r < 0) {
135 image_unref(image);
ebd93cb6 136 return r;
3b1b2d4e
YW
137 }
138
139 /* Then save the object again in the cache. */
140 assert_se(hashmap_put(m->image_cache, image->name, image) > 0);
ebd93cb6
LP
141
142 return sd_bus_reply_method_return(message, NULL);
143}
144
1ddb263d 145int bus_image_method_clone(
ebd93cb6
LP
146 sd_bus_message *message,
147 void *userdata,
148 sd_bus_error *error) {
149
71136404 150 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
99534007
DT
151 Image *image = ASSERT_PTR(userdata);
152 Manager *m = ASSERT_PTR(image->userdata);
ebd93cb6
LP
153 const char *new_name;
154 int r, read_only;
56599585 155 pid_t child;
ebd93cb6 156
ebd93cb6 157 assert(message);
56599585
LP
158
159 if (m->n_operations >= OPERATIONS_MAX)
1b09b81c 160 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
ebd93cb6
LP
161
162 r = sd_bus_message_read(message, "sb", &new_name, &read_only);
163 if (r < 0)
164 return r;
165
166 if (!image_name_is_valid(new_name))
167 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
168
8dd3f6a3
LN
169 const char *details[] = {
170 "image", image->name,
171 "verb", "clone",
172 "new_name", new_name,
173 NULL
174 };
175
70244d1d
LP
176 r = bus_verify_polkit_async(
177 message,
70244d1d 178 "org.freedesktop.machine1.manage-images",
8dd3f6a3 179 details,
70244d1d
LP
180 &m->polkit_registry,
181 error);
182 if (r < 0)
183 return r;
184 if (r == 0)
185 return 1; /* Will call us back */
186
56599585
LP
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");
189
f2747bf5 190 r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
4c253ed1
LP
191 if (r < 0)
192 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
193 if (r == 0) {
56599585
LP
194 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
195
196 r = image_clone(image, new_name, read_only);
197 if (r < 0) {
198 (void) write(errno_pipe_fd[1], &r, sizeof(r));
199 _exit(EXIT_FAILURE);
200 }
201
202 _exit(EXIT_SUCCESS);
203 }
204
205 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
206
03c2b288 207 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
56599585 208 if (r < 0) {
89c9030d 209 (void) sigkill_wait(child);
ebd93cb6 210 return r;
56599585 211 }
ebd93cb6 212
254d1313 213 errno_pipe_fd[0] = -EBADF;
56599585
LP
214
215 return 1;
ebd93cb6
LP
216}
217
1ddb263d 218int bus_image_method_mark_read_only(
ebd93cb6
LP
219 sd_bus_message *message,
220 void *userdata,
221 sd_bus_error *error) {
222
1ddb263d 223 Image *image = userdata;
70244d1d 224 Manager *m = image->userdata;
2e1ae325 225 int read_only, r;
ebd93cb6 226
ebd93cb6
LP
227 assert(message);
228
ebd93cb6
LP
229 r = sd_bus_message_read(message, "b", &read_only);
230 if (r < 0)
231 return r;
232
8dd3f6a3
LN
233 const char *details[] = {
234 "image", image->name,
235 "verb", "mark_read_only",
2c7bcdd5 236 "read_only", one_zero(read_only),
8dd3f6a3
LN
237 NULL
238 };
239
70244d1d
LP
240 r = bus_verify_polkit_async(
241 message,
70244d1d 242 "org.freedesktop.machine1.manage-images",
8dd3f6a3 243 details,
70244d1d
LP
244 &m->polkit_registry,
245 error);
246 if (r < 0)
247 return r;
248 if (r == 0)
249 return 1; /* Will call us back */
250
ebd93cb6
LP
251 r = image_read_only(image, read_only);
252 if (r < 0)
253 return r;
254
255 return sd_bus_reply_method_return(message, NULL);
256}
257
d6ce17c7 258int bus_image_method_set_limit(
d6ce17c7
LP
259 sd_bus_message *message,
260 void *userdata,
261 sd_bus_error *error) {
262
263 Image *image = userdata;
264 Manager *m = image->userdata;
265 uint64_t limit;
266 int r;
267
d6ce17c7
LP
268 assert(message);
269
270 r = sd_bus_message_read(message, "t", &limit);
271 if (r < 0)
272 return r;
a90fb858 273 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
1b09b81c 274 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
d6ce17c7 275
8dd3f6a3
LN
276 const char *details[] = {
277 "machine", image->name,
278 "verb", "set_limit",
279 NULL
280 };
281
d6ce17c7
LP
282 r = bus_verify_polkit_async(
283 message,
d6ce17c7 284 "org.freedesktop.machine1.manage-images",
8dd3f6a3 285 details,
d6ce17c7
LP
286 &m->polkit_registry,
287 error);
288 if (r < 0)
289 return r;
290 if (r == 0)
291 return 1; /* Will call us back */
292
293 r = image_set_limit(image, limit);
294 if (r < 0)
295 return r;
296
297 return sd_bus_reply_method_return(message, NULL);
298}
299
cf30a8c1
LP
300int bus_image_method_get_hostname(
301 sd_bus_message *message,
302 void *userdata,
303 sd_bus_error *error) {
9153b02b 304
cf30a8c1 305 Image *image = userdata;
9153b02b
LP
306 int r;
307
cf30a8c1 308 if (!image->metadata_valid) {
84be0c71 309 r = image_read_metadata(image, &image_policy_container);
cf30a8c1
LP
310 if (r < 0)
311 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
312 }
9153b02b 313
cf30a8c1 314 return sd_bus_reply_method_return(message, "s", image->hostname);
9153b02b
LP
315}
316
cf30a8c1
LP
317int bus_image_method_get_machine_id(
318 sd_bus_message *message,
319 void *userdata,
320 sd_bus_error *error) {
9153b02b 321
cf30a8c1
LP
322 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
323 Image *image = userdata;
324 int r;
9153b02b 325
cf30a8c1 326 if (!image->metadata_valid) {
84be0c71 327 r = image_read_metadata(image, &image_policy_container);
9153b02b 328 if (r < 0)
cf30a8c1 329 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
9153b02b
LP
330 }
331
cf30a8c1 332 r = sd_bus_message_new_method_return(message, &reply);
9153b02b
LP
333 if (r < 0)
334 return r;
335
cf30a8c1
LP
336 if (sd_id128_is_null(image->machine_id)) /* Add an empty array if the ID is zero */
337 r = sd_bus_message_append(reply, "ay", 0);
338 else
339 r = sd_bus_message_append_array(reply, 'y', image->machine_id.bytes, 16);
9153b02b 340 if (r < 0)
cf30a8c1 341 return r;
9153b02b 342
cf30a8c1 343 return sd_bus_send(NULL, reply, NULL);
9153b02b
LP
344}
345
cf30a8c1 346int bus_image_method_get_machine_info(
9153b02b
LP
347 sd_bus_message *message,
348 void *userdata,
349 sd_bus_error *error) {
350
9153b02b
LP
351 Image *image = userdata;
352 int r;
353
cf30a8c1 354 if (!image->metadata_valid) {
84be0c71 355 r = image_read_metadata(image, &image_policy_container);
cf30a8c1
LP
356 if (r < 0)
357 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
358 }
9153b02b 359
cf30a8c1
LP
360 return bus_reply_pair_array(message, image->machine_info);
361}
9153b02b 362
cf30a8c1
LP
363int bus_image_method_get_os_release(
364 sd_bus_message *message,
365 void *userdata,
366 sd_bus_error *error) {
9153b02b 367
cf30a8c1
LP
368 Image *image = userdata;
369 int r;
9153b02b 370
cf30a8c1 371 if (!image->metadata_valid) {
84be0c71 372 r = image_read_metadata(image, &image_policy_container);
cf30a8c1
LP
373 if (r < 0)
374 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
9153b02b 375 }
9153b02b 376
cf30a8c1 377 return bus_reply_pair_array(message, image->os_release);
9153b02b
LP
378}
379
1ddb263d 380static int image_flush_cache(sd_event_source *s, void *userdata) {
99534007 381 Manager *m = ASSERT_PTR(userdata);
1ddb263d
LP
382
383 assert(s);
1ddb263d 384
b07ec5a1 385 hashmap_clear(m->image_cache);
1ddb263d
LP
386 return 0;
387}
388
6d917da1 389int manager_acquire_image(Manager *m, const char *name, Image **ret) {
ebeccf9e
LP
390 int r;
391
6d917da1
YW
392 assert(m);
393 assert(name);
ebeccf9e 394
6d917da1
YW
395 Image *existing = hashmap_get(m->image_cache, name);
396 if (existing) {
397 if (ret)
398 *ret = existing;
1ddb263d 399 return 0;
1ddb263d
LP
400 }
401
1ddb263d
LP
402 if (!m->image_cache_defer_event) {
403 r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
404 if (r < 0)
405 return r;
406
407 r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
408 if (r < 0)
409 return r;
410 }
411
412 r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
413 if (r < 0)
414 return r;
415
6d917da1
YW
416 _cleanup_(image_unrefp) Image *image = NULL;
417 r = image_find(IMAGE_MACHINE, name, NULL, &image);
3a6ce860 418 if (r < 0)
ebeccf9e
LP
419 return r;
420
70244d1d
LP
421 image->userdata = m;
422
32ae5db6 423 r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image);
6d917da1
YW
424 if (r < 0)
425 return r;
426
427 if (ret)
428 *ret = image;
429
430 TAKE_PTR(image);
431 return 0;
432}
433
434static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
435 _cleanup_free_ char *e = NULL;
436 Manager *m = userdata;
437 Image *image;
438 const char *p;
439 int r;
440
441 assert(bus);
442 assert(path);
443 assert(interface);
444 assert(found);
445
446 p = startswith(path, "/org/freedesktop/machine1/image/");
447 if (!p)
448 return 0;
449
450 e = bus_label_unescape(p);
451 if (!e)
452 return -ENOMEM;
453
454 r = manager_acquire_image(m, e, &image);
455 if (r == -ENOENT)
456 return 0;
457 if (r < 0)
1ddb263d 458 return r;
1ddb263d
LP
459
460 *found = image;
ebeccf9e
LP
461 return 1;
462}
463
464char *image_bus_path(const char *name) {
465 _cleanup_free_ char *e = NULL;
466
467 assert(name);
468
469 e = bus_label_escape(name);
470 if (!e)
471 return NULL;
472
b910cc72 473 return strjoin("/org/freedesktop/machine1/image/", e);
ebeccf9e
LP
474}
475
4faa530c 476static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
b07ec5a1 477 _cleanup_hashmap_free_ Hashmap *images = NULL;
ebeccf9e
LP
478 _cleanup_strv_free_ char **l = NULL;
479 Image *image;
ebeccf9e
LP
480 int r;
481
482 assert(bus);
483 assert(path);
484 assert(nodes);
485
b07ec5a1 486 images = hashmap_new(&image_hash_ops);
ebeccf9e
LP
487 if (!images)
488 return -ENOMEM;
489
d577d4a4 490 r = image_discover(IMAGE_MACHINE, NULL, images);
ebeccf9e
LP
491 if (r < 0)
492 return r;
493
90e74a66 494 HASHMAP_FOREACH(image, images) {
ebeccf9e
LP
495 char *p;
496
497 p = image_bus_path(image->name);
498 if (!p)
499 return -ENOMEM;
500
501 r = strv_consume(&l, p);
502 if (r < 0)
503 return r;
504 }
505
1cc6c93a 506 *nodes = TAKE_PTR(l);
ebeccf9e
LP
507
508 return 1;
509}
4faa530c
ZJS
510
511const sd_bus_vtable image_vtable[] = {
512 SD_BUS_VTABLE_START(0),
513 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
514 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
515 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
516 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
517 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
518 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
519 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
520 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
521 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
522 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
523 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
524 SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
525 SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
526 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
527 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
528 SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
529 SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
530 SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
531 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
532 SD_BUS_VTABLE_END
533};
534
535const BusObjectImplementation image_object = {
536 "/org/freedesktop/machine1/image",
537 "org.freedesktop.machine1.Image",
538 .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, image_object_find}),
539 .node_enumerator = image_node_enumerator,
540};