]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/image-dbus.c
core: reduce scope of variants
[thirdparty/systemd.git] / src / machine / image-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <sys/file.h>
4 #include <sys/mount.h>
5
6 #include "alloc-util.h"
7 #include "bus-get-properties.h"
8 #include "bus-label.h"
9 #include "bus-polkit.h"
10 #include "copy.h"
11 #include "dissect-image.h"
12 #include "fd-util.h"
13 #include "fileio.h"
14 #include "fs-util.h"
15 #include "image-dbus.h"
16 #include "io-util.h"
17 #include "loop-util.h"
18 #include "machine-image.h"
19 #include "missing_capability.h"
20 #include "mount-util.h"
21 #include "process-util.h"
22 #include "raw-clone.h"
23 #include "strv.h"
24 #include "user-util.h"
25
26 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
27
28 int bus_image_method_remove(
29 sd_bus_message *message,
30 void *userdata,
31 sd_bus_error *error) {
32
33 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
34 Image *image = userdata;
35 Manager *m = image->userdata;
36 pid_t child;
37 int r;
38
39 assert(message);
40 assert(image);
41
42 if (m->n_operations >= OPERATIONS_MAX)
43 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
44
45 r = bus_verify_polkit_async(
46 message,
47 CAP_SYS_ADMIN,
48 "org.freedesktop.machine1.manage-images",
49 NULL,
50 false,
51 UID_INVALID,
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
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
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) {
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
79 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
80 if (r < 0) {
81 (void) sigkill_wait(child);
82 return r;
83 }
84
85 errno_pipe_fd[0] = -1;
86
87 return 1;
88 }
89
90 int bus_image_method_rename(
91 sd_bus_message *message,
92 void *userdata,
93 sd_bus_error *error) {
94
95 Image *image = userdata;
96 Manager *m = image->userdata;
97 const char *new_name;
98 int r;
99
100 assert(message);
101 assert(image);
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
110 r = bus_verify_polkit_async(
111 message,
112 CAP_SYS_ADMIN,
113 "org.freedesktop.machine1.manage-images",
114 NULL,
115 false,
116 UID_INVALID,
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
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
131 int bus_image_method_clone(
132 sd_bus_message *message,
133 void *userdata,
134 sd_bus_error *error) {
135
136 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
137 Image *image = userdata;
138 Manager *m = image->userdata;
139 const char *new_name;
140 int r, read_only;
141 pid_t child;
142
143 assert(message);
144 assert(image);
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.");
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
157 r = bus_verify_polkit_async(
158 message,
159 CAP_SYS_ADMIN,
160 "org.freedesktop.machine1.manage-images",
161 NULL,
162 false,
163 UID_INVALID,
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
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
174 r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
175 if (r < 0)
176 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
177 if (r == 0) {
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
191 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
192 if (r < 0) {
193 (void) sigkill_wait(child);
194 return r;
195 }
196
197 errno_pipe_fd[0] = -1;
198
199 return 1;
200 }
201
202 int bus_image_method_mark_read_only(
203 sd_bus_message *message,
204 void *userdata,
205 sd_bus_error *error) {
206
207 Image *image = userdata;
208 Manager *m = image->userdata;
209 int r, read_only;
210
211 assert(message);
212
213 r = sd_bus_message_read(message, "b", &read_only);
214 if (r < 0)
215 return r;
216
217 r = bus_verify_polkit_async(
218 message,
219 CAP_SYS_ADMIN,
220 "org.freedesktop.machine1.manage-images",
221 NULL,
222 false,
223 UID_INVALID,
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
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
238 int bus_image_method_set_limit(
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
248 assert(message);
249
250 r = sd_bus_message_read(message, "t", &limit);
251 if (r < 0)
252 return r;
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");
255
256 r = bus_verify_polkit_async(
257 message,
258 CAP_SYS_ADMIN,
259 "org.freedesktop.machine1.manage-images",
260 NULL,
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
277 int bus_image_method_get_hostname(
278 sd_bus_message *message,
279 void *userdata,
280 sd_bus_error *error) {
281
282 Image *image = userdata;
283 int r;
284
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 }
290
291 return sd_bus_reply_method_return(message, "s", image->hostname);
292 }
293
294 int bus_image_method_get_machine_id(
295 sd_bus_message *message,
296 void *userdata,
297 sd_bus_error *error) {
298
299 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
300 Image *image = userdata;
301 int r;
302
303 if (!image->metadata_valid) {
304 r = image_read_metadata(image);
305 if (r < 0)
306 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
307 }
308
309 r = sd_bus_message_new_method_return(message, &reply);
310 if (r < 0)
311 return r;
312
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);
317 if (r < 0)
318 return r;
319
320 return sd_bus_send(NULL, reply, NULL);
321 }
322
323 int bus_image_method_get_machine_info(
324 sd_bus_message *message,
325 void *userdata,
326 sd_bus_error *error) {
327
328 Image *image = userdata;
329 int r;
330
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 }
336
337 return bus_reply_pair_array(message, image->machine_info);
338 }
339
340 int bus_image_method_get_os_release(
341 sd_bus_message *message,
342 void *userdata,
343 sd_bus_error *error) {
344
345 Image *image = userdata;
346 int r;
347
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");
352 }
353
354 return bus_reply_pair_array(message, image->os_release);
355 }
356
357 static int image_flush_cache(sd_event_source *s, void *userdata) {
358 Manager *m = userdata;
359
360 assert(s);
361 assert(m);
362
363 hashmap_clear(m->image_cache);
364 return 0;
365 }
366
367 static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
368 _cleanup_free_ char *e = NULL;
369 Manager *m = userdata;
370 Image *image = NULL;
371 const char *p;
372 int r;
373
374 assert(bus);
375 assert(path);
376 assert(interface);
377 assert(found);
378
379 p = startswith(path, "/org/freedesktop/machine1/image/");
380 if (!p)
381 return 0;
382
383 e = bus_label_unescape(p);
384 if (!e)
385 return -ENOMEM;
386
387 image = hashmap_get(m->image_cache, e);
388 if (image) {
389 *found = image;
390 return 1;
391 }
392
393 r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
394 if (r < 0)
395 return r;
396
397 if (!m->image_cache_defer_event) {
398 r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
399 if (r < 0)
400 return r;
401
402 r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
403 if (r < 0)
404 return r;
405 }
406
407 r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
408 if (r < 0)
409 return r;
410
411 r = image_find(IMAGE_MACHINE, e, &image);
412 if (r == -ENOENT)
413 return 0;
414 if (r < 0)
415 return r;
416
417 image->userdata = m;
418
419 r = hashmap_put(m->image_cache, image->name, image);
420 if (r < 0) {
421 image_unref(image);
422 return r;
423 }
424
425 *found = image;
426 return 1;
427 }
428
429 char *image_bus_path(const char *name) {
430 _cleanup_free_ char *e = NULL;
431
432 assert(name);
433
434 e = bus_label_escape(name);
435 if (!e)
436 return NULL;
437
438 return strjoin("/org/freedesktop/machine1/image/", e);
439 }
440
441 static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
442 _cleanup_hashmap_free_ Hashmap *images = NULL;
443 _cleanup_strv_free_ char **l = NULL;
444 Image *image;
445 int r;
446
447 assert(bus);
448 assert(path);
449 assert(nodes);
450
451 images = hashmap_new(&image_hash_ops);
452 if (!images)
453 return -ENOMEM;
454
455 r = image_discover(IMAGE_MACHINE, images);
456 if (r < 0)
457 return r;
458
459 HASHMAP_FOREACH(image, images) {
460 char *p;
461
462 p = image_bus_path(image->name);
463 if (!p)
464 return -ENOMEM;
465
466 r = strv_consume(&l, p);
467 if (r < 0)
468 return r;
469 }
470
471 *nodes = TAKE_PTR(l);
472
473 return 1;
474 }
475
476 const sd_bus_vtable image_vtable[] = {
477 SD_BUS_VTABLE_START(0),
478 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
479 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
480 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
481 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
482 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
483 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
484 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
485 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
486 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
487 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
488 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
489 SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
490 SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
491 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
492 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
493 SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
494 SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
495 SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
496 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
497 SD_BUS_VTABLE_END
498 };
499
500 const BusObjectImplementation image_object = {
501 "/org/freedesktop/machine1/image",
502 "org.freedesktop.machine1.Image",
503 .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, image_object_find}),
504 .node_enumerator = image_node_enumerator,
505 };