]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
61d0578b LP |
2 | |
3 | #include "alloc-util.h" | |
4 | #include "btrfs-util.h" | |
5 | #include "bus-common-errors.h" | |
3b91bae3 | 6 | #include "bus-object.h" |
269e4d2d | 7 | #include "bus-polkit.h" |
57f1b61b | 8 | #include "discover-image.h" |
61d0578b LP |
9 | #include "fd-util.h" |
10 | #include "io-util.h" | |
204f52e3 | 11 | #include "missing_capability.h" |
61d0578b LP |
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; | |
f5fbe71d | 45 | uint64_t usage = UINT64_MAX; |
61d0578b LP |
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; | |
f5fbe71d | 71 | uint64_t size = UINT64_MAX; |
61d0578b LP |
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; | |
b07ec5a1 | 136 | _cleanup_hashmap_free_ Hashmap *images = NULL; |
61d0578b LP |
137 | Manager *m = userdata; |
138 | Image *image; | |
61d0578b LP |
139 | int r; |
140 | ||
141 | assert(message); | |
142 | assert(m); | |
143 | ||
b07ec5a1 | 144 | images = hashmap_new(&image_hash_ops); |
61d0578b LP |
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 | ||
90e74a66 | 160 | HASHMAP_FOREACH(image, images) { |
61d0578b LP |
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 | PortableChange *changes = NULL; | |
256 | Manager *m = userdata; | |
257 | size_t n_changes = 0; | |
258 | const char *name_or_path; | |
259 | int r, runtime; | |
260 | ||
261 | assert(message); | |
262 | assert(m); | |
263 | ||
264 | /* Note that we do not redirect detaching to the image object here, because we want to allow that users can | |
265 | * detach already deleted images too, in case the user already deleted an image before properly detaching | |
266 | * it. */ | |
267 | ||
268 | r = sd_bus_message_read(message, "sb", &name_or_path, &runtime); | |
269 | if (r < 0) | |
270 | return r; | |
271 | ||
272 | r = bus_verify_polkit_async( | |
273 | message, | |
274 | CAP_SYS_ADMIN, | |
275 | "org.freedesktop.portable1.attach-images", | |
276 | NULL, | |
277 | false, | |
278 | UID_INVALID, | |
279 | &m->polkit_registry, | |
280 | error); | |
281 | if (r < 0) | |
282 | return r; | |
283 | if (r == 0) | |
284 | return 1; /* Will call us back */ | |
285 | ||
286 | r = portable_detach( | |
287 | sd_bus_message_get_bus(message), | |
288 | name_or_path, | |
289 | runtime ? PORTABLE_RUNTIME : 0, | |
290 | &changes, | |
291 | &n_changes, | |
292 | error); | |
293 | if (r < 0) | |
294 | goto finish; | |
295 | ||
296 | r = reply_portable_changes(message, changes, n_changes); | |
297 | ||
298 | finish: | |
299 | portable_changes_free(changes, n_changes); | |
300 | return r; | |
301 | } | |
302 | ||
e26fe5f9 LB |
303 | static int method_reattach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
304 | return redirect_method_to_image(userdata, message, error, bus_image_common_reattach); | |
305 | } | |
306 | ||
61d0578b LP |
307 | static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
308 | return redirect_method_to_image(userdata, message, error, bus_image_common_remove); | |
309 | } | |
310 | ||
311 | static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
312 | return redirect_method_to_image(userdata, message, error, bus_image_common_mark_read_only); | |
313 | } | |
314 | ||
315 | static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
316 | return redirect_method_to_image(userdata, message, error, bus_image_common_set_limit); | |
317 | } | |
318 | ||
319 | static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
320 | Manager *m = userdata; | |
321 | uint64_t limit; | |
322 | int r; | |
323 | ||
324 | assert(message); | |
325 | ||
326 | r = sd_bus_message_read(message, "t", &limit); | |
327 | if (r < 0) | |
328 | return r; | |
329 | if (!FILE_SIZE_VALID_OR_INFINITY(limit)) | |
330 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); | |
331 | ||
332 | r = bus_verify_polkit_async( | |
333 | message, | |
334 | CAP_SYS_ADMIN, | |
335 | "org.freedesktop.portable1.manage-images", | |
336 | NULL, | |
337 | false, | |
338 | UID_INVALID, | |
339 | &m->polkit_registry, | |
340 | error); | |
341 | if (r < 0) | |
342 | return r; | |
343 | if (r == 0) | |
344 | return 1; /* Will call us back */ | |
345 | ||
346 | (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit); | |
347 | ||
348 | r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit); | |
349 | if (r == -ENOTTY) | |
350 | return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs."); | |
351 | if (r < 0) | |
352 | return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m"); | |
353 | ||
354 | return sd_bus_reply_method_return(message, NULL); | |
355 | } | |
356 | ||
357 | const sd_bus_vtable manager_vtable[] = { | |
358 | SD_BUS_VTABLE_START(0), | |
359 | SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0), | |
360 | SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0), | |
361 | SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0), | |
362 | SD_BUS_PROPERTY("Profiles", "as", property_get_profiles, 0, 0), | |
598f938e LB |
363 | SD_BUS_METHOD_WITH_ARGS("GetImage", |
364 | SD_BUS_ARGS("s", image), | |
365 | SD_BUS_RESULT("o", object), | |
366 | method_get_image, | |
367 | SD_BUS_VTABLE_UNPRIVILEGED), | |
368 | SD_BUS_METHOD_WITH_ARGS("ListImages", | |
369 | SD_BUS_NO_ARGS, | |
370 | SD_BUS_RESULT("a(ssbtttso)", images), | |
371 | method_list_images, | |
372 | SD_BUS_VTABLE_UNPRIVILEGED), | |
373 | SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease", | |
374 | SD_BUS_ARGS("s", image), | |
375 | SD_BUS_RESULT("a{ss}", os_release), | |
376 | method_get_image_os_release, | |
377 | SD_BUS_VTABLE_UNPRIVILEGED), | |
378 | SD_BUS_METHOD_WITH_ARGS("GetImageMetadata", | |
379 | SD_BUS_ARGS("s", image, | |
380 | "as", matches), | |
381 | SD_BUS_RESULT("s", image, | |
382 | "ay", os_release, | |
383 | "a{say}", units), | |
384 | method_get_image_metadata, | |
385 | SD_BUS_VTABLE_UNPRIVILEGED), | |
386 | SD_BUS_METHOD_WITH_ARGS("GetImageState", | |
387 | SD_BUS_ARGS("s", image), | |
388 | SD_BUS_RESULT("s", state), | |
389 | method_get_image_state, | |
390 | SD_BUS_VTABLE_UNPRIVILEGED), | |
391 | SD_BUS_METHOD_WITH_ARGS("AttachImage", | |
392 | SD_BUS_ARGS("s", image, | |
393 | "as", matches, | |
394 | "s", profile, | |
395 | "b", runtime, | |
396 | "s", copy_mode), | |
397 | SD_BUS_RESULT("a(sss)", changes), | |
398 | method_attach_image, | |
399 | SD_BUS_VTABLE_UNPRIVILEGED), | |
400 | SD_BUS_METHOD_WITH_ARGS("DetachImage", | |
401 | SD_BUS_ARGS("s", image, | |
402 | "b", runtime), | |
403 | SD_BUS_RESULT("a(sss)", changes), | |
404 | method_detach_image, | |
405 | SD_BUS_VTABLE_UNPRIVILEGED), | |
406 | SD_BUS_METHOD_WITH_ARGS("ReattachImage", | |
407 | SD_BUS_ARGS("s", image, | |
408 | "as", matches, | |
409 | "s", profile, | |
410 | "b", runtime, | |
411 | "s", copy_mode), | |
412 | SD_BUS_RESULT("a(sss)", changes_removed, | |
413 | "a(sss)", changes_updated), | |
414 | method_reattach_image, | |
415 | SD_BUS_VTABLE_UNPRIVILEGED), | |
416 | SD_BUS_METHOD_WITH_ARGS("RemoveImage", | |
417 | SD_BUS_ARGS("s", image), | |
418 | SD_BUS_NO_RESULT, | |
419 | method_remove_image, | |
420 | SD_BUS_VTABLE_UNPRIVILEGED), | |
421 | SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly", | |
422 | SD_BUS_ARGS("s", image, | |
423 | "b", read_only), | |
424 | SD_BUS_NO_RESULT, | |
425 | method_mark_image_read_only, | |
426 | SD_BUS_VTABLE_UNPRIVILEGED), | |
427 | SD_BUS_METHOD_WITH_ARGS("SetImageLimit", | |
428 | SD_BUS_ARGS("s", image, | |
429 | "t", limit), | |
430 | SD_BUS_NO_RESULT, | |
431 | method_set_image_limit, | |
432 | SD_BUS_VTABLE_UNPRIVILEGED), | |
433 | SD_BUS_METHOD_WITH_ARGS("SetPoolLimit", | |
434 | SD_BUS_ARGS("t", limit), | |
435 | SD_BUS_NO_RESULT, | |
436 | method_set_pool_limit, | |
437 | SD_BUS_VTABLE_UNPRIVILEGED), | |
61d0578b LP |
438 | SD_BUS_VTABLE_END |
439 | }; | |
440 | ||
3b91bae3 LB |
441 | const BusObjectImplementation manager_object = { |
442 | "/org/freedesktop/portable1", | |
443 | "org.freedesktop.portable1.Manager", | |
444 | .vtables = BUS_VTABLES(manager_vtable), | |
445 | .children = BUS_IMPLEMENTATIONS(&image_object), | |
446 | }; | |
447 | ||
e26fe5f9 | 448 | static int reply_portable_compose_message(sd_bus_message *reply, const PortableChange *changes, size_t n_changes) { |
61d0578b LP |
449 | size_t i; |
450 | int r; | |
451 | ||
e26fe5f9 | 452 | assert(reply); |
61d0578b LP |
453 | assert(changes || n_changes == 0); |
454 | ||
61d0578b LP |
455 | r = sd_bus_message_open_container(reply, 'a', "(sss)"); |
456 | if (r < 0) | |
457 | return r; | |
458 | ||
459 | for (i = 0; i < n_changes; i++) { | |
ba5b6c59 LP |
460 | if (changes[i].type_or_errno < 0) |
461 | continue; | |
462 | ||
61d0578b | 463 | r = sd_bus_message_append(reply, "(sss)", |
ba5b6c59 | 464 | portable_change_type_to_string(changes[i].type_or_errno), |
61d0578b LP |
465 | changes[i].path, |
466 | changes[i].source); | |
467 | if (r < 0) | |
468 | return r; | |
469 | } | |
470 | ||
471 | r = sd_bus_message_close_container(reply); | |
472 | if (r < 0) | |
473 | return r; | |
474 | ||
e26fe5f9 LB |
475 | return 0; |
476 | } | |
477 | ||
478 | int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) { | |
479 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
480 | int r; | |
481 | ||
482 | assert(m); | |
483 | ||
484 | r = sd_bus_message_new_method_return(m, &reply); | |
485 | if (r < 0) | |
486 | return r; | |
487 | ||
488 | r = reply_portable_compose_message(reply, changes, n_changes); | |
489 | if (r < 0) | |
490 | return r; | |
491 | ||
492 | return sd_bus_send(NULL, reply, NULL); | |
493 | } | |
494 | ||
495 | int reply_portable_changes_pair( | |
496 | sd_bus_message *m, | |
497 | const PortableChange *changes_first, | |
498 | size_t n_changes_first, | |
499 | const PortableChange *changes_second, | |
500 | size_t n_changes_second) { | |
501 | ||
502 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
503 | int r; | |
504 | ||
505 | assert(m); | |
506 | ||
507 | r = sd_bus_message_new_method_return(m, &reply); | |
508 | if (r < 0) | |
509 | return r; | |
510 | ||
511 | r = reply_portable_compose_message(reply, changes_first, n_changes_first); | |
512 | if (r < 0) | |
513 | return r; | |
514 | ||
515 | r = reply_portable_compose_message(reply, changes_second, n_changes_second); | |
516 | if (r < 0) | |
517 | return r; | |
518 | ||
61d0578b LP |
519 | return sd_bus_send(NULL, reply, NULL); |
520 | } |