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