]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
ebeccf9e | 2 | |
fe993888 | 3 | #include <sys/file.h> |
9153b02b LP |
4 | #include <sys/mount.h> |
5 | ||
b5efdb8a | 6 | #include "alloc-util.h" |
ebeccf9e | 7 | #include "bus-label.h" |
1ddb263d | 8 | #include "bus-util.h" |
9153b02b LP |
9 | #include "copy.h" |
10 | #include "dissect-image.h" | |
56599585 | 11 | #include "fd-util.h" |
9153b02b LP |
12 | #include "fileio.h" |
13 | #include "fs-util.h" | |
003dffde | 14 | #include "image-dbus.h" |
a90fb858 | 15 | #include "io-util.h" |
9153b02b | 16 | #include "loop-util.h" |
ee104e11 | 17 | #include "machine-image.h" |
9153b02b | 18 | #include "mount-util.h" |
56599585 | 19 | #include "process-util.h" |
9153b02b | 20 | #include "raw-clone.h" |
ee104e11 LP |
21 | #include "strv.h" |
22 | #include "user-util.h" | |
ebeccf9e | 23 | |
1ddb263d | 24 | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType); |
ebeccf9e | 25 | |
1ddb263d | 26 | int bus_image_method_remove( |
08682124 LP |
27 | sd_bus_message *message, |
28 | void *userdata, | |
29 | sd_bus_error *error) { | |
30 | ||
5d2036b5 | 31 | _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; |
1ddb263d | 32 | Image *image = userdata; |
70244d1d | 33 | Manager *m = image->userdata; |
5d2036b5 | 34 | pid_t child; |
08682124 LP |
35 | int r; |
36 | ||
08682124 | 37 | assert(message); |
1ddb263d | 38 | assert(image); |
08682124 | 39 | |
5d2036b5 LP |
40 | if (m->n_operations >= OPERATIONS_MAX) |
41 | return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); | |
42 | ||
70244d1d LP |
43 | r = bus_verify_polkit_async( |
44 | message, | |
45 | CAP_SYS_ADMIN, | |
46 | "org.freedesktop.machine1.manage-images", | |
403ed0e5 | 47 | NULL, |
70244d1d | 48 | false, |
c529695e | 49 | UID_INVALID, |
70244d1d LP |
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 | ||
5d2036b5 LP |
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 | ||
4c253ed1 LP |
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) { | |
5d2036b5 LP |
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 | ||
03c2b288 | 77 | r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); |
5d2036b5 LP |
78 | if (r < 0) { |
79 | (void) sigkill_wait(child); | |
08682124 | 80 | return r; |
5d2036b5 LP |
81 | } |
82 | ||
83 | errno_pipe_fd[0] = -1; | |
08682124 | 84 | |
5d2036b5 | 85 | return 1; |
08682124 LP |
86 | } |
87 | ||
1ddb263d | 88 | int bus_image_method_rename( |
ebd93cb6 LP |
89 | sd_bus_message *message, |
90 | void *userdata, | |
91 | sd_bus_error *error) { | |
92 | ||
1ddb263d | 93 | Image *image = userdata; |
70244d1d | 94 | Manager *m = image->userdata; |
ebd93cb6 LP |
95 | const char *new_name; |
96 | int r; | |
97 | ||
ebd93cb6 | 98 | assert(message); |
1ddb263d | 99 | assert(image); |
ebd93cb6 LP |
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 | ||
70244d1d LP |
108 | r = bus_verify_polkit_async( |
109 | message, | |
110 | CAP_SYS_ADMIN, | |
111 | "org.freedesktop.machine1.manage-images", | |
403ed0e5 | 112 | NULL, |
70244d1d | 113 | false, |
c529695e | 114 | UID_INVALID, |
70244d1d LP |
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 | ||
ebd93cb6 LP |
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 | ||
1ddb263d | 129 | int bus_image_method_clone( |
ebd93cb6 LP |
130 | sd_bus_message *message, |
131 | void *userdata, | |
132 | sd_bus_error *error) { | |
133 | ||
56599585 | 134 | _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; |
1ddb263d | 135 | Image *image = userdata; |
70244d1d | 136 | Manager *m = image->userdata; |
ebd93cb6 LP |
137 | const char *new_name; |
138 | int r, read_only; | |
56599585 | 139 | pid_t child; |
ebd93cb6 | 140 | |
ebd93cb6 | 141 | assert(message); |
1ddb263d | 142 | assert(image); |
56599585 LP |
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."); | |
ebd93cb6 LP |
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 | ||
70244d1d LP |
155 | r = bus_verify_polkit_async( |
156 | message, | |
157 | CAP_SYS_ADMIN, | |
158 | "org.freedesktop.machine1.manage-images", | |
403ed0e5 | 159 | NULL, |
70244d1d | 160 | false, |
c529695e | 161 | UID_INVALID, |
70244d1d LP |
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 | ||
56599585 LP |
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 | ||
4c253ed1 LP |
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) { | |
56599585 LP |
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 | ||
03c2b288 | 189 | r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); |
56599585 | 190 | if (r < 0) { |
89c9030d | 191 | (void) sigkill_wait(child); |
ebd93cb6 | 192 | return r; |
56599585 | 193 | } |
ebd93cb6 | 194 | |
56599585 LP |
195 | errno_pipe_fd[0] = -1; |
196 | ||
197 | return 1; | |
ebd93cb6 LP |
198 | } |
199 | ||
1ddb263d | 200 | int bus_image_method_mark_read_only( |
ebd93cb6 LP |
201 | sd_bus_message *message, |
202 | void *userdata, | |
203 | sd_bus_error *error) { | |
204 | ||
1ddb263d | 205 | Image *image = userdata; |
70244d1d | 206 | Manager *m = image->userdata; |
ebd93cb6 LP |
207 | int r, read_only; |
208 | ||
ebd93cb6 LP |
209 | assert(message); |
210 | ||
ebd93cb6 LP |
211 | r = sd_bus_message_read(message, "b", &read_only); |
212 | if (r < 0) | |
213 | return r; | |
214 | ||
70244d1d LP |
215 | r = bus_verify_polkit_async( |
216 | message, | |
217 | CAP_SYS_ADMIN, | |
218 | "org.freedesktop.machine1.manage-images", | |
403ed0e5 | 219 | NULL, |
70244d1d | 220 | false, |
c529695e | 221 | UID_INVALID, |
70244d1d LP |
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 | ||
ebd93cb6 LP |
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 | ||
d6ce17c7 | 236 | int bus_image_method_set_limit( |
d6ce17c7 LP |
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 | ||
d6ce17c7 LP |
246 | assert(message); |
247 | ||
248 | r = sd_bus_message_read(message, "t", &limit); | |
249 | if (r < 0) | |
250 | return r; | |
a90fb858 LP |
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"); | |
d6ce17c7 LP |
253 | |
254 | r = bus_verify_polkit_async( | |
255 | message, | |
256 | CAP_SYS_ADMIN, | |
257 | "org.freedesktop.machine1.manage-images", | |
403ed0e5 | 258 | NULL, |
d6ce17c7 LP |
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 | ||
cf30a8c1 LP |
275 | int bus_image_method_get_hostname( |
276 | sd_bus_message *message, | |
277 | void *userdata, | |
278 | sd_bus_error *error) { | |
9153b02b | 279 | |
cf30a8c1 | 280 | Image *image = userdata; |
9153b02b LP |
281 | int r; |
282 | ||
cf30a8c1 LP |
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 | } | |
9153b02b | 288 | |
cf30a8c1 | 289 | return sd_bus_reply_method_return(message, "s", image->hostname); |
9153b02b LP |
290 | } |
291 | ||
cf30a8c1 LP |
292 | int bus_image_method_get_machine_id( |
293 | sd_bus_message *message, | |
294 | void *userdata, | |
295 | sd_bus_error *error) { | |
9153b02b | 296 | |
cf30a8c1 LP |
297 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
298 | Image *image = userdata; | |
299 | int r; | |
9153b02b | 300 | |
cf30a8c1 LP |
301 | if (!image->metadata_valid) { |
302 | r = image_read_metadata(image); | |
9153b02b | 303 | if (r < 0) |
cf30a8c1 | 304 | return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m"); |
9153b02b LP |
305 | } |
306 | ||
cf30a8c1 | 307 | r = sd_bus_message_new_method_return(message, &reply); |
9153b02b LP |
308 | if (r < 0) |
309 | return r; | |
310 | ||
cf30a8c1 LP |
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); | |
9153b02b | 315 | if (r < 0) |
cf30a8c1 | 316 | return r; |
9153b02b | 317 | |
cf30a8c1 | 318 | return sd_bus_send(NULL, reply, NULL); |
9153b02b LP |
319 | } |
320 | ||
cf30a8c1 | 321 | int bus_image_method_get_machine_info( |
9153b02b LP |
322 | sd_bus_message *message, |
323 | void *userdata, | |
324 | sd_bus_error *error) { | |
325 | ||
9153b02b LP |
326 | Image *image = userdata; |
327 | int r; | |
328 | ||
cf30a8c1 LP |
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 | } | |
9153b02b | 334 | |
cf30a8c1 LP |
335 | return bus_reply_pair_array(message, image->machine_info); |
336 | } | |
9153b02b | 337 | |
cf30a8c1 LP |
338 | int bus_image_method_get_os_release( |
339 | sd_bus_message *message, | |
340 | void *userdata, | |
341 | sd_bus_error *error) { | |
9153b02b | 342 | |
cf30a8c1 LP |
343 | Image *image = userdata; |
344 | int r; | |
9153b02b | 345 | |
cf30a8c1 LP |
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"); | |
9153b02b | 350 | } |
9153b02b | 351 | |
cf30a8c1 | 352 | return bus_reply_pair_array(message, image->os_release); |
9153b02b LP |
353 | } |
354 | ||
ebeccf9e LP |
355 | const sd_bus_vtable image_vtable[] = { |
356 | SD_BUS_VTABLE_START(0), | |
1ddb263d LP |
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), | |
c19de711 | 363 | SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0), |
1ddb263d | 364 | SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0), |
c19de711 | 365 | SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0), |
1ddb263d | 366 | SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0), |
70244d1d LP |
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), | |
d6ce17c7 | 371 | SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED), |
cf30a8c1 LP |
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), | |
9153b02b | 375 | SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), |
ebeccf9e LP |
376 | SD_BUS_VTABLE_END |
377 | }; | |
378 | ||
1ddb263d LP |
379 | static int image_flush_cache(sd_event_source *s, void *userdata) { |
380 | Manager *m = userdata; | |
1ddb263d LP |
381 | |
382 | assert(s); | |
383 | assert(m); | |
384 | ||
b07ec5a1 | 385 | hashmap_clear(m->image_cache); |
1ddb263d LP |
386 | return 0; |
387 | } | |
388 | ||
ebeccf9e | 389 | int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { |
1ddb263d LP |
390 | _cleanup_free_ char *e = NULL; |
391 | Manager *m = userdata; | |
392 | Image *image = NULL; | |
393 | const char *p; | |
ebeccf9e LP |
394 | int r; |
395 | ||
396 | assert(bus); | |
397 | assert(path); | |
398 | assert(interface); | |
399 | assert(found); | |
400 | ||
1ddb263d LP |
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 | ||
b07ec5a1 | 415 | r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops); |
1ddb263d LP |
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 | ||
5ef46e5f | 433 | r = image_find(IMAGE_MACHINE, e, &image); |
3a6ce860 LP |
434 | if (r == -ENOENT) |
435 | return 0; | |
436 | if (r < 0) | |
ebeccf9e LP |
437 | return r; |
438 | ||
70244d1d LP |
439 | image->userdata = m; |
440 | ||
1ddb263d LP |
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; | |
ebeccf9e LP |
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) { | |
b07ec5a1 | 464 | _cleanup_hashmap_free_ Hashmap *images = NULL; |
ebeccf9e LP |
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 | ||
b07ec5a1 | 474 | images = hashmap_new(&image_hash_ops); |
ebeccf9e LP |
475 | if (!images) |
476 | return -ENOMEM; | |
477 | ||
5ef46e5f | 478 | r = image_discover(IMAGE_MACHINE, images); |
ebeccf9e LP |
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 | ||
1cc6c93a | 494 | *nodes = TAKE_PTR(l); |
ebeccf9e LP |
495 | |
496 | return 1; | |
497 | } |