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