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