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