]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image-dbus.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / machine / image-dbus.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ebeccf9e
LP
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
fe993888 21#include <sys/file.h>
9153b02b
LP
22#include <sys/mount.h>
23
b5efdb8a 24#include "alloc-util.h"
ebeccf9e 25#include "bus-label.h"
1ddb263d 26#include "bus-util.h"
9153b02b
LP
27#include "copy.h"
28#include "dissect-image.h"
56599585 29#include "fd-util.h"
9153b02b
LP
30#include "fileio.h"
31#include "fs-util.h"
003dffde 32#include "image-dbus.h"
a90fb858 33#include "io-util.h"
9153b02b 34#include "loop-util.h"
ee104e11 35#include "machine-image.h"
9153b02b 36#include "mount-util.h"
56599585 37#include "process-util.h"
9153b02b 38#include "raw-clone.h"
ee104e11
LP
39#include "strv.h"
40#include "user-util.h"
ebeccf9e 41
1ddb263d 42static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
ebeccf9e 43
1ddb263d 44int bus_image_method_remove(
08682124
LP
45 sd_bus_message *message,
46 void *userdata,
47 sd_bus_error *error) {
48
5d2036b5 49 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1ddb263d 50 Image *image = userdata;
70244d1d 51 Manager *m = image->userdata;
5d2036b5 52 pid_t child;
08682124
LP
53 int r;
54
08682124 55 assert(message);
1ddb263d 56 assert(image);
08682124 57
5d2036b5
LP
58 if (m->n_operations >= OPERATIONS_MAX)
59 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
60
70244d1d
LP
61 r = bus_verify_polkit_async(
62 message,
63 CAP_SYS_ADMIN,
64 "org.freedesktop.machine1.manage-images",
403ed0e5 65 NULL,
70244d1d 66 false,
c529695e 67 UID_INVALID,
70244d1d
LP
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
5d2036b5
LP
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
4c253ed1
LP
78 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
79 if (r < 0)
80 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
81 if (r == 0) {
5d2036b5
LP
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
03c2b288 95 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
5d2036b5
LP
96 if (r < 0) {
97 (void) sigkill_wait(child);
08682124 98 return r;
5d2036b5
LP
99 }
100
101 errno_pipe_fd[0] = -1;
08682124 102
5d2036b5 103 return 1;
08682124
LP
104}
105
1ddb263d 106int bus_image_method_rename(
ebd93cb6
LP
107 sd_bus_message *message,
108 void *userdata,
109 sd_bus_error *error) {
110
1ddb263d 111 Image *image = userdata;
70244d1d 112 Manager *m = image->userdata;
ebd93cb6
LP
113 const char *new_name;
114 int r;
115
ebd93cb6 116 assert(message);
1ddb263d 117 assert(image);
ebd93cb6
LP
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
70244d1d
LP
126 r = bus_verify_polkit_async(
127 message,
128 CAP_SYS_ADMIN,
129 "org.freedesktop.machine1.manage-images",
403ed0e5 130 NULL,
70244d1d 131 false,
c529695e 132 UID_INVALID,
70244d1d
LP
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
ebd93cb6
LP
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
1ddb263d 147int bus_image_method_clone(
ebd93cb6
LP
148 sd_bus_message *message,
149 void *userdata,
150 sd_bus_error *error) {
151
56599585 152 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1ddb263d 153 Image *image = userdata;
70244d1d 154 Manager *m = image->userdata;
ebd93cb6
LP
155 const char *new_name;
156 int r, read_only;
56599585 157 pid_t child;
ebd93cb6 158
ebd93cb6 159 assert(message);
1ddb263d 160 assert(image);
56599585
LP
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.");
ebd93cb6
LP
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
70244d1d
LP
173 r = bus_verify_polkit_async(
174 message,
175 CAP_SYS_ADMIN,
176 "org.freedesktop.machine1.manage-images",
403ed0e5 177 NULL,
70244d1d 178 false,
c529695e 179 UID_INVALID,
70244d1d
LP
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
56599585
LP
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
4c253ed1
LP
190 r = safe_fork("(imgclone)", FORK_RESET_SIGNALS, &child);
191 if (r < 0)
192 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
193 if (r == 0) {
56599585
LP
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
03c2b288 207 r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
56599585 208 if (r < 0) {
89c9030d 209 (void) sigkill_wait(child);
ebd93cb6 210 return r;
56599585 211 }
ebd93cb6 212
56599585
LP
213 errno_pipe_fd[0] = -1;
214
215 return 1;
ebd93cb6
LP
216}
217
1ddb263d 218int bus_image_method_mark_read_only(
ebd93cb6
LP
219 sd_bus_message *message,
220 void *userdata,
221 sd_bus_error *error) {
222
1ddb263d 223 Image *image = userdata;
70244d1d 224 Manager *m = image->userdata;
ebd93cb6
LP
225 int r, read_only;
226
ebd93cb6
LP
227 assert(message);
228
ebd93cb6
LP
229 r = sd_bus_message_read(message, "b", &read_only);
230 if (r < 0)
231 return r;
232
70244d1d
LP
233 r = bus_verify_polkit_async(
234 message,
235 CAP_SYS_ADMIN,
236 "org.freedesktop.machine1.manage-images",
403ed0e5 237 NULL,
70244d1d 238 false,
c529695e 239 UID_INVALID,
70244d1d
LP
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
ebd93cb6
LP
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
d6ce17c7 254int bus_image_method_set_limit(
d6ce17c7
LP
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
d6ce17c7
LP
264 assert(message);
265
266 r = sd_bus_message_read(message, "t", &limit);
267 if (r < 0)
268 return r;
a90fb858
LP
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");
d6ce17c7
LP
271
272 r = bus_verify_polkit_async(
273 message,
274 CAP_SYS_ADMIN,
275 "org.freedesktop.machine1.manage-images",
403ed0e5 276 NULL,
d6ce17c7
LP
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
cf30a8c1
LP
293int bus_image_method_get_hostname(
294 sd_bus_message *message,
295 void *userdata,
296 sd_bus_error *error) {
9153b02b 297
cf30a8c1 298 Image *image = userdata;
9153b02b
LP
299 int r;
300
cf30a8c1
LP
301 if (!image->metadata_valid) {
302 r = image_read_metadata(image);
303 if (r < 0)
304 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
305 }
9153b02b 306
cf30a8c1 307 return sd_bus_reply_method_return(message, "s", image->hostname);
9153b02b
LP
308}
309
cf30a8c1
LP
310int bus_image_method_get_machine_id(
311 sd_bus_message *message,
312 void *userdata,
313 sd_bus_error *error) {
9153b02b 314
cf30a8c1
LP
315 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
316 Image *image = userdata;
317 int r;
9153b02b 318
cf30a8c1
LP
319 if (!image->metadata_valid) {
320 r = image_read_metadata(image);
9153b02b 321 if (r < 0)
cf30a8c1 322 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
9153b02b
LP
323 }
324
cf30a8c1 325 r = sd_bus_message_new_method_return(message, &reply);
9153b02b
LP
326 if (r < 0)
327 return r;
328
cf30a8c1
LP
329 if (sd_id128_is_null(image->machine_id)) /* Add an empty array if the ID is zero */
330 r = sd_bus_message_append(reply, "ay", 0);
331 else
332 r = sd_bus_message_append_array(reply, 'y', image->machine_id.bytes, 16);
9153b02b 333 if (r < 0)
cf30a8c1 334 return r;
9153b02b 335
cf30a8c1 336 return sd_bus_send(NULL, reply, NULL);
9153b02b
LP
337}
338
cf30a8c1 339int bus_image_method_get_machine_info(
9153b02b
LP
340 sd_bus_message *message,
341 void *userdata,
342 sd_bus_error *error) {
343
9153b02b
LP
344 Image *image = userdata;
345 int r;
346
cf30a8c1
LP
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 }
9153b02b 352
cf30a8c1
LP
353 return bus_reply_pair_array(message, image->machine_info);
354}
9153b02b 355
cf30a8c1
LP
356int bus_image_method_get_os_release(
357 sd_bus_message *message,
358 void *userdata,
359 sd_bus_error *error) {
9153b02b 360
cf30a8c1
LP
361 Image *image = userdata;
362 int r;
9153b02b 363
cf30a8c1
LP
364 if (!image->metadata_valid) {
365 r = image_read_metadata(image);
366 if (r < 0)
367 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
9153b02b 368 }
9153b02b 369
cf30a8c1 370 return bus_reply_pair_array(message, image->os_release);
9153b02b
LP
371}
372
ebeccf9e
LP
373const sd_bus_vtable image_vtable[] = {
374 SD_BUS_VTABLE_START(0),
1ddb263d
LP
375 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
376 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
377 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
378 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
379 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
380 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
c19de711 381 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
1ddb263d 382 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
c19de711 383 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
1ddb263d 384 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
70244d1d
LP
385 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
386 SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
387 SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
388 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
d6ce17c7 389 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
cf30a8c1
LP
390 SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
391 SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
392 SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
9153b02b 393 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
ebeccf9e
LP
394 SD_BUS_VTABLE_END
395};
396
1ddb263d
LP
397static int image_flush_cache(sd_event_source *s, void *userdata) {
398 Manager *m = userdata;
1ddb263d
LP
399
400 assert(s);
401 assert(m);
402
224b0e7a 403 hashmap_clear_with_destructor(m->image_cache, image_unref);
1ddb263d
LP
404 return 0;
405}
406
ebeccf9e 407int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1ddb263d
LP
408 _cleanup_free_ char *e = NULL;
409 Manager *m = userdata;
410 Image *image = NULL;
411 const char *p;
ebeccf9e
LP
412 int r;
413
414 assert(bus);
415 assert(path);
416 assert(interface);
417 assert(found);
418
1ddb263d
LP
419 p = startswith(path, "/org/freedesktop/machine1/image/");
420 if (!p)
421 return 0;
422
423 e = bus_label_unescape(p);
424 if (!e)
425 return -ENOMEM;
426
427 image = hashmap_get(m->image_cache, e);
428 if (image) {
429 *found = image;
430 return 1;
431 }
432
433 r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
434 if (r < 0)
435 return r;
436
437 if (!m->image_cache_defer_event) {
438 r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
439 if (r < 0)
440 return r;
441
442 r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
443 if (r < 0)
444 return r;
445 }
446
447 r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
448 if (r < 0)
449 return r;
450
451 r = image_find(e, &image);
ebeccf9e
LP
452 if (r <= 0)
453 return r;
454
70244d1d
LP
455 image->userdata = m;
456
1ddb263d
LP
457 r = hashmap_put(m->image_cache, image->name, image);
458 if (r < 0) {
459 image_unref(image);
460 return r;
461 }
462
463 *found = image;
ebeccf9e
LP
464 return 1;
465}
466
467char *image_bus_path(const char *name) {
468 _cleanup_free_ char *e = NULL;
469
470 assert(name);
471
472 e = bus_label_escape(name);
473 if (!e)
474 return NULL;
475
476 return strappend("/org/freedesktop/machine1/image/", e);
477}
478
479int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
480 _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
481 _cleanup_strv_free_ char **l = NULL;
482 Image *image;
483 Iterator i;
484 int r;
485
486 assert(bus);
487 assert(path);
488 assert(nodes);
489
490 images = hashmap_new(&string_hash_ops);
491 if (!images)
492 return -ENOMEM;
493
494 r = image_discover(images);
495 if (r < 0)
496 return r;
497
498 HASHMAP_FOREACH(image, images, i) {
499 char *p;
500
501 p = image_bus_path(image->name);
502 if (!p)
503 return -ENOMEM;
504
505 r = strv_consume(&l, p);
506 if (r < 0)
507 return r;
508 }
509
1cc6c93a 510 *nodes = TAKE_PTR(l);
ebeccf9e
LP
511
512 return 1;
513}