Line data Source code
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 0 : static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
26 :
27 0 : int bus_image_method_remove(
28 : sd_bus_message *message,
29 : void *userdata,
30 : sd_bus_error *error) {
31 :
32 0 : _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
33 0 : Image *image = userdata;
34 0 : Manager *m = image->userdata;
35 : pid_t child;
36 : int r;
37 :
38 0 : assert(message);
39 0 : assert(image);
40 :
41 0 : if (m->n_operations >= OPERATIONS_MAX)
42 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
43 :
44 0 : 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 0 : if (r < 0)
54 0 : return r;
55 0 : if (r == 0)
56 0 : return 1; /* Will call us back */
57 :
58 0 : if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
59 0 : return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
60 :
61 0 : r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
62 0 : if (r < 0)
63 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
64 0 : if (r == 0) {
65 0 : errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
66 :
67 0 : r = image_remove(image);
68 0 : if (r < 0) {
69 0 : (void) write(errno_pipe_fd[1], &r, sizeof(r));
70 0 : _exit(EXIT_FAILURE);
71 : }
72 :
73 0 : _exit(EXIT_SUCCESS);
74 : }
75 :
76 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
77 :
78 0 : r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
79 0 : if (r < 0) {
80 0 : (void) sigkill_wait(child);
81 0 : return r;
82 : }
83 :
84 0 : errno_pipe_fd[0] = -1;
85 :
86 0 : return 1;
87 : }
88 :
89 0 : int bus_image_method_rename(
90 : sd_bus_message *message,
91 : void *userdata,
92 : sd_bus_error *error) {
93 :
94 0 : Image *image = userdata;
95 0 : Manager *m = image->userdata;
96 : const char *new_name;
97 : int r;
98 :
99 0 : assert(message);
100 0 : assert(image);
101 :
102 0 : r = sd_bus_message_read(message, "s", &new_name);
103 0 : if (r < 0)
104 0 : return r;
105 :
106 0 : if (!image_name_is_valid(new_name))
107 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
108 :
109 0 : 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 0 : if (r < 0)
119 0 : return r;
120 0 : if (r == 0)
121 0 : return 1; /* Will call us back */
122 :
123 0 : r = image_rename(image, new_name);
124 0 : if (r < 0)
125 0 : return r;
126 :
127 0 : return sd_bus_reply_method_return(message, NULL);
128 : }
129 :
130 0 : int bus_image_method_clone(
131 : sd_bus_message *message,
132 : void *userdata,
133 : sd_bus_error *error) {
134 :
135 0 : _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
136 0 : Image *image = userdata;
137 0 : Manager *m = image->userdata;
138 : const char *new_name;
139 : int r, read_only;
140 : pid_t child;
141 :
142 0 : assert(message);
143 0 : assert(image);
144 0 : assert(m);
145 :
146 0 : if (m->n_operations >= OPERATIONS_MAX)
147 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
148 :
149 0 : r = sd_bus_message_read(message, "sb", &new_name, &read_only);
150 0 : if (r < 0)
151 0 : return r;
152 :
153 0 : if (!image_name_is_valid(new_name))
154 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
155 :
156 0 : 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 0 : if (r < 0)
166 0 : return r;
167 0 : if (r == 0)
168 0 : return 1; /* Will call us back */
169 :
170 0 : if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
171 0 : return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
172 :
173 0 : r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
174 0 : if (r < 0)
175 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
176 0 : if (r == 0) {
177 0 : errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
178 :
179 0 : r = image_clone(image, new_name, read_only);
180 0 : if (r < 0) {
181 0 : (void) write(errno_pipe_fd[1], &r, sizeof(r));
182 0 : _exit(EXIT_FAILURE);
183 : }
184 :
185 0 : _exit(EXIT_SUCCESS);
186 : }
187 :
188 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
189 :
190 0 : r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
191 0 : if (r < 0) {
192 0 : (void) sigkill_wait(child);
193 0 : return r;
194 : }
195 :
196 0 : errno_pipe_fd[0] = -1;
197 :
198 0 : return 1;
199 : }
200 :
201 0 : int bus_image_method_mark_read_only(
202 : sd_bus_message *message,
203 : void *userdata,
204 : sd_bus_error *error) {
205 :
206 0 : Image *image = userdata;
207 0 : Manager *m = image->userdata;
208 : int r, read_only;
209 :
210 0 : assert(message);
211 :
212 0 : r = sd_bus_message_read(message, "b", &read_only);
213 0 : if (r < 0)
214 0 : return r;
215 :
216 0 : 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 0 : if (r < 0)
226 0 : return r;
227 0 : if (r == 0)
228 0 : return 1; /* Will call us back */
229 :
230 0 : r = image_read_only(image, read_only);
231 0 : if (r < 0)
232 0 : return r;
233 :
234 0 : return sd_bus_reply_method_return(message, NULL);
235 : }
236 :
237 0 : int bus_image_method_set_limit(
238 : sd_bus_message *message,
239 : void *userdata,
240 : sd_bus_error *error) {
241 :
242 0 : Image *image = userdata;
243 0 : Manager *m = image->userdata;
244 : uint64_t limit;
245 : int r;
246 :
247 0 : assert(message);
248 :
249 0 : r = sd_bus_message_read(message, "t", &limit);
250 0 : if (r < 0)
251 0 : return r;
252 0 : if (!FILE_SIZE_VALID_OR_INFINITY(limit))
253 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
254 :
255 0 : 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 0 : if (r < 0)
265 0 : return r;
266 0 : if (r == 0)
267 0 : return 1; /* Will call us back */
268 :
269 0 : r = image_set_limit(image, limit);
270 0 : if (r < 0)
271 0 : return r;
272 :
273 0 : return sd_bus_reply_method_return(message, NULL);
274 : }
275 :
276 0 : int bus_image_method_get_hostname(
277 : sd_bus_message *message,
278 : void *userdata,
279 : sd_bus_error *error) {
280 :
281 0 : Image *image = userdata;
282 : int r;
283 :
284 0 : if (!image->metadata_valid) {
285 0 : r = image_read_metadata(image);
286 0 : if (r < 0)
287 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
288 : }
289 :
290 0 : return sd_bus_reply_method_return(message, "s", image->hostname);
291 : }
292 :
293 0 : int bus_image_method_get_machine_id(
294 : sd_bus_message *message,
295 : void *userdata,
296 : sd_bus_error *error) {
297 :
298 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
299 0 : Image *image = userdata;
300 : int r;
301 :
302 0 : if (!image->metadata_valid) {
303 0 : r = image_read_metadata(image);
304 0 : if (r < 0)
305 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
306 : }
307 :
308 0 : r = sd_bus_message_new_method_return(message, &reply);
309 0 : if (r < 0)
310 0 : return r;
311 :
312 0 : if (sd_id128_is_null(image->machine_id)) /* Add an empty array if the ID is zero */
313 0 : r = sd_bus_message_append(reply, "ay", 0);
314 : else
315 0 : r = sd_bus_message_append_array(reply, 'y', image->machine_id.bytes, 16);
316 0 : if (r < 0)
317 0 : return r;
318 :
319 0 : return sd_bus_send(NULL, reply, NULL);
320 : }
321 :
322 0 : int bus_image_method_get_machine_info(
323 : sd_bus_message *message,
324 : void *userdata,
325 : sd_bus_error *error) {
326 :
327 0 : Image *image = userdata;
328 : int r;
329 :
330 0 : if (!image->metadata_valid) {
331 0 : r = image_read_metadata(image);
332 0 : if (r < 0)
333 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
334 : }
335 :
336 0 : return bus_reply_pair_array(message, image->machine_info);
337 : }
338 :
339 0 : int bus_image_method_get_os_release(
340 : sd_bus_message *message,
341 : void *userdata,
342 : sd_bus_error *error) {
343 :
344 0 : Image *image = userdata;
345 : int r;
346 :
347 0 : if (!image->metadata_valid) {
348 0 : r = image_read_metadata(image);
349 0 : if (r < 0)
350 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
351 : }
352 :
353 0 : 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 0 : static int image_flush_cache(sd_event_source *s, void *userdata) {
381 0 : Manager *m = userdata;
382 :
383 0 : assert(s);
384 0 : assert(m);
385 :
386 0 : hashmap_clear(m->image_cache);
387 0 : return 0;
388 : }
389 :
390 0 : int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
391 0 : _cleanup_free_ char *e = NULL;
392 0 : Manager *m = userdata;
393 0 : Image *image = NULL;
394 : const char *p;
395 : int r;
396 :
397 0 : assert(bus);
398 0 : assert(path);
399 0 : assert(interface);
400 0 : assert(found);
401 :
402 0 : p = startswith(path, "/org/freedesktop/machine1/image/");
403 0 : if (!p)
404 0 : return 0;
405 :
406 0 : e = bus_label_unescape(p);
407 0 : if (!e)
408 0 : return -ENOMEM;
409 :
410 0 : image = hashmap_get(m->image_cache, e);
411 0 : if (image) {
412 0 : *found = image;
413 0 : return 1;
414 : }
415 :
416 0 : r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
417 0 : if (r < 0)
418 0 : return r;
419 :
420 0 : if (!m->image_cache_defer_event) {
421 0 : r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
422 0 : if (r < 0)
423 0 : return r;
424 :
425 0 : r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
426 0 : if (r < 0)
427 0 : return r;
428 : }
429 :
430 0 : r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
431 0 : if (r < 0)
432 0 : return r;
433 :
434 0 : r = image_find(IMAGE_MACHINE, e, &image);
435 0 : if (r == -ENOENT)
436 0 : return 0;
437 0 : if (r < 0)
438 0 : return r;
439 :
440 0 : image->userdata = m;
441 :
442 0 : r = hashmap_put(m->image_cache, image->name, image);
443 0 : if (r < 0) {
444 0 : image_unref(image);
445 0 : return r;
446 : }
447 :
448 0 : *found = image;
449 0 : return 1;
450 : }
451 :
452 0 : char *image_bus_path(const char *name) {
453 0 : _cleanup_free_ char *e = NULL;
454 :
455 0 : assert(name);
456 :
457 0 : e = bus_label_escape(name);
458 0 : if (!e)
459 0 : return NULL;
460 :
461 0 : return strjoin("/org/freedesktop/machine1/image/", e);
462 : }
463 :
464 0 : int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
465 0 : _cleanup_hashmap_free_ Hashmap *images = NULL;
466 0 : _cleanup_strv_free_ char **l = NULL;
467 : Image *image;
468 : Iterator i;
469 : int r;
470 :
471 0 : assert(bus);
472 0 : assert(path);
473 0 : assert(nodes);
474 :
475 0 : images = hashmap_new(&image_hash_ops);
476 0 : if (!images)
477 0 : return -ENOMEM;
478 :
479 0 : r = image_discover(IMAGE_MACHINE, images);
480 0 : if (r < 0)
481 0 : return r;
482 :
483 0 : HASHMAP_FOREACH(image, images, i) {
484 : char *p;
485 :
486 0 : p = image_bus_path(image->name);
487 0 : if (!p)
488 0 : return -ENOMEM;
489 :
490 0 : r = strv_consume(&l, p);
491 0 : if (r < 0)
492 0 : return r;
493 : }
494 :
495 0 : *nodes = TAKE_PTR(l);
496 :
497 0 : return 1;
498 : }
|