]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/portable/portabled-bus.c
Merge pull request #27220 from yuwata/sd-device-follow-ups-for-devlink
[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 CAP_SYS_ADMIN,
324 "org.freedesktop.portable1.attach-images",
325 NULL,
326 false,
327 UID_INVALID,
328 &m->polkit_registry,
329 error);
330 if (r < 0)
331 return r;
332 if (r == 0)
333 return 1; /* Will call us back */
334
335 r = portable_detach(
336 sd_bus_message_get_bus(message),
337 name_or_path,
338 extension_images,
339 flags,
340 &changes,
341 &n_changes,
342 error);
343 if (r < 0)
344 return r;
345
346 return reply_portable_changes(message, changes, n_changes);
347 }
348
349 static int method_reattach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
350 return redirect_method_to_image(userdata, message, error, bus_image_common_reattach);
351 }
352
353 static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
354 return redirect_method_to_image(userdata, message, error, bus_image_common_remove);
355 }
356
357 static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
358 return redirect_method_to_image(userdata, message, error, bus_image_common_mark_read_only);
359 }
360
361 static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
362 return redirect_method_to_image(userdata, message, error, bus_image_common_set_limit);
363 }
364
365 static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
366 Manager *m = userdata;
367 uint64_t limit;
368 int r;
369
370 assert(message);
371
372 r = sd_bus_message_read(message, "t", &limit);
373 if (r < 0)
374 return r;
375 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
376 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
377
378 r = bus_verify_polkit_async(
379 message,
380 CAP_SYS_ADMIN,
381 "org.freedesktop.portable1.manage-images",
382 NULL,
383 false,
384 UID_INVALID,
385 &m->polkit_registry,
386 error);
387 if (r < 0)
388 return r;
389 if (r == 0)
390 return 1; /* Will call us back */
391
392 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit);
393
394 r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit);
395 if (r == -ENOTTY)
396 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
397 if (r < 0)
398 return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
399
400 return sd_bus_reply_method_return(message, NULL);
401 }
402
403 const sd_bus_vtable manager_vtable[] = {
404 SD_BUS_VTABLE_START(0),
405 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
406 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
407 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
408 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles, 0, 0),
409 SD_BUS_METHOD_WITH_ARGS("GetImage",
410 SD_BUS_ARGS("s", image),
411 SD_BUS_RESULT("o", object),
412 method_get_image,
413 SD_BUS_VTABLE_UNPRIVILEGED),
414 SD_BUS_METHOD_WITH_ARGS("ListImages",
415 SD_BUS_NO_ARGS,
416 SD_BUS_RESULT("a(ssbtttso)", images),
417 method_list_images,
418 SD_BUS_VTABLE_UNPRIVILEGED),
419 SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
420 SD_BUS_ARGS("s", image),
421 SD_BUS_RESULT("a{ss}", os_release),
422 method_get_image_os_release,
423 SD_BUS_VTABLE_UNPRIVILEGED),
424 SD_BUS_METHOD_WITH_ARGS("GetImageMetadata",
425 SD_BUS_ARGS("s", image,
426 "as", matches),
427 SD_BUS_RESULT("s", image,
428 "ay", os_release,
429 "a{say}", units),
430 method_get_image_metadata,
431 SD_BUS_VTABLE_UNPRIVILEGED),
432 SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
433 SD_BUS_ARGS("s", image,
434 "as", extensions,
435 "as", matches,
436 "t", flags),
437 SD_BUS_RESULT("s", image,
438 "ay", os_release,
439 "a{say}", extensions,
440 "a{say}", units),
441 method_get_image_metadata,
442 SD_BUS_VTABLE_UNPRIVILEGED),
443 SD_BUS_METHOD_WITH_ARGS("GetImageState",
444 SD_BUS_ARGS("s", image),
445 SD_BUS_RESULT("s", state),
446 method_get_image_state,
447 SD_BUS_VTABLE_UNPRIVILEGED),
448 SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
449 SD_BUS_ARGS("s", image,
450 "as", extensions,
451 "t", flags),
452 SD_BUS_RESULT("s", state),
453 method_get_image_state,
454 SD_BUS_VTABLE_UNPRIVILEGED),
455 SD_BUS_METHOD_WITH_ARGS("AttachImage",
456 SD_BUS_ARGS("s", image,
457 "as", matches,
458 "s", profile,
459 "b", runtime,
460 "s", copy_mode),
461 SD_BUS_RESULT("a(sss)", changes),
462 method_attach_image,
463 SD_BUS_VTABLE_UNPRIVILEGED),
464 SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
465 SD_BUS_ARGS("s", image,
466 "as", extensions,
467 "as", matches,
468 "s", profile,
469 "s", copy_mode,
470 "t", flags),
471 SD_BUS_RESULT("a(sss)", changes),
472 method_attach_image,
473 SD_BUS_VTABLE_UNPRIVILEGED),
474 SD_BUS_METHOD_WITH_ARGS("DetachImage",
475 SD_BUS_ARGS("s", image,
476 "b", runtime),
477 SD_BUS_RESULT("a(sss)", changes),
478 method_detach_image,
479 SD_BUS_VTABLE_UNPRIVILEGED),
480 SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
481 SD_BUS_ARGS("s", image,
482 "as", extensions,
483 "t", flags),
484 SD_BUS_RESULT("a(sss)", changes),
485 method_detach_image,
486 SD_BUS_VTABLE_UNPRIVILEGED),
487 SD_BUS_METHOD_WITH_ARGS("ReattachImage",
488 SD_BUS_ARGS("s", image,
489 "as", matches,
490 "s", profile,
491 "b", runtime,
492 "s", copy_mode),
493 SD_BUS_RESULT("a(sss)", changes_removed,
494 "a(sss)", changes_updated),
495 method_reattach_image,
496 SD_BUS_VTABLE_UNPRIVILEGED),
497 SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
498 SD_BUS_ARGS("s", image,
499 "as", extensions,
500 "as", matches,
501 "s", profile,
502 "s", copy_mode,
503 "t", flags),
504 SD_BUS_RESULT("a(sss)", changes_removed,
505 "a(sss)", changes_updated),
506 method_reattach_image,
507 SD_BUS_VTABLE_UNPRIVILEGED),
508 SD_BUS_METHOD_WITH_ARGS("RemoveImage",
509 SD_BUS_ARGS("s", image),
510 SD_BUS_NO_RESULT,
511 method_remove_image,
512 SD_BUS_VTABLE_UNPRIVILEGED),
513 SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
514 SD_BUS_ARGS("s", image,
515 "b", read_only),
516 SD_BUS_NO_RESULT,
517 method_mark_image_read_only,
518 SD_BUS_VTABLE_UNPRIVILEGED),
519 SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
520 SD_BUS_ARGS("s", image,
521 "t", limit),
522 SD_BUS_NO_RESULT,
523 method_set_image_limit,
524 SD_BUS_VTABLE_UNPRIVILEGED),
525 SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
526 SD_BUS_ARGS("t", limit),
527 SD_BUS_NO_RESULT,
528 method_set_pool_limit,
529 SD_BUS_VTABLE_UNPRIVILEGED),
530 SD_BUS_VTABLE_END
531 };
532
533 const BusObjectImplementation manager_object = {
534 "/org/freedesktop/portable1",
535 "org.freedesktop.portable1.Manager",
536 .vtables = BUS_VTABLES(manager_vtable),
537 .children = BUS_IMPLEMENTATIONS(&image_object),
538 };
539
540 static int reply_portable_compose_message(sd_bus_message *reply, const PortableChange *changes, size_t n_changes) {
541 size_t i;
542 int r;
543
544 assert(reply);
545 assert(changes || n_changes == 0);
546
547 r = sd_bus_message_open_container(reply, 'a', "(sss)");
548 if (r < 0)
549 return r;
550
551 for (i = 0; i < n_changes; i++) {
552 if (changes[i].type_or_errno < 0)
553 continue;
554
555 r = sd_bus_message_append(reply, "(sss)",
556 portable_change_type_to_string(changes[i].type_or_errno),
557 changes[i].path,
558 changes[i].source);
559 if (r < 0)
560 return r;
561 }
562
563 r = sd_bus_message_close_container(reply);
564 if (r < 0)
565 return r;
566
567 return 0;
568 }
569
570 int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) {
571 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
572 int r;
573
574 assert(m);
575
576 r = sd_bus_message_new_method_return(m, &reply);
577 if (r < 0)
578 return r;
579
580 r = reply_portable_compose_message(reply, changes, n_changes);
581 if (r < 0)
582 return r;
583
584 return sd_bus_send(NULL, reply, NULL);
585 }
586
587 int reply_portable_changes_pair(
588 sd_bus_message *m,
589 const PortableChange *changes_first,
590 size_t n_changes_first,
591 const PortableChange *changes_second,
592 size_t n_changes_second) {
593
594 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
595 int r;
596
597 assert(m);
598
599 r = sd_bus_message_new_method_return(m, &reply);
600 if (r < 0)
601 return r;
602
603 r = reply_portable_compose_message(reply, changes_first, n_changes_first);
604 if (r < 0)
605 return r;
606
607 r = reply_portable_compose_message(reply, changes_second, n_changes_second);
608 if (r < 0)
609 return r;
610
611 return sd_bus_send(NULL, reply, NULL);
612 }