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