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