]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/image-dbus.c
Merge pull request #33139 from weblate/weblate-systemd-main
[thirdparty/systemd.git] / src / machine / image-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/file.h>
4 #include <sys/mount.h>
5
6 #include "alloc-util.h"
7 #include "bus-get-properties.h"
8 #include "bus-label.h"
9 #include "bus-polkit.h"
10 #include "copy.h"
11 #include "discover-image.h"
12 #include "dissect-image.h"
13 #include "fd-util.h"
14 #include "fileio.h"
15 #include "fs-util.h"
16 #include "image-dbus.h"
17 #include "io-util.h"
18 #include "loop-util.h"
19 #include "missing_capability.h"
20 #include "mount-util.h"
21 #include "os-util.h"
22 #include "process-util.h"
23 #include "raw-clone.h"
24 #include "strv.h"
25 #include "user-util.h"
26
27 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
28
29 int bus_image_method_remove(
30 sd_bus_message *message,
31 void *userdata,
32 sd_bus_error *error) {
33
34 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
35 Image *image = ASSERT_PTR(userdata);
36 Manager *m = image->userdata;
37 pid_t child;
38 int r;
39
40 assert(message);
41
42 if (m->n_operations >= OPERATIONS_MAX)
43 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
44
45 const char *details[] = {
46 "image", image->name,
47 "verb", "remove",
48 NULL
49 };
50
51 r = bus_verify_polkit_async(
52 message,
53 "org.freedesktop.machine1.manage-images",
54 details,
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
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
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) {
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
82 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
83 if (r < 0) {
84 (void) sigkill_wait(child);
85 return r;
86 }
87
88 errno_pipe_fd[0] = -EBADF;
89
90 return 1;
91 }
92
93 int bus_image_method_rename(
94 sd_bus_message *message,
95 void *userdata,
96 sd_bus_error *error) {
97
98 Image *image = ASSERT_PTR(userdata);
99 Manager *m = image->userdata;
100 const char *new_name;
101 int r;
102
103 assert(message);
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
112 const char *details[] = {
113 "image", image->name,
114 "verb", "rename",
115 "new_name", new_name,
116 NULL
117 };
118
119 r = bus_verify_polkit_async(
120 message,
121 "org.freedesktop.machine1.manage-images",
122 details,
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
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
133 r = image_rename(image, new_name);
134 if (r < 0) {
135 image_unref(image);
136 return r;
137 }
138
139 /* Then save the object again in the cache. */
140 assert_se(hashmap_put(m->image_cache, image->name, image) > 0);
141
142 return sd_bus_reply_method_return(message, NULL);
143 }
144
145 int bus_image_method_clone(
146 sd_bus_message *message,
147 void *userdata,
148 sd_bus_error *error) {
149
150 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
151 Image *image = ASSERT_PTR(userdata);
152 Manager *m = ASSERT_PTR(image->userdata);
153 const char *new_name;
154 int r, read_only;
155 pid_t child;
156
157 assert(message);
158
159 if (m->n_operations >= OPERATIONS_MAX)
160 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
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
169 const char *details[] = {
170 "image", image->name,
171 "verb", "clone",
172 "new_name", new_name,
173 NULL
174 };
175
176 r = bus_verify_polkit_async(
177 message,
178 "org.freedesktop.machine1.manage-images",
179 details,
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
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
190 r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
191 if (r < 0)
192 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
193 if (r == 0) {
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
207 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
208 if (r < 0) {
209 (void) sigkill_wait(child);
210 return r;
211 }
212
213 errno_pipe_fd[0] = -EBADF;
214
215 return 1;
216 }
217
218 int bus_image_method_mark_read_only(
219 sd_bus_message *message,
220 void *userdata,
221 sd_bus_error *error) {
222
223 Image *image = userdata;
224 Manager *m = image->userdata;
225 int read_only, r;
226
227 assert(message);
228
229 r = sd_bus_message_read(message, "b", &read_only);
230 if (r < 0)
231 return r;
232
233 const char *details[] = {
234 "image", image->name,
235 "verb", "mark_read_only",
236 "read_only", one_zero(read_only),
237 NULL
238 };
239
240 r = bus_verify_polkit_async(
241 message,
242 "org.freedesktop.machine1.manage-images",
243 details,
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
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
258 int bus_image_method_set_limit(
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
268 assert(message);
269
270 r = sd_bus_message_read(message, "t", &limit);
271 if (r < 0)
272 return r;
273 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
274 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
275
276 const char *details[] = {
277 "machine", image->name,
278 "verb", "set_limit",
279 NULL
280 };
281
282 r = bus_verify_polkit_async(
283 message,
284 "org.freedesktop.machine1.manage-images",
285 details,
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
300 int bus_image_method_get_hostname(
301 sd_bus_message *message,
302 void *userdata,
303 sd_bus_error *error) {
304
305 Image *image = userdata;
306 int r;
307
308 if (!image->metadata_valid) {
309 r = image_read_metadata(image, &image_policy_container);
310 if (r < 0)
311 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
312 }
313
314 return sd_bus_reply_method_return(message, "s", image->hostname);
315 }
316
317 int bus_image_method_get_machine_id(
318 sd_bus_message *message,
319 void *userdata,
320 sd_bus_error *error) {
321
322 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
323 Image *image = userdata;
324 int r;
325
326 if (!image->metadata_valid) {
327 r = image_read_metadata(image, &image_policy_container);
328 if (r < 0)
329 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
330 }
331
332 r = sd_bus_message_new_method_return(message, &reply);
333 if (r < 0)
334 return r;
335
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);
340 if (r < 0)
341 return r;
342
343 return sd_bus_send(NULL, reply, NULL);
344 }
345
346 int bus_image_method_get_machine_info(
347 sd_bus_message *message,
348 void *userdata,
349 sd_bus_error *error) {
350
351 Image *image = userdata;
352 int r;
353
354 if (!image->metadata_valid) {
355 r = image_read_metadata(image, &image_policy_container);
356 if (r < 0)
357 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
358 }
359
360 return bus_reply_pair_array(message, image->machine_info);
361 }
362
363 int bus_image_method_get_os_release(
364 sd_bus_message *message,
365 void *userdata,
366 sd_bus_error *error) {
367
368 Image *image = userdata;
369 int r;
370
371 if (!image->metadata_valid) {
372 r = image_read_metadata(image, &image_policy_container);
373 if (r < 0)
374 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
375 }
376
377 return bus_reply_pair_array(message, image->os_release);
378 }
379
380 static int image_flush_cache(sd_event_source *s, void *userdata) {
381 Manager *m = ASSERT_PTR(userdata);
382
383 assert(s);
384
385 hashmap_clear(m->image_cache);
386 return 0;
387 }
388
389 int manager_acquire_image(Manager *m, const char *name, Image **ret) {
390 int r;
391
392 assert(m);
393 assert(name);
394
395 Image *existing = hashmap_get(m->image_cache, name);
396 if (existing) {
397 if (ret)
398 *ret = existing;
399 return 0;
400 }
401
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
416 _cleanup_(image_unrefp) Image *image = NULL;
417 r = image_find(IMAGE_MACHINE, name, NULL, &image);
418 if (r < 0)
419 return r;
420
421 image->userdata = m;
422
423 r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image);
424 if (r < 0)
425 return r;
426
427 if (ret)
428 *ret = image;
429
430 TAKE_PTR(image);
431 return 0;
432 }
433
434 static 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)
458 return r;
459
460 *found = image;
461 return 1;
462 }
463
464 char *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
473 return strjoin("/org/freedesktop/machine1/image/", e);
474 }
475
476 static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
477 _cleanup_hashmap_free_ Hashmap *images = NULL;
478 _cleanup_strv_free_ char **l = NULL;
479 Image *image;
480 int r;
481
482 assert(bus);
483 assert(path);
484 assert(nodes);
485
486 images = hashmap_new(&image_hash_ops);
487 if (!images)
488 return -ENOMEM;
489
490 r = image_discover(IMAGE_MACHINE, NULL, images);
491 if (r < 0)
492 return r;
493
494 HASHMAP_FOREACH(image, images) {
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
506 *nodes = TAKE_PTR(l);
507
508 return 1;
509 }
510
511 const 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
535 const 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 };