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