]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/portable/portabled-bus.c
Merge pull request #22175 from keszybz/kernel-install-mkosi-initrd
[thirdparty/systemd.git] / src / portable / portabled-bus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "btrfs-util.h"
5 #include "bus-common-errors.h"
6 #include "bus-object.h"
7 #include "bus-polkit.h"
8 #include "discover-image.h"
9 #include "fd-util.h"
10 #include "io-util.h"
11 #include "missing_capability.h"
12 #include "portable.h"
13 #include "portabled-bus.h"
14 #include "portabled-image-bus.h"
15 #include "portabled-image.h"
16 #include "portabled.h"
17 #include "strv.h"
18 #include "user-util.h"
19
20 static int property_get_pool_path(
21 sd_bus *bus,
22 const char *path,
23 const char *interface,
24 const char *property,
25 sd_bus_message *reply,
26 void *userdata,
27 sd_bus_error *error) {
28
29 assert(bus);
30 assert(reply);
31
32 return sd_bus_message_append(reply, "s", "/var/lib/portables");
33 }
34
35 static int property_get_pool_usage(
36 sd_bus *bus,
37 const char *path,
38 const char *interface,
39 const char *property,
40 sd_bus_message *reply,
41 void *userdata,
42 sd_bus_error *error) {
43
44 _cleanup_close_ int fd = -1;
45 uint64_t usage = UINT64_MAX;
46
47 assert(bus);
48 assert(reply);
49
50 fd = open("/var/lib/portables", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
51 if (fd >= 0) {
52 BtrfsQuotaInfo q;
53
54 if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
55 usage = q.referenced;
56 }
57
58 return sd_bus_message_append(reply, "t", usage);
59 }
60
61 static int property_get_pool_limit(
62 sd_bus *bus,
63 const char *path,
64 const char *interface,
65 const char *property,
66 sd_bus_message *reply,
67 void *userdata,
68 sd_bus_error *error) {
69
70 _cleanup_close_ int fd = -1;
71 uint64_t size = UINT64_MAX;
72
73 assert(bus);
74 assert(reply);
75
76 fd = open("/var/lib/portables", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
77 if (fd >= 0) {
78 BtrfsQuotaInfo q;
79
80 if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
81 size = q.referenced_max;
82 }
83
84 return sd_bus_message_append(reply, "t", size);
85 }
86
87 static int property_get_profiles(
88 sd_bus *bus,
89 const char *path,
90 const char *interface,
91 const char *property,
92 sd_bus_message *reply,
93 void *userdata,
94 sd_bus_error *error) {
95
96 _cleanup_strv_free_ char **l = NULL;
97 int r;
98
99 assert(bus);
100 assert(reply);
101
102 r = portable_get_profiles(&l);
103 if (r < 0)
104 return r;
105
106 return sd_bus_message_append_strv(reply, l);
107 }
108
109 static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
110 _cleanup_free_ char *p = NULL;
111 Manager *m = userdata;
112 const char *name;
113 Image *image;
114 int r;
115
116 assert(message);
117 assert(m);
118
119 r = sd_bus_message_read(message, "s", &name);
120 if (r < 0)
121 return r;
122
123 r = bus_image_acquire(m, message, name, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
124 if (r < 0)
125 return r;
126
127 r = bus_image_path(image, &p);
128 if (r < 0)
129 return r;
130
131 return sd_bus_reply_method_return(message, "o", p);
132 }
133
134 static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
135 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
136 _cleanup_hashmap_free_ Hashmap *images = NULL;
137 Manager *m = userdata;
138 Image *image;
139 int r;
140
141 assert(message);
142 assert(m);
143
144 images = hashmap_new(&image_hash_ops);
145 if (!images)
146 return -ENOMEM;
147
148 r = manager_image_cache_discover(m, images, error);
149 if (r < 0)
150 return r;
151
152 r = sd_bus_message_new_method_return(message, &reply);
153 if (r < 0)
154 return r;
155
156 r = sd_bus_message_open_container(reply, 'a', "(ssbtttso)");
157 if (r < 0)
158 return r;
159
160 HASHMAP_FOREACH(image, images) {
161 _cleanup_(sd_bus_error_free) sd_bus_error error_state = SD_BUS_ERROR_NULL;
162 PortableState state = _PORTABLE_STATE_INVALID;
163 _cleanup_free_ char *p = NULL;
164
165 r = bus_image_path(image, &p);
166 if (r < 0)
167 return r;
168
169 r = portable_get_state(
170 sd_bus_message_get_bus(message),
171 image->path,
172 NULL,
173 0,
174 &state,
175 &error_state);
176 if (r < 0)
177 log_debug_errno(r, "Failed to get state of image '%s', ignoring: %s",
178 image->path, bus_error_message(&error_state, r));
179
180 r = sd_bus_message_append(reply, "(ssbtttso)",
181 image->name,
182 image_type_to_string(image->type),
183 image->read_only,
184 image->crtime,
185 image->mtime,
186 image->usage,
187 portable_state_to_string(state),
188 p);
189 if (r < 0)
190 return r;
191 }
192
193 r = sd_bus_message_close_container(reply);
194 if (r < 0)
195 return r;
196
197 return sd_bus_send(NULL, reply, NULL);
198 }
199
200 static int redirect_method_to_image(
201 Manager *m,
202 sd_bus_message *message,
203 sd_bus_error *error,
204 int (*method)(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error* error)) {
205
206 const char *name_or_path;
207 int r;
208
209 assert(m);
210 assert(message);
211 assert(method);
212
213 r = sd_bus_message_read(message, "s", &name_or_path);
214 if (r < 0)
215 return r;
216
217 return method(m, message, name_or_path, NULL, error);
218 }
219
220 static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
221 return redirect_method_to_image(userdata, message, error, bus_image_common_get_os_release);
222 }
223
224 static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
225 return redirect_method_to_image(userdata, message, error, bus_image_common_get_metadata);
226 }
227
228 static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
229 _cleanup_strv_free_ char **extension_images = NULL;
230 const char *name_or_path;
231 PortableState state;
232 int r;
233
234 assert(message);
235
236 r = sd_bus_message_read(message, "s", &name_or_path);
237 if (r < 0)
238 return r;
239
240 if (sd_bus_message_is_method_call(message, NULL, "GetImageStateWithExtensions")) {
241 uint64_t input_flags = 0;
242
243 r = sd_bus_message_read_strv(message, &extension_images);
244 if (r < 0)
245 return r;
246
247 r = sd_bus_message_read(message, "t", &input_flags);
248 if (r < 0)
249 return r;
250
251 /* No flags are supported by this method for now. */
252 if (input_flags != 0)
253 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
254 "Invalid 'flags' parameter '%" PRIu64 "'",
255 input_flags);
256 }
257
258 r = portable_get_state(
259 sd_bus_message_get_bus(message),
260 name_or_path,
261 extension_images,
262 0,
263 &state,
264 error);
265 if (r < 0)
266 return r;
267
268 return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
269 }
270
271 static int method_attach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
272 return redirect_method_to_image(userdata, message, error, bus_image_common_attach);
273 }
274
275 static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
276 _cleanup_strv_free_ char **extension_images = NULL;
277 PortableChange *changes = NULL;
278 PortableFlags flags = 0;
279 Manager *m = userdata;
280 size_t n_changes = 0;
281 const char *name_or_path;
282 int r;
283
284 assert(message);
285 assert(m);
286
287 /* Note that we do not redirect detaching to the image object here, because we want to allow that users can
288 * detach already deleted images too, in case the user already deleted an image before properly detaching
289 * it. */
290
291 r = sd_bus_message_read(message, "s", &name_or_path);
292 if (r < 0)
293 return r;
294
295 if (sd_bus_message_is_method_call(message, NULL, "DetachImageWithExtensions")) {
296 uint64_t input_flags = 0;
297
298 r = sd_bus_message_read_strv(message, &extension_images);
299 if (r < 0)
300 return r;
301
302 r = sd_bus_message_read(message, "t", &input_flags);
303 if (r < 0)
304 return r;
305
306 if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
307 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
308 "Invalid 'flags' parameter '%" PRIu64 "'",
309 input_flags);
310 flags |= input_flags;
311 } else {
312 int runtime;
313
314 r = sd_bus_message_read(message, "b", &runtime);
315 if (r < 0)
316 return r;
317
318 if (runtime)
319 flags |= PORTABLE_RUNTIME;
320 }
321
322 r = bus_verify_polkit_async(
323 message,
324 CAP_SYS_ADMIN,
325 "org.freedesktop.portable1.attach-images",
326 NULL,
327 false,
328 UID_INVALID,
329 &m->polkit_registry,
330 error);
331 if (r < 0)
332 return r;
333 if (r == 0)
334 return 1; /* Will call us back */
335
336 r = portable_detach(
337 sd_bus_message_get_bus(message),
338 name_or_path,
339 extension_images,
340 flags,
341 &changes,
342 &n_changes,
343 error);
344 if (r < 0)
345 goto finish;
346
347 r = reply_portable_changes(message, changes, n_changes);
348
349 finish:
350 portable_changes_free(changes, n_changes);
351 return r;
352 }
353
354 static int method_reattach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
355 return redirect_method_to_image(userdata, message, error, bus_image_common_reattach);
356 }
357
358 static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
359 return redirect_method_to_image(userdata, message, error, bus_image_common_remove);
360 }
361
362 static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
363 return redirect_method_to_image(userdata, message, error, bus_image_common_mark_read_only);
364 }
365
366 static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
367 return redirect_method_to_image(userdata, message, error, bus_image_common_set_limit);
368 }
369
370 static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
371 Manager *m = userdata;
372 uint64_t limit;
373 int r;
374
375 assert(message);
376
377 r = sd_bus_message_read(message, "t", &limit);
378 if (r < 0)
379 return r;
380 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
381 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
382
383 r = bus_verify_polkit_async(
384 message,
385 CAP_SYS_ADMIN,
386 "org.freedesktop.portable1.manage-images",
387 NULL,
388 false,
389 UID_INVALID,
390 &m->polkit_registry,
391 error);
392 if (r < 0)
393 return r;
394 if (r == 0)
395 return 1; /* Will call us back */
396
397 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit);
398
399 r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit);
400 if (r == -ENOTTY)
401 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
402 if (r < 0)
403 return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
404
405 return sd_bus_reply_method_return(message, NULL);
406 }
407
408 const sd_bus_vtable manager_vtable[] = {
409 SD_BUS_VTABLE_START(0),
410 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
411 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
412 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
413 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles, 0, 0),
414 SD_BUS_METHOD_WITH_ARGS("GetImage",
415 SD_BUS_ARGS("s", image),
416 SD_BUS_RESULT("o", object),
417 method_get_image,
418 SD_BUS_VTABLE_UNPRIVILEGED),
419 SD_BUS_METHOD_WITH_ARGS("ListImages",
420 SD_BUS_NO_ARGS,
421 SD_BUS_RESULT("a(ssbtttso)", images),
422 method_list_images,
423 SD_BUS_VTABLE_UNPRIVILEGED),
424 SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
425 SD_BUS_ARGS("s", image),
426 SD_BUS_RESULT("a{ss}", os_release),
427 method_get_image_os_release,
428 SD_BUS_VTABLE_UNPRIVILEGED),
429 SD_BUS_METHOD_WITH_ARGS("GetImageMetadata",
430 SD_BUS_ARGS("s", image,
431 "as", matches),
432 SD_BUS_RESULT("s", image,
433 "ay", os_release,
434 "a{say}", units),
435 method_get_image_metadata,
436 SD_BUS_VTABLE_UNPRIVILEGED),
437 SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
438 SD_BUS_ARGS("s", image,
439 "as", extensions,
440 "as", matches,
441 "t", flags),
442 SD_BUS_RESULT("s", image,
443 "ay", os_release,
444 "a{say}", units),
445 method_get_image_metadata,
446 SD_BUS_VTABLE_UNPRIVILEGED),
447 SD_BUS_METHOD_WITH_ARGS("GetImageState",
448 SD_BUS_ARGS("s", image),
449 SD_BUS_RESULT("s", state),
450 method_get_image_state,
451 SD_BUS_VTABLE_UNPRIVILEGED),
452 SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
453 SD_BUS_ARGS("s", image,
454 "as", extensions,
455 "t", flags),
456 SD_BUS_RESULT("s", state),
457 method_get_image_state,
458 SD_BUS_VTABLE_UNPRIVILEGED),
459 SD_BUS_METHOD_WITH_ARGS("AttachImage",
460 SD_BUS_ARGS("s", image,
461 "as", matches,
462 "s", profile,
463 "b", runtime,
464 "s", copy_mode),
465 SD_BUS_RESULT("a(sss)", changes),
466 method_attach_image,
467 SD_BUS_VTABLE_UNPRIVILEGED),
468 SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
469 SD_BUS_ARGS("s", image,
470 "as", extensions,
471 "as", matches,
472 "s", profile,
473 "s", copy_mode,
474 "t", flags),
475 SD_BUS_RESULT("a(sss)", changes),
476 method_attach_image,
477 SD_BUS_VTABLE_UNPRIVILEGED),
478 SD_BUS_METHOD_WITH_ARGS("DetachImage",
479 SD_BUS_ARGS("s", image,
480 "b", runtime),
481 SD_BUS_RESULT("a(sss)", changes),
482 method_detach_image,
483 SD_BUS_VTABLE_UNPRIVILEGED),
484 SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
485 SD_BUS_ARGS("s", image,
486 "as", extensions,
487 "t", flags),
488 SD_BUS_RESULT("a(sss)", changes),
489 method_detach_image,
490 SD_BUS_VTABLE_UNPRIVILEGED),
491 SD_BUS_METHOD_WITH_ARGS("ReattachImage",
492 SD_BUS_ARGS("s", image,
493 "as", matches,
494 "s", profile,
495 "b", runtime,
496 "s", copy_mode),
497 SD_BUS_RESULT("a(sss)", changes_removed,
498 "a(sss)", changes_updated),
499 method_reattach_image,
500 SD_BUS_VTABLE_UNPRIVILEGED),
501 SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
502 SD_BUS_ARGS("s", image,
503 "as", extensions,
504 "as", matches,
505 "s", profile,
506 "s", copy_mode,
507 "t", flags),
508 SD_BUS_RESULT("a(sss)", changes_removed,
509 "a(sss)", changes_updated),
510 method_reattach_image,
511 SD_BUS_VTABLE_UNPRIVILEGED),
512 SD_BUS_METHOD_WITH_ARGS("RemoveImage",
513 SD_BUS_ARGS("s", image),
514 SD_BUS_NO_RESULT,
515 method_remove_image,
516 SD_BUS_VTABLE_UNPRIVILEGED),
517 SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
518 SD_BUS_ARGS("s", image,
519 "b", read_only),
520 SD_BUS_NO_RESULT,
521 method_mark_image_read_only,
522 SD_BUS_VTABLE_UNPRIVILEGED),
523 SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
524 SD_BUS_ARGS("s", image,
525 "t", limit),
526 SD_BUS_NO_RESULT,
527 method_set_image_limit,
528 SD_BUS_VTABLE_UNPRIVILEGED),
529 SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
530 SD_BUS_ARGS("t", limit),
531 SD_BUS_NO_RESULT,
532 method_set_pool_limit,
533 SD_BUS_VTABLE_UNPRIVILEGED),
534 SD_BUS_VTABLE_END
535 };
536
537 const BusObjectImplementation manager_object = {
538 "/org/freedesktop/portable1",
539 "org.freedesktop.portable1.Manager",
540 .vtables = BUS_VTABLES(manager_vtable),
541 .children = BUS_IMPLEMENTATIONS(&image_object),
542 };
543
544 static int reply_portable_compose_message(sd_bus_message *reply, const PortableChange *changes, size_t n_changes) {
545 size_t i;
546 int r;
547
548 assert(reply);
549 assert(changes || n_changes == 0);
550
551 r = sd_bus_message_open_container(reply, 'a', "(sss)");
552 if (r < 0)
553 return r;
554
555 for (i = 0; i < n_changes; i++) {
556 if (changes[i].type_or_errno < 0)
557 continue;
558
559 r = sd_bus_message_append(reply, "(sss)",
560 portable_change_type_to_string(changes[i].type_or_errno),
561 changes[i].path,
562 changes[i].source);
563 if (r < 0)
564 return r;
565 }
566
567 r = sd_bus_message_close_container(reply);
568 if (r < 0)
569 return r;
570
571 return 0;
572 }
573
574 int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) {
575 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
576 int r;
577
578 assert(m);
579
580 r = sd_bus_message_new_method_return(m, &reply);
581 if (r < 0)
582 return r;
583
584 r = reply_portable_compose_message(reply, changes, n_changes);
585 if (r < 0)
586 return r;
587
588 return sd_bus_send(NULL, reply, NULL);
589 }
590
591 int reply_portable_changes_pair(
592 sd_bus_message *m,
593 const PortableChange *changes_first,
594 size_t n_changes_first,
595 const PortableChange *changes_second,
596 size_t n_changes_second) {
597
598 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
599 int r;
600
601 assert(m);
602
603 r = sd_bus_message_new_method_return(m, &reply);
604 if (r < 0)
605 return r;
606
607 r = reply_portable_compose_message(reply, changes_first, n_changes_first);
608 if (r < 0)
609 return r;
610
611 r = reply_portable_compose_message(reply, changes_second, n_changes_second);
612 if (r < 0)
613 return r;
614
615 return sd_bus_send(NULL, reply, NULL);
616 }