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