]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/portable/portabled-image-bus.c
gcrypt: do not ignore return values
[thirdparty/systemd.git] / src / portable / portabled-image-bus.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
61d0578b 2
ca78ad1d
ZJS
3#include <fcntl.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6#include <unistd.h>
7
61d0578b
LP
8#include "alloc-util.h"
9#include "bus-common-errors.h"
40af3d02 10#include "bus-get-properties.h"
61d0578b 11#include "bus-label.h"
3b91bae3 12#include "bus-object.h"
269e4d2d 13#include "bus-polkit.h"
61d0578b 14#include "bus-util.h"
57f1b61b 15#include "discover-image.h"
61d0578b
LP
16#include "fd-util.h"
17#include "fileio.h"
18#include "io-util.h"
204f52e3 19#include "missing_capability.h"
6ef06723 20#include "os-util.h"
61d0578b
LP
21#include "portable.h"
22#include "portabled-bus.h"
23#include "portabled-image-bus.h"
24#include "portabled-image.h"
25#include "portabled.h"
26#include "process-util.h"
27#include "strv.h"
28#include "user-util.h"
29
30static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
31
32int bus_image_common_get_os_release(
33 Manager *m,
34 sd_bus_message *message,
35 const char *name_or_path,
36 Image *image,
37 sd_bus_error *error) {
38
39 int r;
40
41 assert(name_or_path || image);
42 assert(message);
43
44 if (!m) {
45 assert(image);
46 m = image->userdata;
47 }
48
49 r = bus_image_acquire(m,
50 message,
51 name_or_path,
52 image,
53 BUS_IMAGE_AUTHENTICATE_BY_PATH,
54 "org.freedesktop.portable1.inspect-images",
55 &image,
56 error);
57 if (r < 0)
58 return r;
59 if (r == 0) /* Will call us back */
60 return 1;
61
62 if (!image->metadata_valid) {
63 r = image_read_metadata(image);
64 if (r < 0)
65 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
66 }
67
68 return bus_reply_pair_array(message, image->os_release);
69}
70
71static int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
72 return bus_image_common_get_os_release(NULL, message, NULL, userdata, error);
73}
74
75static int append_fd(sd_bus_message *m, PortableMetadata *d) {
76 _cleanup_fclose_ FILE *f = NULL;
77 _cleanup_free_ char *buf = NULL;
78 size_t n;
79 int r;
80
81 assert(m);
82 assert(d);
83 assert(d->fd >= 0);
84
4fa744a3 85 f = take_fdopen(&d->fd, "r");
61d0578b
LP
86 if (!f)
87 return -errno;
88
61d0578b
LP
89 r = read_full_stream(f, &buf, &n);
90 if (r < 0)
91 return r;
92
93 return sd_bus_message_append_array(m, 'y', buf, n);
94}
95
96int bus_image_common_get_metadata(
97 Manager *m,
98 sd_bus_message *message,
99 const char *name_or_path,
100 Image *image,
101 sd_bus_error *error) {
102
103 _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
67818055 104 _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
61d0578b
LP
105 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
106 _cleanup_free_ PortableMetadata **sorted = NULL;
107 _cleanup_strv_free_ char **matches = NULL;
108 size_t i;
109 int r;
110
111 assert(name_or_path || image);
112 assert(message);
113
114 if (!m) {
115 assert(image);
116 m = image->userdata;
117 }
118
119 r = sd_bus_message_read_strv(message, &matches);
120 if (r < 0)
121 return r;
122
123 r = bus_image_acquire(m,
124 message,
125 name_or_path,
126 image,
127 BUS_IMAGE_AUTHENTICATE_BY_PATH,
128 "org.freedesktop.portable1.inspect-images",
129 &image,
130 error);
131 if (r < 0)
132 return r;
133 if (r == 0) /* Will call us back */
134 return 1;
135
136 r = portable_extract(
137 image->path,
138 matches,
139 &os_release,
140 &unit_files,
141 error);
142 if (r < 0)
143 return r;
144
145 r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
146 if (r < 0)
147 return r;
148
149 r = sd_bus_message_new_method_return(message, &reply);
150 if (r < 0)
151 return r;
152
153 r = sd_bus_message_append(reply, "s", image->path);
154 if (r < 0)
155 return r;
156
157 r = append_fd(reply, os_release);
158 if (r < 0)
159 return r;
160
161 r = sd_bus_message_open_container(reply, 'a', "{say}");
162 if (r < 0)
163 return r;
164
165 for (i = 0; i < hashmap_size(unit_files); i++) {
166
61d0578b
LP
167 r = sd_bus_message_open_container(reply, 'e', "say");
168 if (r < 0)
169 return r;
170
171 r = sd_bus_message_append(reply, "s", sorted[i]->name);
172 if (r < 0)
173 return r;
174
175 r = append_fd(reply, sorted[i]);
176 if (r < 0)
177 return r;
178
179 r = sd_bus_message_close_container(reply);
180 if (r < 0)
181 return r;
182 }
183
184 r = sd_bus_message_close_container(reply);
185 if (r < 0)
186 return r;
187
188 return sd_bus_send(NULL, reply, NULL);
189}
190
191static int bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
192 return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
193}
194
195static int bus_image_method_get_state(
196 sd_bus_message *message,
197 void *userdata,
198 sd_bus_error *error) {
199
200 Image *image = userdata;
201 PortableState state;
202 int r;
203
204 assert(message);
205 assert(image);
206
207 r = portable_get_state(
208 sd_bus_message_get_bus(message),
209 image->path,
210 0,
211 &state,
212 error);
213 if (r < 0)
214 return r;
215
216 return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
217}
218
219int bus_image_common_attach(
220 Manager *m,
221 sd_bus_message *message,
222 const char *name_or_path,
223 Image *image,
224 sd_bus_error *error) {
225
c5a935b0 226 _cleanup_strv_free_ char **matches = NULL;
61d0578b
LP
227 PortableChange *changes = NULL;
228 PortableFlags flags = 0;
c5a935b0 229 const char *profile, *copy_mode;
61d0578b 230 size_t n_changes = 0;
61d0578b
LP
231 int runtime, r;
232
233 assert(message);
234 assert(name_or_path || image);
235
236 if (!m) {
237 assert(image);
238 m = image->userdata;
239 }
240
241 r = sd_bus_message_read_strv(message, &matches);
242 if (r < 0)
243 return r;
244
245 r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
246 if (r < 0)
247 return r;
248
249 if (streq(copy_mode, "symlink"))
250 flags |= PORTABLE_PREFER_SYMLINK;
251 else if (streq(copy_mode, "copy"))
252 flags |= PORTABLE_PREFER_COPY;
253 else if (!isempty(copy_mode))
254 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
255
256 if (runtime)
257 flags |= PORTABLE_RUNTIME;
258
259 r = bus_image_acquire(m,
260 message,
261 name_or_path,
262 image,
263 BUS_IMAGE_AUTHENTICATE_ALL,
264 "org.freedesktop.portable1.attach-images",
265 &image,
266 error);
267 if (r < 0)
268 return r;
269 if (r == 0) /* Will call us back */
270 return 1;
271
272 r = portable_attach(
273 sd_bus_message_get_bus(message),
274 image->path,
275 matches,
276 profile,
277 flags,
278 &changes,
279 &n_changes,
280 error);
281 if (r < 0)
282 goto finish;
283
284 r = reply_portable_changes(message, changes, n_changes);
285
286finish:
287 portable_changes_free(changes, n_changes);
288 return r;
289}
290
291static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
292 return bus_image_common_attach(NULL, message, NULL, userdata, error);
293}
294
295static int bus_image_method_detach(
296 sd_bus_message *message,
297 void *userdata,
298 sd_bus_error *error) {
299
300 PortableChange *changes = NULL;
301 Image *image = userdata;
302 Manager *m = image->userdata;
303 size_t n_changes = 0;
304 int r, runtime;
305
306 assert(message);
307 assert(image);
308 assert(m);
309
310 r = sd_bus_message_read(message, "b", &runtime);
311 if (r < 0)
312 return r;
313
314 r = bus_verify_polkit_async(
315 message,
316 CAP_SYS_ADMIN,
317 "org.freedesktop.portable1.attach-images",
318 NULL,
319 false,
320 UID_INVALID,
321 &m->polkit_registry,
322 error);
323 if (r < 0)
324 return r;
325 if (r == 0)
326 return 1; /* Will call us back */
327
328 r = portable_detach(
329 sd_bus_message_get_bus(message),
330 image->path,
331 runtime ? PORTABLE_RUNTIME : 0,
332 &changes,
333 &n_changes,
334 error);
335 if (r < 0)
336 goto finish;
337
338 r = reply_portable_changes(message, changes, n_changes);
339
340finish:
341 portable_changes_free(changes, n_changes);
342 return r;
343}
344
345int bus_image_common_remove(
346 Manager *m,
347 sd_bus_message *message,
348 const char *name_or_path,
349 Image *image,
350 sd_bus_error *error) {
351
352 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
353 _cleanup_(sigkill_waitp) pid_t child = 0;
354 PortableState state;
355 int r;
356
357 assert(message);
358 assert(name_or_path || image);
359
360 if (!m) {
361 assert(image);
362 m = image->userdata;
363 }
364
365 if (m->n_operations >= OPERATIONS_MAX)
366 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
367
368 r = bus_image_acquire(m,
369 message,
370 name_or_path,
371 image,
372 BUS_IMAGE_AUTHENTICATE_ALL,
373 "org.freedesktop.portable1.manage-images",
374 &image,
375 error);
376 if (r < 0)
377 return r;
378 if (r == 0)
379 return 1; /* Will call us back */
380
381 r = portable_get_state(
382 sd_bus_message_get_bus(message),
383 image->path,
384 0,
385 &state,
386 error);
387 if (r < 0)
388 return r;
389
390 if (state != PORTABLE_DETACHED)
391 return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
392
393 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
394 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
395
396 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
397 if (r < 0)
398 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
399 if (r == 0) {
400 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
401
402 r = image_remove(image);
403 if (r < 0) {
404 (void) write(errno_pipe_fd[1], &r, sizeof(r));
405 _exit(EXIT_FAILURE);
406 }
407
408 _exit(EXIT_SUCCESS);
409 }
410
411 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
412
413 r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
414 if (r < 0)
415 return r;
416
417 child = 0;
418 errno_pipe_fd[0] = -1;
419
420 return 1;
421}
422
423static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
424 return bus_image_common_remove(NULL, message, NULL, userdata, error);
425}
426
e26fe5f9
LB
427/* Given two PortableChange arrays, return a new array that has all elements of the first that are
428 * not also present in the second, comparing the basename of the path values. */
429static int normalize_portable_changes(
430 const PortableChange *changes_attached,
431 size_t n_changes_attached,
432 const PortableChange *changes_detached,
433 size_t n_changes_detached,
434 PortableChange **ret_changes,
435 size_t *ret_n_changes) {
436
437 PortableChange *changes = NULL;
438 size_t n_changes = 0;
439 int r = 0;
440
441 assert(ret_n_changes);
442 assert(ret_changes);
443
444 if (n_changes_detached == 0)
445 return 0; /* Nothing to do */
446
447 changes = new0(PortableChange, n_changes_attached + n_changes_detached);
448 if (!changes)
449 return -ENOMEM;
450
451 /* Corner case: only detached, nothing attached */
452 if (n_changes_attached == 0) {
453 memcpy(changes, changes_detached, sizeof(PortableChange) * n_changes_detached);
454 *ret_changes = TAKE_PTR(changes);
455 *ret_n_changes = n_changes_detached;
456
457 return 0;
458 }
459
460 for (size_t i = 0; i < n_changes_detached; ++i) {
461 bool found = false;
462
463 for (size_t j = 0; j < n_changes_attached; ++j)
464 if (streq(basename(changes_detached[i].path), basename(changes_attached[j].path))) {
465 found = true;
466 break;
467 }
468
469 if (!found) {
470 _cleanup_free_ char *path = NULL, *source = NULL;
471
472 path = strdup(changes_detached[i].path);
473 if (!path) {
474 r = -ENOMEM;
475 goto fail;
476 }
477
478 if (changes_detached[i].source) {
479 source = strdup(changes_detached[i].source);
480 if (!source) {
481 r = -ENOMEM;
482 goto fail;
483 }
484 }
485
486 changes[n_changes++] = (PortableChange) {
ba5b6c59 487 .type_or_errno = changes_detached[i].type_or_errno,
e26fe5f9
LB
488 .path = TAKE_PTR(path),
489 .source = TAKE_PTR(source),
490 };
491 }
492 }
493
494 *ret_n_changes = n_changes;
495 *ret_changes = TAKE_PTR(changes);
496
497 return 0;
498
499fail:
500 portable_changes_free(changes, n_changes);
501 return r;
502}
503
504int bus_image_common_reattach(
505 Manager *m,
506 sd_bus_message *message,
507 const char *name_or_path,
508 Image *image,
509 sd_bus_error *error) {
510
511 PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL;
512 size_t n_changes_detached = 0, n_changes_attached = 0, n_changes_gone = 0;
513 _cleanup_strv_free_ char **matches = NULL;
514 PortableFlags flags = PORTABLE_REATTACH;
515 const char *profile, *copy_mode;
516 int runtime, r;
517
518 assert(message);
519 assert(name_or_path || image);
520
521 if (!m) {
522 assert(image);
523 m = image->userdata;
524 }
525
526 r = sd_bus_message_read_strv(message, &matches);
527 if (r < 0)
528 return r;
529
530 r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
531 if (r < 0)
532 return r;
533
534 if (streq(copy_mode, "symlink"))
535 flags |= PORTABLE_PREFER_SYMLINK;
536 else if (streq(copy_mode, "copy"))
537 flags |= PORTABLE_PREFER_COPY;
538 else if (!isempty(copy_mode))
539 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
540
541 if (runtime)
542 flags |= PORTABLE_RUNTIME;
543
544 r = bus_image_acquire(m,
545 message,
546 name_or_path,
547 image,
548 BUS_IMAGE_AUTHENTICATE_ALL,
549 "org.freedesktop.portable1.attach-images",
550 &image,
551 error);
552 if (r < 0)
553 return r;
554 if (r == 0) /* Will call us back */
555 return 1;
556
557 r = portable_detach(
558 sd_bus_message_get_bus(message),
559 image->path,
560 flags,
561 &changes_detached,
562 &n_changes_detached,
563 error);
564 if (r < 0)
565 goto finish;
566
567 r = portable_attach(
568 sd_bus_message_get_bus(message),
569 image->path,
570 matches,
571 profile,
572 flags,
573 &changes_attached,
574 &n_changes_attached,
575 error);
576 if (r < 0)
577 goto finish;
578
579 /* We want to return the list of units really removed by the detach,
580 * and not added again by the attach */
581 r = normalize_portable_changes(changes_attached, n_changes_attached,
582 changes_detached, n_changes_detached,
583 &changes_gone, &n_changes_gone);
584 if (r < 0)
585 goto finish;
586
587 /* First, return the units that are gone (so that the caller can stop them)
588 * Then, return the units that are changed/added (so that the caller can
589 * start/restart/enable them) */
590 r = reply_portable_changes_pair(message,
591 changes_gone, n_changes_gone,
592 changes_attached, n_changes_attached);
593 if (r < 0)
594 goto finish;
595
596finish:
597 portable_changes_free(changes_detached, n_changes_detached);
598 portable_changes_free(changes_attached, n_changes_attached);
599 portable_changes_free(changes_gone, n_changes_gone);
600 return r;
601}
602
603static int bus_image_method_reattach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
604 return bus_image_common_reattach(NULL, message, NULL, userdata, error);
605}
606
61d0578b
LP
607int bus_image_common_mark_read_only(
608 Manager *m,
609 sd_bus_message *message,
610 const char *name_or_path,
611 Image *image,
612 sd_bus_error *error) {
613
614 int r, read_only;
615
616 assert(message);
617 assert(name_or_path || image);
618
619 if (!m) {
620 assert(image);
621 m = image->userdata;
622 }
623
624 r = sd_bus_message_read(message, "b", &read_only);
625 if (r < 0)
626 return r;
627
628 r = bus_image_acquire(m,
629 message,
630 name_or_path,
631 image,
632 BUS_IMAGE_AUTHENTICATE_ALL,
633 "org.freedesktop.portable1.manage-images",
634 &image,
635 error);
636 if (r < 0)
637 return r;
638 if (r == 0)
639 return 1; /* Will call us back */
640
641 r = image_read_only(image, read_only);
642 if (r < 0)
643 return r;
644
645 return sd_bus_reply_method_return(message, NULL);
646}
647
648static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
649 return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
650}
651
652int bus_image_common_set_limit(
653 Manager *m,
654 sd_bus_message *message,
655 const char *name_or_path,
656 Image *image,
657 sd_bus_error *error) {
658
659 uint64_t limit;
660 int r;
661
662 assert(message);
663 assert(name_or_path || image);
664
665 if (!m) {
666 assert(image);
667 m = image->userdata;
668 }
669
670 r = sd_bus_message_read(message, "t", &limit);
671 if (r < 0)
672 return r;
673 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
674 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
675
676 r = bus_image_acquire(m,
677 message,
678 name_or_path,
679 image,
680 BUS_IMAGE_AUTHENTICATE_ALL,
681 "org.freedesktop.portable1.manage-images",
682 &image,
683 error);
684 if (r < 0)
685 return r;
686 if (r == 0)
687 return 1; /* Will call us back */
688
689 r = image_set_limit(image, limit);
690 if (r < 0)
691 return r;
692
693 return sd_bus_reply_method_return(message, NULL);
694}
695
696static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
697 return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
698}
699
700const sd_bus_vtable image_vtable[] = {
701 SD_BUS_VTABLE_START(0),
702 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
703 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
704 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
705 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
706 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
707 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
708 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
709 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
710 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
711 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
598f938e
LB
712 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
713 SD_BUS_NO_ARGS,
714 SD_BUS_RESULT("a{ss}", os_release),
715 bus_image_method_get_os_release,
716 SD_BUS_VTABLE_UNPRIVILEGED),
717 SD_BUS_METHOD_WITH_ARGS("GetMetadata",
718 SD_BUS_ARGS("as", matches),
719 SD_BUS_RESULT("s", image,
720 "ay", os_release,
721 "a{say}", units),
722 bus_image_method_get_metadata,
723 SD_BUS_VTABLE_UNPRIVILEGED),
724 SD_BUS_METHOD_WITH_ARGS("GetState",
725 SD_BUS_NO_ARGS,
726 SD_BUS_RESULT("s", state),
727 bus_image_method_get_state,
728 SD_BUS_VTABLE_UNPRIVILEGED),
729 SD_BUS_METHOD_WITH_ARGS("Attach",
730 SD_BUS_ARGS("as", matches,
731 "s", profile,
732 "b", runtime,
733 "s", copy_mode),
734 SD_BUS_RESULT("a(sss)", changes),
735 bus_image_method_attach,
736 SD_BUS_VTABLE_UNPRIVILEGED),
737 SD_BUS_METHOD_WITH_ARGS("Detach",
738 SD_BUS_ARGS("b", runtime),
739 SD_BUS_RESULT("a(sss)", changes),
740 bus_image_method_detach,
741 SD_BUS_VTABLE_UNPRIVILEGED),
742 SD_BUS_METHOD_WITH_ARGS("Reattach",
743 SD_BUS_ARGS("as", matches,
744 "s", profile,
745 "b", runtime,
746 "s", copy_mode),
747 SD_BUS_RESULT("a(sss)", changes_removed,
748 "a(sss)", changes_updated),
749 bus_image_method_reattach,
750 SD_BUS_VTABLE_UNPRIVILEGED),
751 SD_BUS_METHOD_WITH_ARGS("Remove",
752 SD_BUS_NO_ARGS,
753 SD_BUS_NO_RESULT,
754 bus_image_method_remove,
755 SD_BUS_VTABLE_UNPRIVILEGED),
756 SD_BUS_METHOD_WITH_ARGS("MarkReadOnly",
757 SD_BUS_ARGS("b", read_only),
758 SD_BUS_NO_RESULT,
759 bus_image_method_mark_read_only,
760 SD_BUS_VTABLE_UNPRIVILEGED),
761 SD_BUS_METHOD_WITH_ARGS("SetLimit",
762 SD_BUS_ARGS("t", limit),
763 SD_BUS_NO_RESULT,
764 bus_image_method_set_limit,
765 SD_BUS_VTABLE_UNPRIVILEGED),
61d0578b
LP
766 SD_BUS_VTABLE_END
767};
768
769int bus_image_path(Image *image, char **ret) {
770 assert(image);
771 assert(ret);
772
773 if (!image->discoverable)
774 return -EINVAL;
775
776 return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
777}
778
779int bus_image_acquire(
780 Manager *m,
781 sd_bus_message *message,
782 const char *name_or_path,
783 Image *image,
784 ImageAcquireMode mode,
785 const char *polkit_action,
786 Image **ret,
787 sd_bus_error *error) {
788
789 _cleanup_(image_unrefp) Image *loaded = NULL;
790 Image *cached;
791 int r;
792
793 assert(m);
794 assert(message);
795 assert(name_or_path || image);
796 assert(mode >= 0);
797 assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
798 assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
799 assert(ret);
800
801 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
802
803 if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
804 r = bus_verify_polkit_async(
805 message,
806 CAP_SYS_ADMIN,
807 polkit_action,
808 NULL,
809 false,
810 UID_INVALID,
811 &m->polkit_registry,
812 error);
813 if (r < 0)
814 return r;
815 if (r == 0) { /* Will call us back */
816 *ret = NULL;
817 return 0;
818 }
819 }
820
821 /* Already passed in? */
822 if (image) {
823 *ret = image;
824 return 1;
825 }
826
827 /* Let's see if this image is already cached? */
828 cached = manager_image_cache_get(m, name_or_path);
829 if (cached) {
830 *ret = cached;
831 return 1;
832 }
833
834 if (image_name_is_valid(name_or_path)) {
835
836 /* If it's a short name, let's search for it */
d577d4a4 837 r = image_find(IMAGE_PORTABLE, name_or_path, NULL, &loaded);
61d0578b
LP
838 if (r == -ENOENT)
839 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
840
841 /* other errors are handled below… */
842 } else {
843 /* Don't accept path if this is always forbidden */
844 if (mode == BUS_IMAGE_REFUSE_BY_PATH)
845 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path);
846
847 if (!path_is_absolute(name_or_path))
848 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);
849
850 if (!path_is_normalized(name_or_path))
851 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path);
852
853 if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
854 r = bus_verify_polkit_async(
855 message,
856 CAP_SYS_ADMIN,
857 polkit_action,
858 NULL,
859 false,
860 UID_INVALID,
861 &m->polkit_registry,
862 error);
863 if (r < 0)
864 return r;
865 if (r == 0) { /* Will call us back */
866 *ret = NULL;
867 return 0;
868 }
869 }
870
871 r = image_from_path(name_or_path, &loaded);
872 }
787dfb82
LP
873 if (r == -EMEDIUMTYPE) {
874 sd_bus_error_setf(error, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, "Typ of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.", name_or_path);
875 return r;
876 }
61d0578b
LP
877 if (r < 0)
878 return r;
879
880 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
881 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
882 * means we don't actually need to ref the return object. */
883 r = manager_image_cache_add(m, loaded);
884 if (r < 0)
885 return r;
886
887 *ret = loaded;
888 return 1;
889}
890
891int bus_image_object_find(
892 sd_bus *bus,
893 const char *path,
894 const char *interface,
895 void *userdata,
896 void **found,
897 sd_bus_error *error) {
898
899 _cleanup_free_ char *e = NULL;
900 Manager *m = userdata;
901 Image *image = NULL;
902 int r;
903
904 assert(bus);
905 assert(path);
906 assert(interface);
907 assert(found);
908
909 r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
910 if (r < 0)
911 return 0;
912 if (r == 0)
913 goto not_found;
914
915 r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
916 if (r == -ENOENT)
917 goto not_found;
918 if (r < 0)
919 return r;
920
921 *found = image;
922 return 1;
923
924not_found:
925 *found = NULL;
926 return 0;
927}
928
929int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
b07ec5a1 930 _cleanup_hashmap_free_ Hashmap *images = NULL;
61d0578b
LP
931 _cleanup_strv_free_ char **l = NULL;
932 size_t n_allocated = 0, n = 0;
933 Manager *m = userdata;
934 Image *image;
61d0578b
LP
935 int r;
936
937 assert(bus);
938 assert(path);
939 assert(nodes);
940
b07ec5a1 941 images = hashmap_new(&image_hash_ops);
61d0578b
LP
942 if (!images)
943 return -ENOMEM;
944
945 r = manager_image_cache_discover(m, images, error);
946 if (r < 0)
947 return r;
948
90e74a66 949 HASHMAP_FOREACH(image, images) {
61d0578b
LP
950 char *p;
951
952 r = bus_image_path(image, &p);
953 if (r < 0)
954 return r;
955
956 if (!GREEDY_REALLOC(l, n_allocated, n+2)) {
957 free(p);
958 return -ENOMEM;
959 }
960
961 l[n++] = p;
962 l[n] = NULL;
963 }
964
965 *nodes = TAKE_PTR(l);
966
967 return 1;
968}
3b91bae3
LB
969
970const BusObjectImplementation image_object = {
971 "/org/freedesktop/portable1/image",
972 "org.freedesktop.portable1.Image",
973 .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, bus_image_object_find}),
974 .node_enumerator = bus_image_node_enumerator,
975};