]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/image-dbus.c
8bc6565079768569a6f32eda50f7431693aaf5ef
[thirdparty/systemd.git] / src / machine / image-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "sd-bus.h"
6
7 #include "alloc-util.h"
8 #include "bus-get-properties.h"
9 #include "bus-label.h"
10 #include "bus-object.h"
11 #include "bus-polkit.h"
12 #include "bus-util.h"
13 #include "discover-image.h"
14 #include "fd-util.h"
15 #include "hashmap.h"
16 #include "image-dbus.h"
17 #include "image-policy.h"
18 #include "io-util.h"
19 #include "machined.h"
20 #include "operation.h"
21 #include "os-util.h"
22 #include "process-util.h"
23 #include "strv.h"
24
25 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
26
27 int bus_image_method_remove(
28 sd_bus_message *message,
29 void *userdata,
30 sd_bus_error *error) {
31
32 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
33 Image *image = ASSERT_PTR(userdata);
34 Manager *m = image->userdata;
35 pid_t child;
36 int r;
37
38 assert(message);
39
40 if (m->n_operations >= OPERATIONS_MAX)
41 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
42
43 const char *details[] = {
44 "image", image->name,
45 "verb", "remove",
46 NULL
47 };
48
49 r = bus_verify_polkit_async(
50 message,
51 "org.freedesktop.machine1.manage-images",
52 details,
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
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
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) {
67 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
68 r = image_remove(image);
69 report_errno_and_exit(errno_pipe_fd[1], r);
70 }
71
72 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
73
74 r = operation_new_with_bus_reply(m, /* machine= */ NULL, child, message, errno_pipe_fd[0], /* ret= */ NULL);
75 if (r < 0) {
76 (void) sigkill_wait(child);
77 return r;
78 }
79
80 errno_pipe_fd[0] = -EBADF;
81
82 return 1;
83 }
84
85 int bus_image_method_rename(
86 sd_bus_message *message,
87 void *userdata,
88 sd_bus_error *error) {
89
90 Image *image = ASSERT_PTR(userdata);
91 Manager *m = image->userdata;
92 const char *new_name;
93 int r;
94
95 assert(message);
96
97 r = sd_bus_message_read(message, "s", &new_name);
98 if (r < 0)
99 return r;
100
101 if (!image_name_is_valid(new_name))
102 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
103
104 const char *details[] = {
105 "image", image->name,
106 "verb", "rename",
107 "new_name", new_name,
108 NULL
109 };
110
111 r = bus_verify_polkit_async(
112 message,
113 "org.freedesktop.machine1.manage-images",
114 details,
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
122 r = rename_image_and_update_cache(m, image, new_name);
123 if (r < 0)
124 return sd_bus_error_set_errnof(error, r, "Failed to rename image: %m");
125
126 return sd_bus_reply_method_return(message, NULL);
127 }
128
129 int bus_image_method_clone(
130 sd_bus_message *message,
131 void *userdata,
132 sd_bus_error *error) {
133
134 _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
135 Image *image = ASSERT_PTR(userdata);
136 Manager *m = ASSERT_PTR(image->userdata);
137 const char *new_name;
138 int r, read_only;
139 pid_t child;
140
141 assert(message);
142
143 if (m->n_operations >= OPERATIONS_MAX)
144 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
145
146 r = sd_bus_message_read(message, "sb", &new_name, &read_only);
147 if (r < 0)
148 return r;
149
150 if (!image_name_is_valid(new_name))
151 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
152
153 const char *details[] = {
154 "image", image->name,
155 "verb", "clone",
156 "new_name", new_name,
157 NULL
158 };
159
160 r = bus_verify_polkit_async(
161 message,
162 "org.freedesktop.machine1.manage-images",
163 details,
164 &m->polkit_registry,
165 error);
166 if (r < 0)
167 return r;
168 if (r == 0)
169 return 1; /* Will call us back */
170
171 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
172 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
173
174 r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
175 if (r < 0)
176 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
177 if (r == 0) {
178 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
179 r = image_clone(image, new_name, read_only, m->runtime_scope);
180 report_errno_and_exit(errno_pipe_fd[1], r);
181 }
182
183 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
184
185 r = operation_new_with_bus_reply(m, /* machine= */ NULL, child, message, errno_pipe_fd[0], /* ret= */ NULL);
186 if (r < 0) {
187 (void) sigkill_wait(child);
188 return r;
189 }
190
191 errno_pipe_fd[0] = -EBADF;
192
193 return 1;
194 }
195
196 int bus_image_method_mark_read_only(
197 sd_bus_message *message,
198 void *userdata,
199 sd_bus_error *error) {
200
201 Image *image = userdata;
202 Manager *m = image->userdata;
203 int read_only, r;
204
205 assert(message);
206
207 r = sd_bus_message_read(message, "b", &read_only);
208 if (r < 0)
209 return r;
210
211 const char *details[] = {
212 "image", image->name,
213 "verb", "mark_read_only",
214 "read_only", one_zero(read_only),
215 NULL
216 };
217
218 r = bus_verify_polkit_async(
219 message,
220 "org.freedesktop.machine1.manage-images",
221 details,
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
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
236 int bus_image_method_set_limit(
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
246 assert(message);
247
248 r = sd_bus_message_read(message, "t", &limit);
249 if (r < 0)
250 return r;
251 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
252 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
253
254 const char *details[] = {
255 "machine", image->name,
256 "verb", "set_limit",
257 NULL
258 };
259
260 r = bus_verify_polkit_async(
261 message,
262 "org.freedesktop.machine1.manage-images",
263 details,
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
278 int bus_image_method_get_hostname(
279 sd_bus_message *message,
280 void *userdata,
281 sd_bus_error *error) {
282
283 Image *image = userdata;
284 int r;
285
286 if (!image->metadata_valid) {
287 r = image_read_metadata(image, &image_policy_container);
288 if (r < 0)
289 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
290 }
291
292 return sd_bus_reply_method_return(message, "s", image->hostname);
293 }
294
295 int bus_image_method_get_machine_id(
296 sd_bus_message *message,
297 void *userdata,
298 sd_bus_error *error) {
299
300 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
301 Image *image = userdata;
302 int r;
303
304 if (!image->metadata_valid) {
305 r = image_read_metadata(image, &image_policy_container);
306 if (r < 0)
307 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
308 }
309
310 r = sd_bus_message_new_method_return(message, &reply);
311 if (r < 0)
312 return r;
313
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);
318 if (r < 0)
319 return r;
320
321 return sd_bus_message_send(reply);
322 }
323
324 int bus_image_method_get_machine_info(
325 sd_bus_message *message,
326 void *userdata,
327 sd_bus_error *error) {
328
329 Image *image = userdata;
330 int r;
331
332 if (!image->metadata_valid) {
333 r = image_read_metadata(image, &image_policy_container);
334 if (r < 0)
335 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
336 }
337
338 return bus_reply_pair_array(message, image->machine_info);
339 }
340
341 int bus_image_method_get_os_release(
342 sd_bus_message *message,
343 void *userdata,
344 sd_bus_error *error) {
345
346 Image *image = userdata;
347 int r;
348
349 if (!image->metadata_valid) {
350 r = image_read_metadata(image, &image_policy_container);
351 if (r < 0)
352 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
353 }
354
355 return bus_reply_pair_array(message, image->os_release);
356 }
357
358 static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
359 _cleanup_free_ char *e = NULL;
360 Manager *m = userdata;
361 Image *image;
362 const char *p;
363 int r;
364
365 assert(bus);
366 assert(path);
367 assert(interface);
368 assert(found);
369
370 p = startswith(path, "/org/freedesktop/machine1/image/");
371 if (!p)
372 return 0;
373
374 e = bus_label_unescape(p);
375 if (!e)
376 return -ENOMEM;
377
378 r = manager_acquire_image(m, e, &image);
379 if (r == -ENOENT)
380 return 0;
381 if (r < 0)
382 return r;
383
384 *found = image;
385 return 1;
386 }
387
388 char* image_bus_path(const char *name) {
389 _cleanup_free_ char *e = NULL;
390
391 assert(name);
392
393 e = bus_label_escape(name);
394 if (!e)
395 return NULL;
396
397 return strjoin("/org/freedesktop/machine1/image/", e);
398 }
399
400 static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
401 Manager *m = ASSERT_PTR(userdata);
402 int r;
403
404 assert(bus);
405 assert(path);
406 assert(nodes);
407
408 _cleanup_hashmap_free_ Hashmap *images = NULL;
409 r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, &images);
410 if (r < 0)
411 return r;
412
413 _cleanup_strv_free_ char **l = NULL;
414 Image *image;
415 HASHMAP_FOREACH(image, images) {
416 char *p;
417
418 p = image_bus_path(image->name);
419 if (!p)
420 return -ENOMEM;
421
422 r = strv_consume(&l, p);
423 if (r < 0)
424 return r;
425 }
426
427 *nodes = TAKE_PTR(l);
428
429 return 1;
430 }
431
432 const sd_bus_vtable image_vtable[] = {
433 SD_BUS_VTABLE_START(0),
434 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
435 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
436 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
437 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
438 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
439 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
440 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
441 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
442 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
443 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
444 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
445 SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
446 SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
447 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
448 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
449 SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
450 SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
451 SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
452 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
453 SD_BUS_VTABLE_END
454 };
455
456 const BusObjectImplementation image_object = {
457 "/org/freedesktop/machine1/image",
458 "org.freedesktop.machine1.Image",
459 .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, image_object_find}),
460 .node_enumerator = image_node_enumerator,
461 };