]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image-dbus.c
shared: split out polkit stuff from bus-util.c → bus-polkit.c
[thirdparty/systemd.git] / src / machine / image-dbus.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ebeccf9e 2
fe993888 3#include <sys/file.h>
9153b02b
LP
4#include <sys/mount.h>
5
b5efdb8a 6#include "alloc-util.h"
ebeccf9e 7#include "bus-label.h"
269e4d2d 8#include "bus-polkit.h"
1ddb263d 9#include "bus-util.h"
9153b02b
LP
10#include "copy.h"
11#include "dissect-image.h"
56599585 12#include "fd-util.h"
9153b02b
LP
13#include "fileio.h"
14#include "fs-util.h"
003dffde 15#include "image-dbus.h"
a90fb858 16#include "io-util.h"
9153b02b 17#include "loop-util.h"
ee104e11 18#include "machine-image.h"
204f52e3 19#include "missing_capability.h"
9153b02b 20#include "mount-util.h"
56599585 21#include "process-util.h"
9153b02b 22#include "raw-clone.h"
ee104e11
LP
23#include "strv.h"
24#include "user-util.h"
ebeccf9e 25
1ddb263d 26static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
ebeccf9e 27
1ddb263d 28int bus_image_method_remove(
08682124
LP
29 sd_bus_message *message,
30 void *userdata,
31 sd_bus_error *error) {
32
5d2036b5 33 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1ddb263d 34 Image *image = userdata;
70244d1d 35 Manager *m = image->userdata;
5d2036b5 36 pid_t child;
08682124
LP
37 int r;
38
08682124 39 assert(message);
1ddb263d 40 assert(image);
08682124 41
5d2036b5
LP
42 if (m->n_operations >= OPERATIONS_MAX)
43 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
44
70244d1d
LP
45 r = bus_verify_polkit_async(
46 message,
47 CAP_SYS_ADMIN,
48 "org.freedesktop.machine1.manage-images",
403ed0e5 49 NULL,
70244d1d 50 false,
c529695e 51 UID_INVALID,
70244d1d
LP
52 &m->polkit_registry,
53 error);
54 if (r < 0)
55 return r;
56 if (r == 0)
57 return 1; /* Will call us back */
58
5d2036b5
LP
59 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
60 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
61
4c253ed1
LP
62 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
63 if (r < 0)
64 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
65 if (r == 0) {
5d2036b5
LP
66 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
67
68 r = image_remove(image);
69 if (r < 0) {
70 (void) write(errno_pipe_fd[1], &r, sizeof(r));
71 _exit(EXIT_FAILURE);
72 }
73
74 _exit(EXIT_SUCCESS);
75 }
76
77 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
78
03c2b288 79 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
5d2036b5
LP
80 if (r < 0) {
81 (void) sigkill_wait(child);
08682124 82 return r;
5d2036b5
LP
83 }
84
85 errno_pipe_fd[0] = -1;
08682124 86
5d2036b5 87 return 1;
08682124
LP
88}
89
1ddb263d 90int bus_image_method_rename(
ebd93cb6
LP
91 sd_bus_message *message,
92 void *userdata,
93 sd_bus_error *error) {
94
1ddb263d 95 Image *image = userdata;
70244d1d 96 Manager *m = image->userdata;
ebd93cb6
LP
97 const char *new_name;
98 int r;
99
ebd93cb6 100 assert(message);
1ddb263d 101 assert(image);
ebd93cb6
LP
102
103 r = sd_bus_message_read(message, "s", &new_name);
104 if (r < 0)
105 return r;
106
107 if (!image_name_is_valid(new_name))
108 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
109
70244d1d
LP
110 r = bus_verify_polkit_async(
111 message,
112 CAP_SYS_ADMIN,
113 "org.freedesktop.machine1.manage-images",
403ed0e5 114 NULL,
70244d1d 115 false,
c529695e 116 UID_INVALID,
70244d1d
LP
117 &m->polkit_registry,
118 error);
119 if (r < 0)
120 return r;
121 if (r == 0)
122 return 1; /* Will call us back */
123
ebd93cb6
LP
124 r = image_rename(image, new_name);
125 if (r < 0)
126 return r;
127
128 return sd_bus_reply_method_return(message, NULL);
129}
130
1ddb263d 131int bus_image_method_clone(
ebd93cb6
LP
132 sd_bus_message *message,
133 void *userdata,
134 sd_bus_error *error) {
135
56599585 136 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1ddb263d 137 Image *image = userdata;
70244d1d 138 Manager *m = image->userdata;
ebd93cb6
LP
139 const char *new_name;
140 int r, read_only;
56599585 141 pid_t child;
ebd93cb6 142
ebd93cb6 143 assert(message);
1ddb263d 144 assert(image);
56599585
LP
145 assert(m);
146
147 if (m->n_operations >= OPERATIONS_MAX)
148 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
ebd93cb6
LP
149
150 r = sd_bus_message_read(message, "sb", &new_name, &read_only);
151 if (r < 0)
152 return r;
153
154 if (!image_name_is_valid(new_name))
155 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
156
70244d1d
LP
157 r = bus_verify_polkit_async(
158 message,
159 CAP_SYS_ADMIN,
160 "org.freedesktop.machine1.manage-images",
403ed0e5 161 NULL,
70244d1d 162 false,
c529695e 163 UID_INVALID,
70244d1d
LP
164 &m->polkit_registry,
165 error);
166 if (r < 0)
167 return r;
168 if (r == 0)
169 return 1; /* Will call us back */
170
56599585
LP
171 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
172 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
173
f2747bf5 174 r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
4c253ed1
LP
175 if (r < 0)
176 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
177 if (r == 0) {
56599585
LP
178 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
179
180 r = image_clone(image, new_name, read_only);
181 if (r < 0) {
182 (void) write(errno_pipe_fd[1], &r, sizeof(r));
183 _exit(EXIT_FAILURE);
184 }
185
186 _exit(EXIT_SUCCESS);
187 }
188
189 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
190
03c2b288 191 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
56599585 192 if (r < 0) {
89c9030d 193 (void) sigkill_wait(child);
ebd93cb6 194 return r;
56599585 195 }
ebd93cb6 196
56599585
LP
197 errno_pipe_fd[0] = -1;
198
199 return 1;
ebd93cb6
LP
200}
201
1ddb263d 202int bus_image_method_mark_read_only(
ebd93cb6
LP
203 sd_bus_message *message,
204 void *userdata,
205 sd_bus_error *error) {
206
1ddb263d 207 Image *image = userdata;
70244d1d 208 Manager *m = image->userdata;
ebd93cb6
LP
209 int r, read_only;
210
ebd93cb6
LP
211 assert(message);
212
ebd93cb6
LP
213 r = sd_bus_message_read(message, "b", &read_only);
214 if (r < 0)
215 return r;
216
70244d1d
LP
217 r = bus_verify_polkit_async(
218 message,
219 CAP_SYS_ADMIN,
220 "org.freedesktop.machine1.manage-images",
403ed0e5 221 NULL,
70244d1d 222 false,
c529695e 223 UID_INVALID,
70244d1d
LP
224 &m->polkit_registry,
225 error);
226 if (r < 0)
227 return r;
228 if (r == 0)
229 return 1; /* Will call us back */
230
ebd93cb6
LP
231 r = image_read_only(image, read_only);
232 if (r < 0)
233 return r;
234
235 return sd_bus_reply_method_return(message, NULL);
236}
237
d6ce17c7 238int bus_image_method_set_limit(
d6ce17c7
LP
239 sd_bus_message *message,
240 void *userdata,
241 sd_bus_error *error) {
242
243 Image *image = userdata;
244 Manager *m = image->userdata;
245 uint64_t limit;
246 int r;
247
d6ce17c7
LP
248 assert(message);
249
250 r = sd_bus_message_read(message, "t", &limit);
251 if (r < 0)
252 return r;
a90fb858
LP
253 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
254 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
d6ce17c7
LP
255
256 r = bus_verify_polkit_async(
257 message,
258 CAP_SYS_ADMIN,
259 "org.freedesktop.machine1.manage-images",
403ed0e5 260 NULL,
d6ce17c7
LP
261 false,
262 UID_INVALID,
263 &m->polkit_registry,
264 error);
265 if (r < 0)
266 return r;
267 if (r == 0)
268 return 1; /* Will call us back */
269
270 r = image_set_limit(image, limit);
271 if (r < 0)
272 return r;
273
274 return sd_bus_reply_method_return(message, NULL);
275}
276
cf30a8c1
LP
277int bus_image_method_get_hostname(
278 sd_bus_message *message,
279 void *userdata,
280 sd_bus_error *error) {
9153b02b 281
cf30a8c1 282 Image *image = userdata;
9153b02b
LP
283 int r;
284
cf30a8c1
LP
285 if (!image->metadata_valid) {
286 r = image_read_metadata(image);
287 if (r < 0)
288 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
289 }
9153b02b 290
cf30a8c1 291 return sd_bus_reply_method_return(message, "s", image->hostname);
9153b02b
LP
292}
293
cf30a8c1
LP
294int bus_image_method_get_machine_id(
295 sd_bus_message *message,
296 void *userdata,
297 sd_bus_error *error) {
9153b02b 298
cf30a8c1
LP
299 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
300 Image *image = userdata;
301 int r;
9153b02b 302
cf30a8c1
LP
303 if (!image->metadata_valid) {
304 r = image_read_metadata(image);
9153b02b 305 if (r < 0)
cf30a8c1 306 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
9153b02b
LP
307 }
308
cf30a8c1 309 r = sd_bus_message_new_method_return(message, &reply);
9153b02b
LP
310 if (r < 0)
311 return r;
312
cf30a8c1
LP
313 if (sd_id128_is_null(image->machine_id)) /* Add an empty array if the ID is zero */
314 r = sd_bus_message_append(reply, "ay", 0);
315 else
316 r = sd_bus_message_append_array(reply, 'y', image->machine_id.bytes, 16);
9153b02b 317 if (r < 0)
cf30a8c1 318 return r;
9153b02b 319
cf30a8c1 320 return sd_bus_send(NULL, reply, NULL);
9153b02b
LP
321}
322
cf30a8c1 323int bus_image_method_get_machine_info(
9153b02b
LP
324 sd_bus_message *message,
325 void *userdata,
326 sd_bus_error *error) {
327
9153b02b
LP
328 Image *image = userdata;
329 int r;
330
cf30a8c1
LP
331 if (!image->metadata_valid) {
332 r = image_read_metadata(image);
333 if (r < 0)
334 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
335 }
9153b02b 336
cf30a8c1
LP
337 return bus_reply_pair_array(message, image->machine_info);
338}
9153b02b 339
cf30a8c1
LP
340int bus_image_method_get_os_release(
341 sd_bus_message *message,
342 void *userdata,
343 sd_bus_error *error) {
9153b02b 344
cf30a8c1
LP
345 Image *image = userdata;
346 int r;
9153b02b 347
cf30a8c1
LP
348 if (!image->metadata_valid) {
349 r = image_read_metadata(image);
350 if (r < 0)
351 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
9153b02b 352 }
9153b02b 353
cf30a8c1 354 return bus_reply_pair_array(message, image->os_release);
9153b02b
LP
355}
356
ebeccf9e
LP
357const sd_bus_vtable image_vtable[] = {
358 SD_BUS_VTABLE_START(0),
1ddb263d
LP
359 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
360 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
361 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
362 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
363 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
364 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
c19de711 365 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
1ddb263d 366 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
c19de711 367 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
1ddb263d 368 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
70244d1d
LP
369 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
370 SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
371 SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
372 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
d6ce17c7 373 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
cf30a8c1
LP
374 SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
375 SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
376 SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
9153b02b 377 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
ebeccf9e
LP
378 SD_BUS_VTABLE_END
379};
380
1ddb263d
LP
381static int image_flush_cache(sd_event_source *s, void *userdata) {
382 Manager *m = userdata;
1ddb263d
LP
383
384 assert(s);
385 assert(m);
386
b07ec5a1 387 hashmap_clear(m->image_cache);
1ddb263d
LP
388 return 0;
389}
390
ebeccf9e 391int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1ddb263d
LP
392 _cleanup_free_ char *e = NULL;
393 Manager *m = userdata;
394 Image *image = NULL;
395 const char *p;
ebeccf9e
LP
396 int r;
397
398 assert(bus);
399 assert(path);
400 assert(interface);
401 assert(found);
402
1ddb263d
LP
403 p = startswith(path, "/org/freedesktop/machine1/image/");
404 if (!p)
405 return 0;
406
407 e = bus_label_unescape(p);
408 if (!e)
409 return -ENOMEM;
410
411 image = hashmap_get(m->image_cache, e);
412 if (image) {
413 *found = image;
414 return 1;
415 }
416
b07ec5a1 417 r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
1ddb263d
LP
418 if (r < 0)
419 return r;
420
421 if (!m->image_cache_defer_event) {
422 r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
423 if (r < 0)
424 return r;
425
426 r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
427 if (r < 0)
428 return r;
429 }
430
431 r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
432 if (r < 0)
433 return r;
434
5ef46e5f 435 r = image_find(IMAGE_MACHINE, e, &image);
3a6ce860
LP
436 if (r == -ENOENT)
437 return 0;
438 if (r < 0)
ebeccf9e
LP
439 return r;
440
70244d1d
LP
441 image->userdata = m;
442
1ddb263d
LP
443 r = hashmap_put(m->image_cache, image->name, image);
444 if (r < 0) {
445 image_unref(image);
446 return r;
447 }
448
449 *found = image;
ebeccf9e
LP
450 return 1;
451}
452
453char *image_bus_path(const char *name) {
454 _cleanup_free_ char *e = NULL;
455
456 assert(name);
457
458 e = bus_label_escape(name);
459 if (!e)
460 return NULL;
461
b910cc72 462 return strjoin("/org/freedesktop/machine1/image/", e);
ebeccf9e
LP
463}
464
465int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
b07ec5a1 466 _cleanup_hashmap_free_ Hashmap *images = NULL;
ebeccf9e
LP
467 _cleanup_strv_free_ char **l = NULL;
468 Image *image;
469 Iterator i;
470 int r;
471
472 assert(bus);
473 assert(path);
474 assert(nodes);
475
b07ec5a1 476 images = hashmap_new(&image_hash_ops);
ebeccf9e
LP
477 if (!images)
478 return -ENOMEM;
479
5ef46e5f 480 r = image_discover(IMAGE_MACHINE, images);
ebeccf9e
LP
481 if (r < 0)
482 return r;
483
484 HASHMAP_FOREACH(image, images, i) {
485 char *p;
486
487 p = image_bus_path(image->name);
488 if (!p)
489 return -ENOMEM;
490
491 r = strv_consume(&l, p);
492 if (r < 0)
493 return r;
494 }
495
1cc6c93a 496 *nodes = TAKE_PTR(l);
ebeccf9e
LP
497
498 return 1;
499}