]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/image-dbus.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[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 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) {
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 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) {
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 Image *image = userdata;
299 int r;
300
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 }
306
307 return sd_bus_reply_method_return(message, "s", image->hostname);
308 }
309
310 int bus_image_method_get_machine_id(
311 sd_bus_message *message,
312 void *userdata,
313 sd_bus_error *error) {
314
315 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
316 Image *image = userdata;
317 int r;
318
319 if (!image->metadata_valid) {
320 r = image_read_metadata(image);
321 if (r < 0)
322 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
323 }
324
325 r = sd_bus_message_new_method_return(message, &reply);
326 if (r < 0)
327 return r;
328
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);
333 if (r < 0)
334 return r;
335
336 return sd_bus_send(NULL, reply, NULL);
337 }
338
339 int bus_image_method_get_machine_info(
340 sd_bus_message *message,
341 void *userdata,
342 sd_bus_error *error) {
343
344 Image *image = userdata;
345 int r;
346
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 }
352
353 return bus_reply_pair_array(message, image->machine_info);
354 }
355
356 int bus_image_method_get_os_release(
357 sd_bus_message *message,
358 void *userdata,
359 sd_bus_error *error) {
360
361 Image *image = userdata;
362 int r;
363
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");
368 }
369
370 return bus_reply_pair_array(message, image->os_release);
371 }
372
373 const sd_bus_vtable image_vtable[] = {
374 SD_BUS_VTABLE_START(0),
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),
381 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
382 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
383 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
384 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
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),
389 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
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),
393 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
394 SD_BUS_VTABLE_END
395 };
396
397 static int image_flush_cache(sd_event_source *s, void *userdata) {
398 Manager *m = userdata;
399
400 assert(s);
401 assert(m);
402
403 hashmap_clear_with_destructor(m->image_cache, image_unref);
404 return 0;
405 }
406
407 int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
408 _cleanup_free_ char *e = NULL;
409 Manager *m = userdata;
410 Image *image = NULL;
411 const char *p;
412 int r;
413
414 assert(bus);
415 assert(path);
416 assert(interface);
417 assert(found);
418
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);
452 if (r <= 0)
453 return r;
454
455 image->userdata = m;
456
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;
464 return 1;
465 }
466
467 char *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
479 int 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
510 *nodes = TAKE_PTR(l);
511
512 return 1;
513 }