]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/portable/portabled-image-bus.c
journalctl: add with-unit mode
[thirdparty/systemd.git] / src / portable / portabled-image-bus.c
CommitLineData
61d0578b
LP
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include "alloc-util.h"
4#include "bus-common-errors.h"
5#include "bus-label.h"
6#include "bus-util.h"
7#include "fd-util.h"
8#include "fileio.h"
9#include "io-util.h"
10#include "machine-image.h"
11#include "portable.h"
12#include "portabled-bus.h"
13#include "portabled-image-bus.h"
14#include "portabled-image.h"
15#include "portabled.h"
16#include "process-util.h"
17#include "strv.h"
18#include "user-util.h"
19
20static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
21
22int bus_image_common_get_os_release(
23 Manager *m,
24 sd_bus_message *message,
25 const char *name_or_path,
26 Image *image,
27 sd_bus_error *error) {
28
29 int r;
30
31 assert(name_or_path || image);
32 assert(message);
33
34 if (!m) {
35 assert(image);
36 m = image->userdata;
37 }
38
39 r = bus_image_acquire(m,
40 message,
41 name_or_path,
42 image,
43 BUS_IMAGE_AUTHENTICATE_BY_PATH,
44 "org.freedesktop.portable1.inspect-images",
45 &image,
46 error);
47 if (r < 0)
48 return r;
49 if (r == 0) /* Will call us back */
50 return 1;
51
52 if (!image->metadata_valid) {
53 r = image_read_metadata(image);
54 if (r < 0)
55 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
56 }
57
58 return bus_reply_pair_array(message, image->os_release);
59}
60
61static int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
62 return bus_image_common_get_os_release(NULL, message, NULL, userdata, error);
63}
64
65static int append_fd(sd_bus_message *m, PortableMetadata *d) {
66 _cleanup_fclose_ FILE *f = NULL;
67 _cleanup_free_ char *buf = NULL;
68 size_t n;
69 int r;
70
71 assert(m);
72 assert(d);
73 assert(d->fd >= 0);
74
75 f = fdopen(d->fd, "re");
76 if (!f)
77 return -errno;
78
79 d->fd = -1;
80
81 r = read_full_stream(f, &buf, &n);
82 if (r < 0)
83 return r;
84
85 return sd_bus_message_append_array(m, 'y', buf, n);
86}
87
88int bus_image_common_get_metadata(
89 Manager *m,
90 sd_bus_message *message,
91 const char *name_or_path,
92 Image *image,
93 sd_bus_error *error) {
94
95 _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
96 _cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
97 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
98 _cleanup_free_ PortableMetadata **sorted = NULL;
99 _cleanup_strv_free_ char **matches = NULL;
100 size_t i;
101 int r;
102
103 assert(name_or_path || image);
104 assert(message);
105
106 if (!m) {
107 assert(image);
108 m = image->userdata;
109 }
110
111 r = sd_bus_message_read_strv(message, &matches);
112 if (r < 0)
113 return r;
114
115 r = bus_image_acquire(m,
116 message,
117 name_or_path,
118 image,
119 BUS_IMAGE_AUTHENTICATE_BY_PATH,
120 "org.freedesktop.portable1.inspect-images",
121 &image,
122 error);
123 if (r < 0)
124 return r;
125 if (r == 0) /* Will call us back */
126 return 1;
127
128 r = portable_extract(
129 image->path,
130 matches,
131 &os_release,
132 &unit_files,
133 error);
134 if (r < 0)
135 return r;
136
137 r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
138 if (r < 0)
139 return r;
140
141 r = sd_bus_message_new_method_return(message, &reply);
142 if (r < 0)
143 return r;
144
145 r = sd_bus_message_append(reply, "s", image->path);
146 if (r < 0)
147 return r;
148
149 r = append_fd(reply, os_release);
150 if (r < 0)
151 return r;
152
153 r = sd_bus_message_open_container(reply, 'a', "{say}");
154 if (r < 0)
155 return r;
156
157 for (i = 0; i < hashmap_size(unit_files); i++) {
158
159
160 r = sd_bus_message_open_container(reply, 'e', "say");
161 if (r < 0)
162 return r;
163
164 r = sd_bus_message_append(reply, "s", sorted[i]->name);
165 if (r < 0)
166 return r;
167
168 r = append_fd(reply, sorted[i]);
169 if (r < 0)
170 return r;
171
172 r = sd_bus_message_close_container(reply);
173 if (r < 0)
174 return r;
175 }
176
177 r = sd_bus_message_close_container(reply);
178 if (r < 0)
179 return r;
180
181 return sd_bus_send(NULL, reply, NULL);
182}
183
184static int bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
185 return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
186}
187
188static int bus_image_method_get_state(
189 sd_bus_message *message,
190 void *userdata,
191 sd_bus_error *error) {
192
193 Image *image = userdata;
194 PortableState state;
195 int r;
196
197 assert(message);
198 assert(image);
199
200 r = portable_get_state(
201 sd_bus_message_get_bus(message),
202 image->path,
203 0,
204 &state,
205 error);
206 if (r < 0)
207 return r;
208
209 return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
210}
211
212int bus_image_common_attach(
213 Manager *m,
214 sd_bus_message *message,
215 const char *name_or_path,
216 Image *image,
217 sd_bus_error *error) {
218
219 _cleanup_free_ char **matches = NULL;
220 PortableChange *changes = NULL;
221 PortableFlags flags = 0;
222 const char *copy_mode;
223 size_t n_changes = 0;
224 const char *profile;
225 int runtime, r;
226
227 assert(message);
228 assert(name_or_path || image);
229
230 if (!m) {
231 assert(image);
232 m = image->userdata;
233 }
234
235 r = sd_bus_message_read_strv(message, &matches);
236 if (r < 0)
237 return r;
238
239 r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
240 if (r < 0)
241 return r;
242
243 if (streq(copy_mode, "symlink"))
244 flags |= PORTABLE_PREFER_SYMLINK;
245 else if (streq(copy_mode, "copy"))
246 flags |= PORTABLE_PREFER_COPY;
247 else if (!isempty(copy_mode))
248 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
249
250 if (runtime)
251 flags |= PORTABLE_RUNTIME;
252
253 r = bus_image_acquire(m,
254 message,
255 name_or_path,
256 image,
257 BUS_IMAGE_AUTHENTICATE_ALL,
258 "org.freedesktop.portable1.attach-images",
259 &image,
260 error);
261 if (r < 0)
262 return r;
263 if (r == 0) /* Will call us back */
264 return 1;
265
266 r = portable_attach(
267 sd_bus_message_get_bus(message),
268 image->path,
269 matches,
270 profile,
271 flags,
272 &changes,
273 &n_changes,
274 error);
275 if (r < 0)
276 goto finish;
277
278 r = reply_portable_changes(message, changes, n_changes);
279
280finish:
281 portable_changes_free(changes, n_changes);
282 return r;
283}
284
285static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
286 return bus_image_common_attach(NULL, message, NULL, userdata, error);
287}
288
289static int bus_image_method_detach(
290 sd_bus_message *message,
291 void *userdata,
292 sd_bus_error *error) {
293
294 PortableChange *changes = NULL;
295 Image *image = userdata;
296 Manager *m = image->userdata;
297 size_t n_changes = 0;
298 int r, runtime;
299
300 assert(message);
301 assert(image);
302 assert(m);
303
304 r = sd_bus_message_read(message, "b", &runtime);
305 if (r < 0)
306 return r;
307
308 r = bus_verify_polkit_async(
309 message,
310 CAP_SYS_ADMIN,
311 "org.freedesktop.portable1.attach-images",
312 NULL,
313 false,
314 UID_INVALID,
315 &m->polkit_registry,
316 error);
317 if (r < 0)
318 return r;
319 if (r == 0)
320 return 1; /* Will call us back */
321
322 r = portable_detach(
323 sd_bus_message_get_bus(message),
324 image->path,
325 runtime ? PORTABLE_RUNTIME : 0,
326 &changes,
327 &n_changes,
328 error);
329 if (r < 0)
330 goto finish;
331
332 r = reply_portable_changes(message, changes, n_changes);
333
334finish:
335 portable_changes_free(changes, n_changes);
336 return r;
337}
338
339int bus_image_common_remove(
340 Manager *m,
341 sd_bus_message *message,
342 const char *name_or_path,
343 Image *image,
344 sd_bus_error *error) {
345
346 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
347 _cleanup_(sigkill_waitp) pid_t child = 0;
348 PortableState state;
349 int r;
350
351 assert(message);
352 assert(name_or_path || image);
353
354 if (!m) {
355 assert(image);
356 m = image->userdata;
357 }
358
359 if (m->n_operations >= OPERATIONS_MAX)
360 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
361
362 r = bus_image_acquire(m,
363 message,
364 name_or_path,
365 image,
366 BUS_IMAGE_AUTHENTICATE_ALL,
367 "org.freedesktop.portable1.manage-images",
368 &image,
369 error);
370 if (r < 0)
371 return r;
372 if (r == 0)
373 return 1; /* Will call us back */
374
375 r = portable_get_state(
376 sd_bus_message_get_bus(message),
377 image->path,
378 0,
379 &state,
380 error);
381 if (r < 0)
382 return r;
383
384 if (state != PORTABLE_DETACHED)
385 return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
386
387 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
388 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
389
390 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
391 if (r < 0)
392 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
393 if (r == 0) {
394 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
395
396 r = image_remove(image);
397 if (r < 0) {
398 (void) write(errno_pipe_fd[1], &r, sizeof(r));
399 _exit(EXIT_FAILURE);
400 }
401
402 _exit(EXIT_SUCCESS);
403 }
404
405 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
406
407 r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
408 if (r < 0)
409 return r;
410
411 child = 0;
412 errno_pipe_fd[0] = -1;
413
414 return 1;
415}
416
417static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
418 return bus_image_common_remove(NULL, message, NULL, userdata, error);
419}
420
421int bus_image_common_mark_read_only(
422 Manager *m,
423 sd_bus_message *message,
424 const char *name_or_path,
425 Image *image,
426 sd_bus_error *error) {
427
428 int r, read_only;
429
430 assert(message);
431 assert(name_or_path || image);
432
433 if (!m) {
434 assert(image);
435 m = image->userdata;
436 }
437
438 r = sd_bus_message_read(message, "b", &read_only);
439 if (r < 0)
440 return r;
441
442 r = bus_image_acquire(m,
443 message,
444 name_or_path,
445 image,
446 BUS_IMAGE_AUTHENTICATE_ALL,
447 "org.freedesktop.portable1.manage-images",
448 &image,
449 error);
450 if (r < 0)
451 return r;
452 if (r == 0)
453 return 1; /* Will call us back */
454
455 r = image_read_only(image, read_only);
456 if (r < 0)
457 return r;
458
459 return sd_bus_reply_method_return(message, NULL);
460}
461
462static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
463 return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
464}
465
466int bus_image_common_set_limit(
467 Manager *m,
468 sd_bus_message *message,
469 const char *name_or_path,
470 Image *image,
471 sd_bus_error *error) {
472
473 uint64_t limit;
474 int r;
475
476 assert(message);
477 assert(name_or_path || image);
478
479 if (!m) {
480 assert(image);
481 m = image->userdata;
482 }
483
484 r = sd_bus_message_read(message, "t", &limit);
485 if (r < 0)
486 return r;
487 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
488 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
489
490 r = bus_image_acquire(m,
491 message,
492 name_or_path,
493 image,
494 BUS_IMAGE_AUTHENTICATE_ALL,
495 "org.freedesktop.portable1.manage-images",
496 &image,
497 error);
498 if (r < 0)
499 return r;
500 if (r == 0)
501 return 1; /* Will call us back */
502
503 r = image_set_limit(image, limit);
504 if (r < 0)
505 return r;
506
507 return sd_bus_reply_method_return(message, NULL);
508}
509
510static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
511 return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
512}
513
514const sd_bus_vtable image_vtable[] = {
515 SD_BUS_VTABLE_START(0),
516 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
517 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
518 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
519 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
520 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
521 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
522 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
523 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
524 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
525 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
526 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
527 SD_BUS_METHOD("GetMedatadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
528 SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED),
529 SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED),
530 SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED),
531 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
532 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
533 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
534 SD_BUS_VTABLE_END
535};
536
537int bus_image_path(Image *image, char **ret) {
538 assert(image);
539 assert(ret);
540
541 if (!image->discoverable)
542 return -EINVAL;
543
544 return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
545}
546
547int bus_image_acquire(
548 Manager *m,
549 sd_bus_message *message,
550 const char *name_or_path,
551 Image *image,
552 ImageAcquireMode mode,
553 const char *polkit_action,
554 Image **ret,
555 sd_bus_error *error) {
556
557 _cleanup_(image_unrefp) Image *loaded = NULL;
558 Image *cached;
559 int r;
560
561 assert(m);
562 assert(message);
563 assert(name_or_path || image);
564 assert(mode >= 0);
565 assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
566 assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
567 assert(ret);
568
569 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
570
571 if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
572 r = bus_verify_polkit_async(
573 message,
574 CAP_SYS_ADMIN,
575 polkit_action,
576 NULL,
577 false,
578 UID_INVALID,
579 &m->polkit_registry,
580 error);
581 if (r < 0)
582 return r;
583 if (r == 0) { /* Will call us back */
584 *ret = NULL;
585 return 0;
586 }
587 }
588
589 /* Already passed in? */
590 if (image) {
591 *ret = image;
592 return 1;
593 }
594
595 /* Let's see if this image is already cached? */
596 cached = manager_image_cache_get(m, name_or_path);
597 if (cached) {
598 *ret = cached;
599 return 1;
600 }
601
602 if (image_name_is_valid(name_or_path)) {
603
604 /* If it's a short name, let's search for it */
605 r = image_find(IMAGE_PORTABLE, name_or_path, &loaded);
606 if (r == -ENOENT)
607 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
608
609 /* other errors are handled below… */
610 } else {
611 /* Don't accept path if this is always forbidden */
612 if (mode == BUS_IMAGE_REFUSE_BY_PATH)
613 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path);
614
615 if (!path_is_absolute(name_or_path))
616 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is not valid or not a valid path.", name_or_path);
617
618 if (!path_is_normalized(name_or_path))
619 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path);
620
621 if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
622 r = bus_verify_polkit_async(
623 message,
624 CAP_SYS_ADMIN,
625 polkit_action,
626 NULL,
627 false,
628 UID_INVALID,
629 &m->polkit_registry,
630 error);
631 if (r < 0)
632 return r;
633 if (r == 0) { /* Will call us back */
634 *ret = NULL;
635 return 0;
636 }
637 }
638
639 r = image_from_path(name_or_path, &loaded);
640 }
641 if (r < 0)
642 return r;
643
644 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
645 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
646 * means we don't actually need to ref the return object. */
647 r = manager_image_cache_add(m, loaded);
648 if (r < 0)
649 return r;
650
651 *ret = loaded;
652 return 1;
653}
654
655int bus_image_object_find(
656 sd_bus *bus,
657 const char *path,
658 const char *interface,
659 void *userdata,
660 void **found,
661 sd_bus_error *error) {
662
663 _cleanup_free_ char *e = NULL;
664 Manager *m = userdata;
665 Image *image = NULL;
666 int r;
667
668 assert(bus);
669 assert(path);
670 assert(interface);
671 assert(found);
672
673 r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
674 if (r < 0)
675 return 0;
676 if (r == 0)
677 goto not_found;
678
679 r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
680 if (r == -ENOENT)
681 goto not_found;
682 if (r < 0)
683 return r;
684
685 *found = image;
686 return 1;
687
688not_found:
689 *found = NULL;
690 return 0;
691}
692
693int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
694 _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
695 _cleanup_strv_free_ char **l = NULL;
696 size_t n_allocated = 0, n = 0;
697 Manager *m = userdata;
698 Image *image;
699 Iterator i;
700 int r;
701
702 assert(bus);
703 assert(path);
704 assert(nodes);
705
706 images = hashmap_new(&string_hash_ops);
707 if (!images)
708 return -ENOMEM;
709
710 r = manager_image_cache_discover(m, images, error);
711 if (r < 0)
712 return r;
713
714 HASHMAP_FOREACH(image, images, i) {
715 char *p;
716
717 r = bus_image_path(image, &p);
718 if (r < 0)
719 return r;
720
721 if (!GREEDY_REALLOC(l, n_allocated, n+2)) {
722 free(p);
723 return -ENOMEM;
724 }
725
726 l[n++] = p;
727 l[n] = NULL;
728 }
729
730 *nodes = TAKE_PTR(l);
731
732 return 1;
733}