]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/portable/portabled-image-bus.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[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) {
84be0c71 63 r = image_read_metadata(image, &image_policy_service);
61d0578b
LP
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;
907952bb 78 size_t n = 0;
61d0578b
LP
79 int r;
80
81 assert(m);
61d0578b 82
907952bb
LB
83 if (d) {
84 assert(d->fd >= 0);
61d0578b 85
fe008489
YW
86 r = fdopen_independent(d->fd, "r", &f);
87 if (r < 0)
88 return r;
907952bb
LB
89
90 r = read_full_stream(f, &buf, &n);
91 if (r < 0)
92 return r;
93 }
61d0578b
LP
94
95 return sd_bus_message_append_array(m, 'y', buf, n);
96}
97
98int bus_image_common_get_metadata(
99 Manager *m,
100 sd_bus_message *message,
101 const char *name_or_path,
102 Image *image,
103 sd_bus_error *error) {
104
e3f7ed94 105 _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_releases = NULL;
61d0578b 106 _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
907952bb 107 _cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
67818055 108 _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
61d0578b
LP
109 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
110 _cleanup_free_ PortableMetadata **sorted = NULL;
06768b90 111 PortableFlags flags = 0;
61d0578b
LP
112 int r;
113
114 assert(name_or_path || image);
115 assert(message);
116
117 if (!m) {
118 assert(image);
119 m = image->userdata;
120 }
121
087a799f
ZJS
122 bool have_exti = sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
123 sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions");
124
125 if (have_exti) {
907952bb
LB
126 r = sd_bus_message_read_strv(message, &extension_images);
127 if (r < 0)
128 return r;
129 }
130
61d0578b
LP
131 r = sd_bus_message_read_strv(message, &matches);
132 if (r < 0)
133 return r;
134
087a799f 135 if (have_exti) {
e3f7ed94
LB
136 uint64_t input_flags = 0;
137
907952bb
LB
138 r = sd_bus_message_read(message, "t", &input_flags);
139 if (r < 0)
140 return r;
e3f7ed94
LB
141
142 if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
907952bb
LB
143 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
144 "Invalid 'flags' parameter '%" PRIu64 "'",
145 input_flags);
06768b90 146 flags |= input_flags;
907952bb
LB
147 }
148
61d0578b
LP
149 r = bus_image_acquire(m,
150 message,
151 name_or_path,
152 image,
153 BUS_IMAGE_AUTHENTICATE_BY_PATH,
154 "org.freedesktop.portable1.inspect-images",
155 &image,
156 error);
157 if (r < 0)
158 return r;
159 if (r == 0) /* Will call us back */
160 return 1;
161
162 r = portable_extract(
163 image->path,
164 matches,
907952bb 165 extension_images,
84be0c71 166 /* image_policy= */ NULL,
06768b90 167 flags,
61d0578b 168 &os_release,
e3f7ed94 169 &extension_releases,
61d0578b 170 &unit_files,
8a129c80 171 NULL,
61d0578b
LP
172 error);
173 if (r < 0)
174 return r;
175
176 r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
177 if (r < 0)
178 return r;
179
180 r = sd_bus_message_new_method_return(message, &reply);
181 if (r < 0)
182 return r;
183
184 r = sd_bus_message_append(reply, "s", image->path);
185 if (r < 0)
186 return r;
187
188 r = append_fd(reply, os_release);
189 if (r < 0)
190 return r;
191
e3f7ed94
LB
192 /* If it was requested, also send back the extension path and the content
193 * of each extension-release file. Behind a flag, as it's an incompatible
194 * change. */
087a799f 195 if (have_exti) {
e3f7ed94
LB
196 PortableMetadata *extension_release;
197
087a799f
ZJS
198 r = sd_bus_message_open_container(reply, 'a', "{say}");
199 if (r < 0)
200 return r;
201
e3f7ed94
LB
202 ORDERED_HASHMAP_FOREACH(extension_release, extension_releases) {
203
204 r = sd_bus_message_open_container(reply, 'e', "say");
205 if (r < 0)
206 return r;
207
208 r = sd_bus_message_append(reply, "s", extension_release->image_path);
209 if (r < 0)
210 return r;
211
212 r = append_fd(reply, extension_release);
213 if (r < 0)
214 return r;
215
216 r = sd_bus_message_close_container(reply);
217 if (r < 0)
218 return r;
219 }
087a799f
ZJS
220
221 r = sd_bus_message_close_container(reply);
222 if (r < 0)
223 return r;
e3f7ed94
LB
224 }
225
087a799f
ZJS
226 r = sd_bus_message_open_container(reply, 'a', "{say}");
227 if (r < 0)
228 return r;
229
90e3f358 230 for (size_t i = 0; i < hashmap_size(unit_files); i++) {
61d0578b 231
61d0578b
LP
232 r = sd_bus_message_open_container(reply, 'e', "say");
233 if (r < 0)
234 return r;
235
236 r = sd_bus_message_append(reply, "s", sorted[i]->name);
237 if (r < 0)
238 return r;
239
240 r = append_fd(reply, sorted[i]);
241 if (r < 0)
242 return r;
243
244 r = sd_bus_message_close_container(reply);
245 if (r < 0)
246 return r;
247 }
248
249 r = sd_bus_message_close_container(reply);
250 if (r < 0)
251 return r;
252
253 return sd_bus_send(NULL, reply, NULL);
254}
255
256static int bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
257 return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
258}
259
260static int bus_image_method_get_state(
261 sd_bus_message *message,
262 void *userdata,
263 sd_bus_error *error) {
264
0017415c 265 _cleanup_strv_free_ char **extension_images = NULL;
99534007 266 Image *image = ASSERT_PTR(userdata);
61d0578b
LP
267 PortableState state;
268 int r;
269
270 assert(message);
61d0578b 271
0017415c
LB
272 if (sd_bus_message_is_method_call(message, NULL, "GetStateWithExtensions")) {
273 uint64_t input_flags = 0;
274
275 r = sd_bus_message_read_strv(message, &extension_images);
276 if (r < 0)
277 return r;
278
279 r = sd_bus_message_read(message, "t", &input_flags);
280 if (r < 0)
281 return r;
282
283 /* No flags are supported by this method for now. */
284 if (input_flags != 0)
285 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
286 "Invalid 'flags' parameter '%" PRIu64 "'",
287 input_flags);
288 }
289
61d0578b
LP
290 r = portable_get_state(
291 sd_bus_message_get_bus(message),
292 image->path,
0017415c 293 extension_images,
61d0578b
LP
294 0,
295 &state,
296 error);
297 if (r < 0)
298 return r;
299
300 return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
301}
302
303int bus_image_common_attach(
304 Manager *m,
305 sd_bus_message *message,
306 const char *name_or_path,
307 Image *image,
308 sd_bus_error *error) {
309
907952bb 310 _cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
61d0578b
LP
311 PortableChange *changes = NULL;
312 PortableFlags flags = 0;
c5a935b0 313 const char *profile, *copy_mode;
61d0578b 314 size_t n_changes = 0;
907952bb 315 int r;
61d0578b
LP
316
317 assert(message);
318 assert(name_or_path || image);
319
2b4b01b0
DT
320 CLEANUP_ARRAY(changes, n_changes, portable_changes_free);
321
61d0578b
LP
322 if (!m) {
323 assert(image);
324 m = image->userdata;
325 }
326
907952bb
LB
327 if (sd_bus_message_is_method_call(message, NULL, "AttachImageWithExtensions") ||
328 sd_bus_message_is_method_call(message, NULL, "AttachWithExtensions")) {
329 r = sd_bus_message_read_strv(message, &extension_images);
330 if (r < 0)
331 return r;
332 }
333
61d0578b
LP
334 r = sd_bus_message_read_strv(message, &matches);
335 if (r < 0)
336 return r;
337
907952bb 338 r = sd_bus_message_read(message, "s", &profile);
61d0578b
LP
339 if (r < 0)
340 return r;
341
907952bb
LB
342 if (sd_bus_message_is_method_call(message, NULL, "AttachImageWithExtensions") ||
343 sd_bus_message_is_method_call(message, NULL, "AttachWithExtensions")) {
344 uint64_t input_flags = 0;
345
346 r = sd_bus_message_read(message, "st", &copy_mode, &input_flags);
347 if (r < 0)
348 return r;
349 if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
350 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
351 "Invalid 'flags' parameter '%" PRIu64 "'",
352 input_flags);
353 flags |= input_flags;
354 } else {
355 int runtime;
356
357 r = sd_bus_message_read(message, "bs", &runtime, &copy_mode);
358 if (r < 0)
359 return r;
360
361 if (runtime)
362 flags |= PORTABLE_RUNTIME;
363 }
364
61d0578b
LP
365 if (streq(copy_mode, "symlink"))
366 flags |= PORTABLE_PREFER_SYMLINK;
367 else if (streq(copy_mode, "copy"))
368 flags |= PORTABLE_PREFER_COPY;
369 else if (!isempty(copy_mode))
370 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
371
61d0578b
LP
372 r = bus_image_acquire(m,
373 message,
374 name_or_path,
375 image,
376 BUS_IMAGE_AUTHENTICATE_ALL,
377 "org.freedesktop.portable1.attach-images",
378 &image,
379 error);
380 if (r < 0)
381 return r;
382 if (r == 0) /* Will call us back */
383 return 1;
384
385 r = portable_attach(
386 sd_bus_message_get_bus(message),
387 image->path,
388 matches,
389 profile,
907952bb 390 extension_images,
84be0c71 391 /* image_policy= */ NULL,
61d0578b
LP
392 flags,
393 &changes,
394 &n_changes,
395 error);
396 if (r < 0)
2b4b01b0 397 return r;
61d0578b 398
2b4b01b0 399 return reply_portable_changes(message, changes, n_changes);
61d0578b
LP
400}
401
402static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
403 return bus_image_common_attach(NULL, message, NULL, userdata, error);
404}
405
406static int bus_image_method_detach(
407 sd_bus_message *message,
408 void *userdata,
409 sd_bus_error *error) {
410
907952bb 411 _cleanup_strv_free_ char **extension_images = NULL;
61d0578b 412 PortableChange *changes = NULL;
99534007
DT
413 Image *image = ASSERT_PTR(userdata);
414 Manager *m = ASSERT_PTR(image->userdata);
907952bb 415 PortableFlags flags = 0;
61d0578b 416 size_t n_changes = 0;
907952bb 417 int r;
61d0578b
LP
418
419 assert(message);
61d0578b 420
a5290eff
DT
421 CLEANUP_ARRAY(changes, n_changes, portable_changes_free);
422
907952bb
LB
423 if (sd_bus_message_is_method_call(message, NULL, "DetachWithExtensions")) {
424 r = sd_bus_message_read_strv(message, &extension_images);
425 if (r < 0)
426 return r;
427 }
428
429 if (sd_bus_message_is_method_call(message, NULL, "DetachWithExtensions")) {
430 uint64_t input_flags = 0;
431
432 r = sd_bus_message_read(message, "t", &input_flags);
433 if (r < 0)
434 return r;
435
436 if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
437 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
438 "Invalid 'flags' parameter '%" PRIu64 "'",
439 input_flags);
440 flags |= input_flags;
441 } else {
442 int runtime;
443
444 r = sd_bus_message_read(message, "b", &runtime);
445 if (r < 0)
446 return r;
447
448 if (runtime)
449 flags |= PORTABLE_RUNTIME;
450 }
61d0578b
LP
451
452 r = bus_verify_polkit_async(
453 message,
61d0578b 454 "org.freedesktop.portable1.attach-images",
7b36fb9f 455 /* details= */ NULL,
61d0578b
LP
456 &m->polkit_registry,
457 error);
458 if (r < 0)
459 return r;
460 if (r == 0)
461 return 1; /* Will call us back */
462
463 r = portable_detach(
464 sd_bus_message_get_bus(message),
465 image->path,
907952bb
LB
466 extension_images,
467 flags,
61d0578b
LP
468 &changes,
469 &n_changes,
470 error);
471 if (r < 0)
a5290eff 472 return r;
61d0578b 473
a5290eff 474 return reply_portable_changes(message, changes, n_changes);
61d0578b
LP
475}
476
477int bus_image_common_remove(
478 Manager *m,
479 sd_bus_message *message,
480 const char *name_or_path,
481 Image *image,
482 sd_bus_error *error) {
483
71136404 484 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
61d0578b
LP
485 _cleanup_(sigkill_waitp) pid_t child = 0;
486 PortableState state;
487 int r;
488
489 assert(message);
490 assert(name_or_path || image);
491
492 if (!m) {
493 assert(image);
494 m = image->userdata;
495 }
496
497 if (m->n_operations >= OPERATIONS_MAX)
1b09b81c 498 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
61d0578b
LP
499
500 r = bus_image_acquire(m,
501 message,
502 name_or_path,
503 image,
504 BUS_IMAGE_AUTHENTICATE_ALL,
505 "org.freedesktop.portable1.manage-images",
506 &image,
507 error);
508 if (r < 0)
509 return r;
510 if (r == 0)
511 return 1; /* Will call us back */
512
513 r = portable_get_state(
514 sd_bus_message_get_bus(message),
515 image->path,
0017415c 516 NULL,
61d0578b
LP
517 0,
518 &state,
519 error);
520 if (r < 0)
521 return r;
522
523 if (state != PORTABLE_DETACHED)
524 return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
525
526 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
527 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
528
529 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
530 if (r < 0)
531 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
532 if (r == 0) {
533 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
534
535 r = image_remove(image);
536 if (r < 0) {
537 (void) write(errno_pipe_fd[1], &r, sizeof(r));
538 _exit(EXIT_FAILURE);
539 }
540
541 _exit(EXIT_SUCCESS);
542 }
543
544 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
545
546 r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
547 if (r < 0)
548 return r;
549
550 child = 0;
254d1313 551 errno_pipe_fd[0] = -EBADF;
61d0578b
LP
552
553 return 1;
554}
555
556static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
557 return bus_image_common_remove(NULL, message, NULL, userdata, error);
558}
559
e26fe5f9
LB
560/* Given two PortableChange arrays, return a new array that has all elements of the first that are
561 * not also present in the second, comparing the basename of the path values. */
562static int normalize_portable_changes(
563 const PortableChange *changes_attached,
564 size_t n_changes_attached,
565 const PortableChange *changes_detached,
566 size_t n_changes_detached,
567 PortableChange **ret_changes,
568 size_t *ret_n_changes) {
569
570 PortableChange *changes = NULL;
571 size_t n_changes = 0;
e26fe5f9
LB
572
573 assert(ret_n_changes);
574 assert(ret_changes);
575
576 if (n_changes_detached == 0)
577 return 0; /* Nothing to do */
578
579 changes = new0(PortableChange, n_changes_attached + n_changes_detached);
580 if (!changes)
581 return -ENOMEM;
582
8c2d0d3a
LP
583 CLEANUP_ARRAY(changes, n_changes, portable_changes_free);
584
e26fe5f9
LB
585 /* Corner case: only detached, nothing attached */
586 if (n_changes_attached == 0) {
587 memcpy(changes, changes_detached, sizeof(PortableChange) * n_changes_detached);
588 *ret_changes = TAKE_PTR(changes);
589 *ret_n_changes = n_changes_detached;
e26fe5f9
LB
590 return 0;
591 }
592
593 for (size_t i = 0; i < n_changes_detached; ++i) {
594 bool found = false;
595
596 for (size_t j = 0; j < n_changes_attached; ++j)
597 if (streq(basename(changes_detached[i].path), basename(changes_attached[j].path))) {
598 found = true;
599 break;
600 }
601
602 if (!found) {
603 _cleanup_free_ char *path = NULL, *source = NULL;
604
605 path = strdup(changes_detached[i].path);
8c2d0d3a
LP
606 if (!path)
607 return -ENOMEM;
e26fe5f9
LB
608
609 if (changes_detached[i].source) {
610 source = strdup(changes_detached[i].source);
8c2d0d3a
LP
611 if (!source)
612 return -ENOMEM;
e26fe5f9
LB
613 }
614
615 changes[n_changes++] = (PortableChange) {
ba5b6c59 616 .type_or_errno = changes_detached[i].type_or_errno,
e26fe5f9
LB
617 .path = TAKE_PTR(path),
618 .source = TAKE_PTR(source),
619 };
620 }
621 }
622
623 *ret_n_changes = n_changes;
624 *ret_changes = TAKE_PTR(changes);
625
626 return 0;
e26fe5f9
LB
627}
628
629int bus_image_common_reattach(
630 Manager *m,
631 sd_bus_message *message,
632 const char *name_or_path,
633 Image *image,
634 sd_bus_error *error) {
635
636 PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL;
637 size_t n_changes_detached = 0, n_changes_attached = 0, n_changes_gone = 0;
907952bb 638 _cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
e26fe5f9
LB
639 PortableFlags flags = PORTABLE_REATTACH;
640 const char *profile, *copy_mode;
907952bb 641 int r;
e26fe5f9
LB
642
643 assert(message);
644 assert(name_or_path || image);
645
f86a4129
DT
646 CLEANUP_ARRAY(changes_detached, n_changes_detached, portable_changes_free);
647 CLEANUP_ARRAY(changes_attached, n_changes_attached, portable_changes_free);
648 CLEANUP_ARRAY(changes_gone, n_changes_gone, portable_changes_free);
649
e26fe5f9
LB
650 if (!m) {
651 assert(image);
652 m = image->userdata;
653 }
654
907952bb
LB
655 if (sd_bus_message_is_method_call(message, NULL, "ReattachImageWithExtensions") ||
656 sd_bus_message_is_method_call(message, NULL, "ReattachWithExtensions")) {
657 r = sd_bus_message_read_strv(message, &extension_images);
658 if (r < 0)
659 return r;
660 }
661
e26fe5f9
LB
662 r = sd_bus_message_read_strv(message, &matches);
663 if (r < 0)
664 return r;
665
907952bb 666 r = sd_bus_message_read(message, "s", &profile);
e26fe5f9
LB
667 if (r < 0)
668 return r;
669
907952bb
LB
670 if (sd_bus_message_is_method_call(message, NULL, "ReattachImageWithExtensions") ||
671 sd_bus_message_is_method_call(message, NULL, "ReattachWithExtensions")) {
672 uint64_t input_flags = 0;
673
674 r = sd_bus_message_read(message, "st", &copy_mode, &input_flags);
675 if (r < 0)
676 return r;
677
678 if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
679 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
680 "Invalid 'flags' parameter '%" PRIu64 "'",
681 input_flags);
682 flags |= input_flags;
683 } else {
684 int runtime;
685
686 r = sd_bus_message_read(message, "bs", &runtime, &copy_mode);
687 if (r < 0)
688 return r;
689
690 if (runtime)
691 flags |= PORTABLE_RUNTIME;
692 }
693
e26fe5f9
LB
694 if (streq(copy_mode, "symlink"))
695 flags |= PORTABLE_PREFER_SYMLINK;
696 else if (streq(copy_mode, "copy"))
697 flags |= PORTABLE_PREFER_COPY;
698 else if (!isempty(copy_mode))
699 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
700
e26fe5f9
LB
701 r = bus_image_acquire(m,
702 message,
703 name_or_path,
704 image,
705 BUS_IMAGE_AUTHENTICATE_ALL,
706 "org.freedesktop.portable1.attach-images",
707 &image,
708 error);
709 if (r < 0)
710 return r;
711 if (r == 0) /* Will call us back */
712 return 1;
713
714 r = portable_detach(
715 sd_bus_message_get_bus(message),
716 image->path,
907952bb 717 extension_images,
e26fe5f9
LB
718 flags,
719 &changes_detached,
720 &n_changes_detached,
721 error);
722 if (r < 0)
f86a4129 723 return r;
e26fe5f9
LB
724
725 r = portable_attach(
726 sd_bus_message_get_bus(message),
727 image->path,
728 matches,
729 profile,
907952bb 730 extension_images,
84be0c71 731 /* image_policy= */ NULL,
e26fe5f9
LB
732 flags,
733 &changes_attached,
734 &n_changes_attached,
735 error);
736 if (r < 0)
f86a4129 737 return r;
e26fe5f9
LB
738
739 /* We want to return the list of units really removed by the detach,
740 * and not added again by the attach */
741 r = normalize_portable_changes(changes_attached, n_changes_attached,
742 changes_detached, n_changes_detached,
743 &changes_gone, &n_changes_gone);
744 if (r < 0)
f86a4129 745 return r;
e26fe5f9
LB
746
747 /* First, return the units that are gone (so that the caller can stop them)
748 * Then, return the units that are changed/added (so that the caller can
749 * start/restart/enable them) */
f86a4129
DT
750 return reply_portable_changes_pair(message,
751 changes_gone, n_changes_gone,
752 changes_attached, n_changes_attached);
e26fe5f9
LB
753}
754
755static int bus_image_method_reattach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
756 return bus_image_common_reattach(NULL, message, NULL, userdata, error);
757}
758
61d0578b
LP
759int bus_image_common_mark_read_only(
760 Manager *m,
761 sd_bus_message *message,
762 const char *name_or_path,
763 Image *image,
764 sd_bus_error *error) {
765
766 int r, read_only;
767
768 assert(message);
769 assert(name_or_path || image);
770
771 if (!m) {
772 assert(image);
773 m = image->userdata;
774 }
775
776 r = sd_bus_message_read(message, "b", &read_only);
777 if (r < 0)
778 return r;
779
780 r = bus_image_acquire(m,
781 message,
782 name_or_path,
783 image,
784 BUS_IMAGE_AUTHENTICATE_ALL,
785 "org.freedesktop.portable1.manage-images",
786 &image,
787 error);
788 if (r < 0)
789 return r;
790 if (r == 0)
791 return 1; /* Will call us back */
792
793 r = image_read_only(image, read_only);
794 if (r < 0)
795 return r;
796
797 return sd_bus_reply_method_return(message, NULL);
798}
799
800static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
801 return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
802}
803
804int bus_image_common_set_limit(
805 Manager *m,
806 sd_bus_message *message,
807 const char *name_or_path,
808 Image *image,
809 sd_bus_error *error) {
810
811 uint64_t limit;
812 int r;
813
814 assert(message);
815 assert(name_or_path || image);
816
817 if (!m) {
818 assert(image);
819 m = image->userdata;
820 }
821
822 r = sd_bus_message_read(message, "t", &limit);
823 if (r < 0)
824 return r;
825 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
1b09b81c 826 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
61d0578b
LP
827
828 r = bus_image_acquire(m,
829 message,
830 name_or_path,
831 image,
832 BUS_IMAGE_AUTHENTICATE_ALL,
833 "org.freedesktop.portable1.manage-images",
834 &image,
835 error);
836 if (r < 0)
837 return r;
838 if (r == 0)
839 return 1; /* Will call us back */
840
841 r = image_set_limit(image, limit);
842 if (r < 0)
843 return r;
844
845 return sd_bus_reply_method_return(message, NULL);
846}
847
848static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
849 return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
850}
851
852const sd_bus_vtable image_vtable[] = {
853 SD_BUS_VTABLE_START(0),
854 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
855 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
856 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
857 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
858 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
859 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
860 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
861 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
862 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
863 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
598f938e
LB
864 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
865 SD_BUS_NO_ARGS,
866 SD_BUS_RESULT("a{ss}", os_release),
867 bus_image_method_get_os_release,
868 SD_BUS_VTABLE_UNPRIVILEGED),
869 SD_BUS_METHOD_WITH_ARGS("GetMetadata",
870 SD_BUS_ARGS("as", matches),
871 SD_BUS_RESULT("s", image,
872 "ay", os_release,
873 "a{say}", units),
874 bus_image_method_get_metadata,
875 SD_BUS_VTABLE_UNPRIVILEGED),
907952bb
LB
876 SD_BUS_METHOD_WITH_ARGS("GetMetadataWithExtensions",
877 SD_BUS_ARGS("as", extensions,
878 "as", matches,
879 "t", flags),
880 SD_BUS_RESULT("s", image,
881 "ay", os_release,
087a799f 882 "a{say}", extensions,
907952bb
LB
883 "a{say}", units),
884 bus_image_method_get_metadata,
885 SD_BUS_VTABLE_UNPRIVILEGED),
598f938e
LB
886 SD_BUS_METHOD_WITH_ARGS("GetState",
887 SD_BUS_NO_ARGS,
888 SD_BUS_RESULT("s", state),
889 bus_image_method_get_state,
890 SD_BUS_VTABLE_UNPRIVILEGED),
0017415c
LB
891 SD_BUS_METHOD_WITH_ARGS("GetStateWithExtensions",
892 SD_BUS_ARGS("as", extensions,
893 "t", flags),
894 SD_BUS_RESULT("s", state),
895 bus_image_method_get_state,
896 SD_BUS_VTABLE_UNPRIVILEGED),
598f938e
LB
897 SD_BUS_METHOD_WITH_ARGS("Attach",
898 SD_BUS_ARGS("as", matches,
899 "s", profile,
900 "b", runtime,
901 "s", copy_mode),
902 SD_BUS_RESULT("a(sss)", changes),
903 bus_image_method_attach,
904 SD_BUS_VTABLE_UNPRIVILEGED),
907952bb
LB
905 SD_BUS_METHOD_WITH_ARGS("AttachWithExtensions",
906 SD_BUS_ARGS("as", extensions,
907 "as", matches,
908 "s", profile,
909 "s", copy_mode,
910 "t", flags),
911 SD_BUS_RESULT("a(sss)", changes),
912 bus_image_method_attach,
913 SD_BUS_VTABLE_UNPRIVILEGED),
598f938e
LB
914 SD_BUS_METHOD_WITH_ARGS("Detach",
915 SD_BUS_ARGS("b", runtime),
916 SD_BUS_RESULT("a(sss)", changes),
917 bus_image_method_detach,
918 SD_BUS_VTABLE_UNPRIVILEGED),
907952bb
LB
919 SD_BUS_METHOD_WITH_ARGS("DetachWithExtensions",
920 SD_BUS_ARGS("as", extensions,
921 "t", flags),
922 SD_BUS_RESULT("a(sss)", changes),
923 bus_image_method_detach,
924 SD_BUS_VTABLE_UNPRIVILEGED),
598f938e
LB
925 SD_BUS_METHOD_WITH_ARGS("Reattach",
926 SD_BUS_ARGS("as", matches,
927 "s", profile,
928 "b", runtime,
929 "s", copy_mode),
930 SD_BUS_RESULT("a(sss)", changes_removed,
931 "a(sss)", changes_updated),
932 bus_image_method_reattach,
933 SD_BUS_VTABLE_UNPRIVILEGED),
e0a067a2 934 SD_BUS_METHOD_WITH_ARGS("ReattachWithExtensions",
907952bb
LB
935 SD_BUS_ARGS("as", extensions,
936 "as", matches,
937 "s", profile,
938 "s", copy_mode,
939 "t", flags),
940 SD_BUS_RESULT("a(sss)", changes_removed,
941 "a(sss)", changes_updated),
942 bus_image_method_reattach,
943 SD_BUS_VTABLE_UNPRIVILEGED),
598f938e
LB
944 SD_BUS_METHOD_WITH_ARGS("Remove",
945 SD_BUS_NO_ARGS,
946 SD_BUS_NO_RESULT,
947 bus_image_method_remove,
948 SD_BUS_VTABLE_UNPRIVILEGED),
949 SD_BUS_METHOD_WITH_ARGS("MarkReadOnly",
950 SD_BUS_ARGS("b", read_only),
951 SD_BUS_NO_RESULT,
952 bus_image_method_mark_read_only,
953 SD_BUS_VTABLE_UNPRIVILEGED),
954 SD_BUS_METHOD_WITH_ARGS("SetLimit",
955 SD_BUS_ARGS("t", limit),
956 SD_BUS_NO_RESULT,
957 bus_image_method_set_limit,
958 SD_BUS_VTABLE_UNPRIVILEGED),
e0a067a2
LB
959 /* Deprecated silly typo */
960 SD_BUS_METHOD_WITH_ARGS("ReattacheWithExtensions",
961 SD_BUS_ARGS("as", extensions,
962 "as", matches,
963 "s", profile,
964 "s", copy_mode,
965 "t", flags),
966 SD_BUS_RESULT("a(sss)", changes_removed,
967 "a(sss)", changes_updated),
968 bus_image_method_reattach,
969 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
61d0578b
LP
970 SD_BUS_VTABLE_END
971};
972
973int bus_image_path(Image *image, char **ret) {
974 assert(image);
975 assert(ret);
976
977 if (!image->discoverable)
978 return -EINVAL;
979
980 return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
981}
982
983int bus_image_acquire(
984 Manager *m,
985 sd_bus_message *message,
986 const char *name_or_path,
987 Image *image,
988 ImageAcquireMode mode,
989 const char *polkit_action,
990 Image **ret,
991 sd_bus_error *error) {
992
993 _cleanup_(image_unrefp) Image *loaded = NULL;
994 Image *cached;
995 int r;
996
997 assert(m);
998 assert(message);
999 assert(name_or_path || image);
1000 assert(mode >= 0);
1001 assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
1002 assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
1003 assert(ret);
1004
1005 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
1006
1007 if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
1008 r = bus_verify_polkit_async(
1009 message,
61d0578b 1010 polkit_action,
7b36fb9f 1011 /* details= */ NULL,
61d0578b
LP
1012 &m->polkit_registry,
1013 error);
1014 if (r < 0)
1015 return r;
1016 if (r == 0) { /* Will call us back */
1017 *ret = NULL;
1018 return 0;
1019 }
1020 }
1021
1022 /* Already passed in? */
1023 if (image) {
1024 *ret = image;
1025 return 1;
1026 }
1027
1028 /* Let's see if this image is already cached? */
1029 cached = manager_image_cache_get(m, name_or_path);
1030 if (cached) {
1031 *ret = cached;
1032 return 1;
1033 }
1034
1035 if (image_name_is_valid(name_or_path)) {
1036
1037 /* If it's a short name, let's search for it */
d577d4a4 1038 r = image_find(IMAGE_PORTABLE, name_or_path, NULL, &loaded);
61d0578b 1039 if (r == -ENOENT)
5943d85f
ZJS
1040 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE,
1041 "No image '%s' found.", name_or_path);
61d0578b
LP
1042
1043 /* other errors are handled below… */
1044 } else {
1045 /* Don't accept path if this is always forbidden */
1046 if (mode == BUS_IMAGE_REFUSE_BY_PATH)
5943d85f
ZJS
1047 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1048 "Expected image name, not path in place of '%s'.", name_or_path);
61d0578b
LP
1049
1050 if (!path_is_absolute(name_or_path))
5943d85f
ZJS
1051 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1052 "Image name '%s' is not valid or not a valid path.", name_or_path);
61d0578b
LP
1053
1054 if (!path_is_normalized(name_or_path))
5943d85f
ZJS
1055 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1056 "Image path '%s' is not normalized.", name_or_path);
61d0578b
LP
1057
1058 if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
1059 r = bus_verify_polkit_async(
1060 message,
61d0578b 1061 polkit_action,
7b36fb9f 1062 /* details= */ NULL,
61d0578b
LP
1063 &m->polkit_registry,
1064 error);
1065 if (r < 0)
1066 return r;
1067 if (r == 0) { /* Will call us back */
1068 *ret = NULL;
1069 return 0;
1070 }
1071 }
1072
1073 r = image_from_path(name_or_path, &loaded);
1074 }
787dfb82 1075 if (r == -EMEDIUMTYPE) {
5943d85f
ZJS
1076 sd_bus_error_setf(error, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE,
1077 "Type of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.",
1078 name_or_path);
787dfb82
LP
1079 return r;
1080 }
61d0578b
LP
1081 if (r < 0)
1082 return r;
1083
1084 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
1085 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
1086 * means we don't actually need to ref the return object. */
1087 r = manager_image_cache_add(m, loaded);
1088 if (r < 0)
1089 return r;
1090
1091 *ret = loaded;
1092 return 1;
1093}
1094
1095int bus_image_object_find(
1096 sd_bus *bus,
1097 const char *path,
1098 const char *interface,
1099 void *userdata,
1100 void **found,
1101 sd_bus_error *error) {
1102
1103 _cleanup_free_ char *e = NULL;
1104 Manager *m = userdata;
1105 Image *image = NULL;
1106 int r;
1107
1108 assert(bus);
1109 assert(path);
1110 assert(interface);
1111 assert(found);
1112
1113 r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
1114 if (r < 0)
1115 return 0;
1116 if (r == 0)
1117 goto not_found;
4313e2b6
ZJS
1118 if (isempty(e))
1119 /* The path is "/org/freedesktop/portable1/image" itself */
1120 goto not_found;
61d0578b
LP
1121
1122 r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
1123 if (r == -ENOENT)
1124 goto not_found;
1125 if (r < 0)
1126 return r;
1127
1128 *found = image;
1129 return 1;
1130
1131not_found:
1132 *found = NULL;
1133 return 0;
1134}
1135
1136int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
b07ec5a1 1137 _cleanup_hashmap_free_ Hashmap *images = NULL;
61d0578b 1138 _cleanup_strv_free_ char **l = NULL;
61d0578b 1139 Manager *m = userdata;
319a4f4b 1140 size_t n = 0;
61d0578b 1141 Image *image;
61d0578b
LP
1142 int r;
1143
1144 assert(bus);
1145 assert(path);
1146 assert(nodes);
1147
b07ec5a1 1148 images = hashmap_new(&image_hash_ops);
61d0578b
LP
1149 if (!images)
1150 return -ENOMEM;
1151
1152 r = manager_image_cache_discover(m, images, error);
1153 if (r < 0)
1154 return r;
1155
90e74a66 1156 HASHMAP_FOREACH(image, images) {
61d0578b
LP
1157 char *p;
1158
1159 r = bus_image_path(image, &p);
1160 if (r < 0)
1161 return r;
1162
319a4f4b 1163 if (!GREEDY_REALLOC(l, n+2)) {
61d0578b
LP
1164 free(p);
1165 return -ENOMEM;
1166 }
1167
1168 l[n++] = p;
1169 l[n] = NULL;
1170 }
1171
1172 *nodes = TAKE_PTR(l);
1173
1174 return 1;
1175}
3b91bae3
LB
1176
1177const BusObjectImplementation image_object = {
1178 "/org/freedesktop/portable1/image",
1179 "org.freedesktop.portable1.Image",
1180 .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, bus_image_object_find}),
1181 .node_enumerator = bus_image_node_enumerator,
1182};