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